Completed
Push — master ( 8f38ad...4035d6 )
by Joas
13:12 queued 06:02
created

OC_Util::initObjectStoreRootFS()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 16
nop 1
dl 0
loc 26
rs 8.439
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 Christopher Schäpers <[email protected]>
14
 * @author Christoph Wurst <[email protected]>
15
 * @author Clark Tomlinson <[email protected]>
16
 * @author cmeh <[email protected]>
17
 * @author Florin Peter <[email protected]>
18
 * @author Frank Karlitschek <[email protected]>
19
 * @author Georg Ehrke <[email protected]>
20
 * @author helix84 <[email protected]>
21
 * @author Individual IT Services <[email protected]>
22
 * @author Jakob Sack <[email protected]>
23
 * @author Joas Schilling <[email protected]>
24
 * @author Jörn Friedrich Dreyer <[email protected]>
25
 * @author Lukas Reschke <[email protected]>
26
 * @author Markus Goetz <[email protected]>
27
 * @author Martin Mattel <[email protected]>
28
 * @author Marvin Thomas Rabe <[email protected]>
29
 * @author Michael Gapczynski <[email protected]>
30
 * @author Morris Jobke <[email protected]>
31
 * @author Robin Appelman <[email protected]>
32
 * @author Robin McCorkell <[email protected]>
33
 * @author Roeland Jago Douma <[email protected]>
34
 * @author Stefan Rado <[email protected]>
35
 * @author Stefan Weil <[email protected]>
36
 * @author Thomas Müller <[email protected]>
37
 * @author Thomas Tanghus <[email protected]>
38
 * @author Victor Dubiniuk <[email protected]>
39
 * @author Vincent Petry <[email protected]>
40
 * @author Volkan Gezer <[email protected]>
41
 *
42
 * @license AGPL-3.0
43
 *
44
 * This code is free software: you can redistribute it and/or modify
45
 * it under the terms of the GNU Affero General Public License, version 3,
46
 * as published by the Free Software Foundation.
47
 *
48
 * This program is distributed in the hope that it will be useful,
49
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51
 * GNU Affero General Public License for more details.
52
 *
53
 * You should have received a copy of the GNU Affero General Public License, version 3,
54
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
55
 *
56
 */
57
58
use OCP\IConfig;
59
use OCP\IGroupManager;
60
use OCP\IUser;
61
62
class OC_Util {
63
	public static $scripts = array();
64
	public static $styles = array();
65
	public static $headers = array();
66
	private static $rootMounted = false;
67
	private static $fsSetup = false;
68
69
	/** @var array Local cache of version.php */
70
	private static $versionCache = null;
71
72
	protected static function getAppManager() {
73
		return \OC::$server->getAppManager();
74
	}
75
76
	private static function initLocalStorageRootFS() {
77
		// mount local file backend as root
78
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
79
		//first set up the local "root" storage
80
		\OC\Files\Filesystem::initMountManager();
81
		if (!self::$rootMounted) {
82
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
83
			self::$rootMounted = true;
84
		}
85
	}
86
87
	/**
88
	 * mounting an object storage as the root fs will in essence remove the
89
	 * necessity of a data folder being present.
90
	 * TODO make home storage aware of this and use the object storage instead of local disk access
91
	 *
92
	 * @param array $config containing 'class' and optional 'arguments'
93
	 */
94
	private static function initObjectStoreRootFS($config) {
95
		// check misconfiguration
96
		if (empty($config['class'])) {
97
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
98
		}
99
		if (!isset($config['arguments'])) {
100
			$config['arguments'] = array();
101
		}
102
103
		// instantiate object store implementation
104
		$name = $config['class'];
105
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
106
			$segments = explode('\\', $name);
107
			OC_App::loadApp(strtolower($segments[1]));
108
		}
109
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
110
		// mount with plain / root object store implementation
111
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
112
113
		// mount object storage as root
114
		\OC\Files\Filesystem::initMountManager();
115
		if (!self::$rootMounted) {
116
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
117
			self::$rootMounted = true;
118
		}
119
	}
120
121
	/**
122
	 * Can be set up
123
	 *
124
	 * @param string $user
125
	 * @return boolean
126
	 * @description configure the initial filesystem based on the configuration
127
	 */
128
	public static function setupFS($user = '') {
129
		//setting up the filesystem twice can only lead to trouble
130
		if (self::$fsSetup) {
131
			return false;
132
		}
133
134
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
135
136
		// If we are not forced to load a specific user we load the one that is logged in
137
		if ($user === null) {
138
			$user = '';
139
		} else if ($user == "" && OC_User::isLoggedIn()) {
0 ignored issues
show
Deprecated Code introduced by
The method OC_User::isLoggedIn() has been deprecated with message: use \OC::$server->getUserSession()->isLoggedIn()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
140
			$user = OC_User::getUser();
141
		}
142
143
		// load all filesystem apps before, so no setup-hook gets lost
144
		OC_App::loadApps(array('filesystem'));
145
146
		// the filesystem will finish when $user is not empty,
147
		// mark fs setup here to avoid doing the setup from loading
148
		// OC_Filesystem
149
		if ($user != '') {
150
			self::$fsSetup = true;
151
		}
152
153
		\OC\Files\Filesystem::initMountManager();
154
155
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
156
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
157
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
158
				/** @var \OC\Files\Storage\Common $storage */
159
				$storage->setMountOptions($mount->getOptions());
160
			}
161
			return $storage;
162
		});
163
164
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
165
			if (!$mount->getOption('enable_sharing', true)) {
166
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
167
					'storage' => $storage,
168
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
169
				]);
170
			}
171
			return $storage;
172
		});
173
174
		// install storage availability wrapper, before most other wrappers
175
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
176
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
177
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
178
			}
179
			return $storage;
180
		});
181
182
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
183
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
184
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
185
			}
186
			return $storage;
187
		});
188
189
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
190
			// set up quota for home storages, even for other users
191
			// which can happen when using sharing
192
193
			/**
194
			 * @var \OC\Files\Storage\Storage $storage
195
			 */
196
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
197
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
198
			) {
199
				/** @var \OC\Files\Storage\Home $storage */
200
				if (is_object($storage->getUser())) {
201
					$user = $storage->getUser()->getUID();
202
					$quota = OC_Util::getUserQuota($user);
203
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
204
						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
205
					}
206
				}
207
			}
208
209
			return $storage;
210
		});
211
212
		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
213
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
214
215
		//check if we are using an object storage
216
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
217
		if (isset($objectStore)) {
218
			self::initObjectStoreRootFS($objectStore);
219
		} else {
220
			self::initLocalStorageRootFS();
221
		}
222
223
		if ($user != '' && !OCP\User::userExists($user)) {
0 ignored issues
show
Deprecated Code introduced by
The method OCP\User::userExists() has been deprecated with message: 8.1.0 use method userExists() of \OCP\IUserManager - \OC::$server->getUserManager()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
224
			\OC::$server->getEventLogger()->end('setup_fs');
225
			return false;
226
		}
227
228
		//if we aren't logged in, there is no use to set up the filesystem
229
		if ($user != "") {
230
231
			$userDir = '/' . $user . '/files';
232
233
			//jail the user into his "home" directory
234
			\OC\Files\Filesystem::init($user, $userDir);
235
236
			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
237
		}
238
		\OC::$server->getEventLogger()->end('setup_fs');
239
		return true;
240
	}
241
242
	/**
243
	 * check if a password is required for each public link
244
	 *
245
	 * @return boolean
246
	 */
247
	public static function isPublicLinkPasswordRequired() {
248
		$appConfig = \OC::$server->getAppConfig();
249
		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
250
		return ($enforcePassword === 'yes') ? true : false;
251
	}
252
253
	/**
254
	 * check if sharing is disabled for the current user
255
	 * @param IConfig $config
256
	 * @param IGroupManager $groupManager
257
	 * @param IUser|null $user
258
	 * @return bool
259
	 */
260
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
261
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
262
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
263
			$excludedGroups = json_decode($groupsList);
264 View Code Duplication
			if (is_null($excludedGroups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
265
				$excludedGroups = explode(',', $groupsList);
266
				$newValue = json_encode($excludedGroups);
267
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
268
			}
269
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 260 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...
270 View Code Duplication
			if (!empty($usersGroups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
272
				// if the user is only in groups which are disabled for sharing then
273
				// sharing is also disabled for the user
274
				if (empty($remainingGroups)) {
275
					return true;
276
				}
277
			}
278
		}
279
		return false;
280
	}
281
282
	/**
283
	 * check if share API enforces a default expire date
284
	 *
285
	 * @return boolean
286
	 */
287
	public static function isDefaultExpireDateEnforced() {
288
		$isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\Config::getAppValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
289
		$enforceDefaultExpireDate = false;
290
		if ($isDefaultExpireDateEnabled === 'yes') {
291
			$value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\Config::getAppValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
292
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
293
		}
294
295
		return $enforceDefaultExpireDate;
296
	}
297
298
	/**
299
	 * Get the quota of a user
300
	 *
301
	 * @param string $userId
302
	 * @return int Quota bytes
303
	 */
304
	public static function getUserQuota($userId) {
305
		$user = \OC::$server->getUserManager()->get($userId);
306
		if (is_null($user)) {
307
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
308
		}
309
		$userQuota = $user->getQuota();
310
		if($userQuota === 'none') {
311
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
312
		}
313
		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 313 which is incompatible with the return type documented by OC_Util::getUserQuota of type integer. It seems like you forgot to handle an error condition.
Loading history...
314
	}
315
316
	/**
317
	 * copies the skeleton to the users /files
318
	 *
319
	 * @param String $userId
320
	 * @param \OCP\Files\Folder $userDirectory
321
	 * @throws \RuntimeException
322
	 */
323
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
324
325
		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
326
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
327
328
		if ($instanceId === null) {
329
			throw new \RuntimeException('no instance id!');
330
		}
331
		$appdata = 'appdata_' . $instanceId;
332
		if ($userId === $appdata) {
333
			throw new \RuntimeException('username is reserved name: ' . $appdata);
334
		}
335
336
		if (!empty($skeletonDirectory)) {
337
			\OCP\Util::writeLog(
338
				'files_skeleton',
339
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
340
				\OCP\Util::DEBUG
341
			);
342
			self::copyr($skeletonDirectory, $userDirectory);
343
			// update the file cache
344
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
345
		}
346
	}
347
348
	/**
349
	 * copies a directory recursively by using streams
350
	 *
351
	 * @param string $source
352
	 * @param \OCP\Files\Folder $target
353
	 * @return void
354
	 */
355
	public static function copyr($source, \OCP\Files\Folder $target) {
356
		$logger = \OC::$server->getLogger();
357
358
		// Verify if folder exists
359
		$dir = opendir($source);
360
		if($dir === false) {
361
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
362
			return;
363
		}
364
365
		// Copy the files
366
		while (false !== ($file = readdir($dir))) {
367
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
368
				if (is_dir($source . '/' . $file)) {
369
					$child = $target->newFolder($file);
370
					self::copyr($source . '/' . $file, $child);
371
				} else {
372
					$child = $target->newFile($file);
373
					$sourceStream = fopen($source . '/' . $file, 'r');
374
					if($sourceStream === false) {
375
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
376
						closedir($dir);
377
						return;
378
					}
379
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
380
				}
381
			}
382
		}
383
		closedir($dir);
384
	}
385
386
	/**
387
	 * @return void
388
	 */
389
	public static function tearDownFS() {
390
		\OC\Files\Filesystem::tearDown();
391
		\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...
392
		self::$fsSetup = false;
393
		self::$rootMounted = false;
394
	}
395
396
	/**
397
	 * get the current installed version of ownCloud
398
	 *
399
	 * @return array
400
	 */
401
	public static function getVersion() {
402
		OC_Util::loadVersion();
403
		return self::$versionCache['OC_Version'];
404
	}
405
406
	/**
407
	 * get the current installed version string of ownCloud
408
	 *
409
	 * @return string
410
	 */
411
	public static function getVersionString() {
412
		OC_Util::loadVersion();
413
		return self::$versionCache['OC_VersionString'];
414
	}
415
416
	/**
417
	 * @deprecated the value is of no use anymore
418
	 * @return string
419
	 */
420
	public static function getEditionString() {
421
		return '';
422
	}
423
424
	/**
425
	 * @description get the update channel of the current installed of ownCloud.
426
	 * @return string
427
	 */
428
	public static function getChannel() {
429
		OC_Util::loadVersion();
430
431
		// Allow overriding update channel
432
		if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
433
			self::$versionCache['OC_Channel'] = \OC::$server->getAppConfig()->getValue('core', 'OC_Channel');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
434
		}
435
436
		return self::$versionCache['OC_Channel'];
437
	}
438
439
	/**
440
	 * @description get the build number of the current installed of ownCloud.
441
	 * @return string
442
	 */
443
	public static function getBuild() {
444
		OC_Util::loadVersion();
445
		return self::$versionCache['OC_Build'];
446
	}
447
448
	/**
449
	 * @description load the version.php into the session as cache
450
	 */
451
	private static function loadVersion() {
452
		if (self::$versionCache !== null) {
453
			return;
454
		}
455
456
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
457
		require OC::$SERVERROOT . '/version.php';
458
		/** @var $timestamp int */
459
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
460
		/** @var $OC_Version string */
461
		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...
462
		/** @var $OC_VersionString string */
463
		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...
464
		/** @var $OC_Build string */
465
		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...
466
467
		/** @var $OC_Channel string */
468
		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...
469
	}
470
471
	/**
472
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
473
	 *
474
	 * @param string $application application to get the files from
475
	 * @param string $directory directory within this application (css, js, vendor, etc)
476
	 * @param string $file the file inside of the above folder
477
	 * @return string the path
478
	 */
479
	private static function generatePath($application, $directory, $file) {
480
		if (is_null($file)) {
481
			$file = $application;
482
			$application = "";
483
		}
484
		if (!empty($application)) {
485
			return "$application/$directory/$file";
486
		} else {
487
			return "$directory/$file";
488
		}
489
	}
490
491
	/**
492
	 * add a javascript file
493
	 *
494
	 * @param string $application application id
495
	 * @param string|null $file filename
496
	 * @param bool $prepend prepend the Script to the beginning of the list
497
	 * @return void
498
	 */
499
	public static function addScript($application, $file = null, $prepend = false) {
500
		$path = OC_Util::generatePath($application, 'js', $file);
501
502
		// core js files need separate handling
503
		if ($application !== 'core' && $file !== null) {
504
			self::addTranslations ( $application );
505
		}
506
		self::addExternalResource($application, $prepend, $path, "script");
507
	}
508
509
	/**
510
	 * add a javascript file from the vendor sub folder
511
	 *
512
	 * @param string $application application id
513
	 * @param string|null $file filename
514
	 * @param bool $prepend prepend the Script to the beginning of the list
515
	 * @return void
516
	 */
517
	public static function addVendorScript($application, $file = null, $prepend = false) {
518
		$path = OC_Util::generatePath($application, 'vendor', $file);
519
		self::addExternalResource($application, $prepend, $path, "script");
520
	}
521
522
	/**
523
	 * add a translation JS file
524
	 *
525
	 * @param string $application application id
526
	 * @param string $languageCode language code, defaults to the current language
527
	 * @param bool $prepend prepend the Script to the beginning of the list 
528
	 */
529
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
530
		if (is_null($languageCode)) {
531
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
532
		}
533
		if (!empty($application)) {
534
			$path = "$application/l10n/$languageCode";
535
		} else {
536
			$path = "l10n/$languageCode";
537
		}
538
		self::addExternalResource($application, $prepend, $path, "script");
539
	}
540
541
	/**
542
	 * add a css file
543
	 *
544
	 * @param string $application application id
545
	 * @param string|null $file filename
546
	 * @param bool $prepend prepend the Style to the beginning of the list
547
	 * @return void
548
	 */
549
	public static function addStyle($application, $file = null, $prepend = false) {
550
		$path = OC_Util::generatePath($application, 'css', $file);
551
		self::addExternalResource($application, $prepend, $path, "style");
552
	}
553
554
	/**
555
	 * add a css file from the vendor sub folder
556
	 *
557
	 * @param string $application application id
558
	 * @param string|null $file filename
559
	 * @param bool $prepend prepend the Style to the beginning of the list
560
	 * @return void
561
	 */
562
	public static function addVendorStyle($application, $file = null, $prepend = false) {
563
		$path = OC_Util::generatePath($application, 'vendor', $file);
564
		self::addExternalResource($application, $prepend, $path, "style");
565
	}
566
567
	/**
568
	 * add an external resource css/js file
569
	 *
570
	 * @param string $application application id
571
	 * @param bool $prepend prepend the file to the beginning of the list
572
	 * @param string $path 
573
	 * @param string $type (script or style)
574
	 * @return void
575
	 */
576
	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...
577
578
		if ($type === "style") {
579 View Code Duplication
			if (!in_array($path, self::$styles)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
580
				if ($prepend === true) {
581
					array_unshift ( self::$styles, $path );
582
				} else {
583
					self::$styles[] = $path;
584
				}
585
			}
586 View Code Duplication
		} elseif ($type === "script") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
587
			if (!in_array($path, self::$scripts)) {
588
				if ($prepend === true) {
589
					array_unshift ( self::$scripts, $path );
590
				} else {
591
					self::$scripts [] = $path;
592
				}
593
			}
594
		}
595
	}
596
597
	/**
598
	 * Add a custom element to the header
599
	 * If $text is null then the element will be written as empty element.
600
	 * So use "" to get a closing tag.
601
	 * @param string $tag tag name of the element
602
	 * @param array $attributes array of attributes for the element
603
	 * @param string $text the text content for the element
604
	 */
605 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
606
		self::$headers[] = array(
607
			'tag' => $tag,
608
			'attributes' => $attributes,
609
			'text' => $text
610
		);
611
	}
612
613
	/**
614
	 * formats a timestamp in the "right" way
615
	 *
616
	 * @param int $timestamp
617
	 * @param bool $dateOnly option to omit time from the result
618
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
619
	 * @return string timestamp
620
	 *
621
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
622
	 */
623
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
624
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
625
			$timeZone = new \DateTimeZone($timeZone);
626
		}
627
628
		/** @var \OC\DateTimeFormatter $formatter */
629
		$formatter = \OC::$server->query('DateTimeFormatter');
630
		if ($dateOnly) {
631
			return $formatter->formatDate($timestamp, 'long', $timeZone);
632
		}
633
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
634
	}
635
636
	/**
637
	 * check if the current server configuration is suitable for ownCloud
638
	 *
639
	 * @param \OCP\IConfig $config
640
	 * @return array arrays with error messages and hints
641
	 */
642
	public static function checkServer(\OCP\IConfig $config) {
643
		$l = \OC::$server->getL10N('lib');
644
		$errors = array();
645
		$CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
646
647
		if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
648
			// this check needs to be done every time
649
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
650
		}
651
652
		// Assume that if checkServer() succeeded before in this session, then all is fine.
653
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
654
			return $errors;
655
		}
656
657
		$webServerRestart = false;
658
		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
659
			\OC::$server->getThemingDefaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
660
661
		$urlGenerator = \OC::$server->getURLGenerator();
662
663
		$availableDatabases = $setup->getSupportedDatabases();
664
		if (empty($availableDatabases)) {
665
			$errors[] = array(
666
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
667
				'hint' => '' //TODO: sane hint
668
			);
669
			$webServerRestart = true;
670
		}
671
672
		// Check if config folder is writable.
673
		if(!OC_Helper::isReadOnlyConfigEnabled()) {
674
			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
675
				$errors[] = array(
676
					'error' => $l->t('Cannot write into "config" directory'),
677
					'hint' => $l->t('This can usually be fixed by '
678
						. '%sgiving the webserver write access to the config directory%s.',
679
						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
680
				);
681
			}
682
		}
683
684
		// Check if there is a writable install folder.
685
		if ($config->getSystemValue('appstoreenabled', true)) {
686
			if (OC_App::getInstallPath() === null
687
				|| !is_writable(OC_App::getInstallPath())
688
				|| !is_readable(OC_App::getInstallPath())
689
			) {
690
				$errors[] = array(
691
					'error' => $l->t('Cannot write into "apps" directory'),
692
					'hint' => $l->t('This can usually be fixed by '
693
						. '%sgiving the webserver write access to the apps directory%s'
694
						. ' or disabling the appstore in the config file.',
695
						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
696
				);
697
			}
698
		}
699
		// Create root dir.
700
		if ($config->getSystemValue('installed', false)) {
701
			if (!is_dir($CONFIG_DATADIRECTORY)) {
702
				$success = @mkdir($CONFIG_DATADIRECTORY);
703
				if ($success) {
704
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
705
				} else {
706
					$errors[] = array(
707
						'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
708
						'hint' => $l->t('This can usually be fixed by '
709
							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
710
							array($urlGenerator->linkToDocs('admin-dir_permissions')))
711
					);
712
				}
713
			} else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
714
				//common hint for all file permissions error messages
715
				$permissionsHint = $l->t('Permissions can usually be fixed by '
716
					. '%sgiving the webserver write access to the root directory%s.',
717
					array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
718
				$errors[] = array(
719
					'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
720
					'hint' => $permissionsHint
721
				);
722
			} else {
723
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
724
			}
725
		}
726
727
		if (!OC_Util::isSetLocaleWorking()) {
728
			$errors[] = array(
729
				'error' => $l->t('Setting locale to %s failed',
730
					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
731
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
732
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
733
			);
734
		}
735
736
		// Contains the dependencies that should be checked against
737
		// classes = class_exists
738
		// functions = function_exists
739
		// defined = defined
740
		// ini = ini_get
741
		// If the dependency is not found the missing module name is shown to the EndUser
742
		// When adding new checks always verify that they pass on Travis as well
743
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
744
		$dependencies = array(
745
			'classes' => array(
746
				'ZipArchive' => 'zip',
747
				'DOMDocument' => 'dom',
748
				'XMLWriter' => 'XMLWriter',
749
				'XMLReader' => 'XMLReader',
750
			),
751
			'functions' => [
752
				'xml_parser_create' => 'libxml',
753
				'mb_strcut' => 'mb multibyte',
754
				'ctype_digit' => 'ctype',
755
				'json_encode' => 'JSON',
756
				'gd_info' => 'GD',
757
				'gzencode' => 'zlib',
758
				'iconv' => 'iconv',
759
				'simplexml_load_string' => 'SimpleXML',
760
				'hash' => 'HASH Message Digest Framework',
761
				'curl_init' => 'cURL',
762
				'openssl_verify' => 'OpenSSL',
763
			],
764
			'defined' => array(
765
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
766
			),
767
			'ini' => [
768
				'default_charset' => 'UTF-8',
769
			],
770
		);
771
		$missingDependencies = array();
772
		$invalidIniSettings = [];
773
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
774
775
		/**
776
		 * FIXME: The dependency check does not work properly on HHVM on the moment
777
		 *        and prevents installation. Once HHVM is more compatible with our
778
		 *        approach to check for these values we should re-enable those
779
		 *        checks.
780
		 */
781
		$iniWrapper = \OC::$server->getIniWrapper();
782
		if (!self::runningOnHhvm()) {
783
			foreach ($dependencies['classes'] as $class => $module) {
784
				if (!class_exists($class)) {
785
					$missingDependencies[] = $module;
786
				}
787
			}
788
			foreach ($dependencies['functions'] as $function => $module) {
789
				if (!function_exists($function)) {
790
					$missingDependencies[] = $module;
791
				}
792
			}
793
			foreach ($dependencies['defined'] as $defined => $module) {
794
				if (!defined($defined)) {
795
					$missingDependencies[] = $module;
796
				}
797
			}
798
			foreach ($dependencies['ini'] as $setting => $expected) {
799
				if (is_bool($expected)) {
800
					if ($iniWrapper->getBool($setting) !== $expected) {
801
						$invalidIniSettings[] = [$setting, $expected];
802
					}
803
				}
804
				if (is_int($expected)) {
805
					if ($iniWrapper->getNumeric($setting) !== $expected) {
806
						$invalidIniSettings[] = [$setting, $expected];
807
					}
808
				}
809
				if (is_string($expected)) {
810
					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
811
						$invalidIniSettings[] = [$setting, $expected];
812
					}
813
				}
814
			}
815
		}
816
817
		foreach($missingDependencies as $missingDependency) {
818
			$errors[] = array(
819
				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
820
				'hint' => $moduleHint
821
			);
822
			$webServerRestart = true;
823
		}
824
		foreach($invalidIniSettings as $setting) {
825
			if(is_bool($setting[1])) {
826
				$setting[1] = ($setting[1]) ? 'on' : 'off';
827
			}
828
			$errors[] = [
829
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
830
				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
831
			];
832
			$webServerRestart = true;
833
		}
834
835
		/**
836
		 * The mbstring.func_overload check can only be performed if the mbstring
837
		 * module is installed as it will return null if the checking setting is
838
		 * not available and thus a check on the boolean value fails.
839
		 *
840
		 * TODO: Should probably be implemented in the above generic dependency
841
		 *       check somehow in the long-term.
842
		 */
843
		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
844
			$iniWrapper->getBool('mbstring.func_overload') === true) {
845
			$errors[] = array(
846
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
847
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
848
			);
849
		}
850
851
		if(function_exists('xml_parser_create') &&
852
			LIBXML_LOADED_VERSION < 20700 ) {
853
			$version = LIBXML_LOADED_VERSION;
854
			$major = floor($version/10000);
855
			$version -= ($major * 10000);
856
			$minor = floor($version/100);
857
			$version -= ($minor * 100);
858
			$patch = $version;
859
			$errors[] = array(
860
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
861
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
862
			);
863
		}
864
865
		if (!self::isAnnotationsWorking()) {
866
			$errors[] = array(
867
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
868
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
869
			);
870
		}
871
872
		if (!\OC::$CLI && $webServerRestart) {
873
			$errors[] = array(
874
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
875
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
876
			);
877
		}
878
879
		$errors = array_merge($errors, self::checkDatabaseVersion());
880
881
		// Cache the result of this function
882
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
883
884
		return $errors;
885
	}
886
887
	/**
888
	 * Check the database version
889
	 *
890
	 * @return array errors array
891
	 */
892
	public static function checkDatabaseVersion() {
893
		$l = \OC::$server->getL10N('lib');
894
		$errors = array();
895
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
896
		if ($dbType === 'pgsql') {
897
			// check PostgreSQL version
898
			try {
899
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
900
				$data = $result->fetchRow();
901
				if (isset($data['server_version'])) {
902
					$version = $data['server_version'];
903
					if (version_compare($version, '9.0.0', '<')) {
904
						$errors[] = array(
905
							'error' => $l->t('PostgreSQL >= 9 required'),
906
							'hint' => $l->t('Please upgrade your database version')
907
						);
908
					}
909
				}
910
			} 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...
911
				$logger = \OC::$server->getLogger();
912
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
913
				$logger->logException($e);
914
			}
915
		}
916
		return $errors;
917
	}
918
919
	/**
920
	 * Check for correct file permissions of data directory
921
	 *
922
	 * @param string $dataDirectory
923
	 * @return array arrays with error messages and hints
924
	 */
925
	public static function checkDataDirectoryPermissions($dataDirectory) {
926
		$l = \OC::$server->getL10N('lib');
927
		$errors = array();
928
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
929
			. ' cannot be listed by other users.');
930
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
931
		if (substr($perms, -1) != '0') {
932
			chmod($dataDirectory, 0770);
933
			clearstatcache();
934
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
935 View Code Duplication
			if (substr($perms, 2, 1) != '0') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
936
				$errors[] = array(
937
					'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
938
					'hint' => $permissionsModHint
939
				);
940
			}
941
		}
942
		return $errors;
943
	}
944
945
	/**
946
	 * Check that the data directory exists and is valid by
947
	 * checking the existence of the ".ocdata" file.
948
	 *
949
	 * @param string $dataDirectory data directory path
950
	 * @return array errors found
951
	 */
952
	public static function checkDataDirectoryValidity($dataDirectory) {
953
		$l = \OC::$server->getL10N('lib');
954
		$errors = [];
955 View Code Duplication
		if ($dataDirectory[0] !== '/') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
956
			$errors[] = [
957
				'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
958
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
959
			];
960
		}
961 View Code Duplication
		if (!file_exists($dataDirectory . '/.ocdata')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
962
			$errors[] = [
963
				'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
964
				'hint' => $l->t('Please check that the data directory contains a file' .
965
					' ".ocdata" in its root.')
966
			];
967
		}
968
		return $errors;
969
	}
970
971
	/**
972
	 * Check if the user is logged in, redirects to home if not. With
973
	 * redirect URL parameter to the request URI.
974
	 *
975
	 * @return void
976
	 */
977
	public static function checkLoggedIn() {
978
		// Check if we are a user
979
		if (!OC_User::isLoggedIn()) {
0 ignored issues
show
Deprecated Code introduced by
The method OC_User::isLoggedIn() has been deprecated with message: use \OC::$server->getUserSession()->isLoggedIn()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
980
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
981
						'core.login.showLoginForm',
982
						[
983
							'redirect_url' => urlencode(\OC::$server->getRequest()->getRequestUri()),
984
						]
985
					)
986
			);
987
			exit();
988
		}
989
		// Redirect to index page if 2FA challenge was not solved yet
990
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
991
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
992
			exit();
993
		}
994
	}
995
996
	/**
997
	 * Check if the user is a admin, redirects to home if not
998
	 *
999
	 * @return void
1000
	 */
1001
	public static function checkAdminUser() {
1002
		OC_Util::checkLoggedIn();
1003
		if (!OC_User::isAdminUser(OC_User::getUser())) {
1004
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1005
			exit();
1006
		}
1007
	}
1008
1009
	/**
1010
	 * Check if it is allowed to remember login.
1011
	 *
1012
	 * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
1013
	 *
1014
	 * @return bool
1015
	 */
1016
	public static function rememberLoginAllowed() {
1017
1018
		$apps = OC_App::getEnabledApps();
1019
1020
		foreach ($apps as $app) {
1021
			$appInfo = OC_App::getAppInfo($app);
1022
			if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
1023
				return false;
1024
			}
1025
1026
		}
1027
		return true;
1028
	}
1029
1030
	/**
1031
	 * Check if the user is a subadmin, redirects to home if not
1032
	 *
1033
	 * @return null|boolean $groups where the current user is subadmin
1034
	 */
1035
	public static function checkSubAdminUser() {
1036
		OC_Util::checkLoggedIn();
1037
		$userObject = \OC::$server->getUserSession()->getUser();
1038
		$isSubAdmin = false;
1039
		if($userObject !== null) {
1040
			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1041
		}
1042
1043
		if (!$isSubAdmin) {
1044
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1045
			exit();
1046
		}
1047
		return true;
1048
	}
1049
1050
	/**
1051
	 * Returns the URL of the default page
1052
	 * based on the system configuration and
1053
	 * the apps visible for the current user
1054
	 *
1055
	 * @return string URL
1056
	 */
1057
	public static function getDefaultPageUrl() {
1058
		$urlGenerator = \OC::$server->getURLGenerator();
1059
		// Deny the redirect if the URL contains a @
1060
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1061
		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1062
			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1063
		} else {
1064
			$defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1065
			if ($defaultPage) {
1066
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1067
			} else {
1068
				$appId = 'files';
1069
				$defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
0 ignored issues
show
Deprecated Code introduced by
The method OCP\Config::getSystemValue() has been deprecated with message: 8.0.0 use method getSystemValue of \OCP\IConfig This function gets the value from config.php. If it does not exist,
$default will be returned.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1070
				// find the first app that is enabled for the current user
1071
				foreach ($defaultApps as $defaultApp) {
1072
					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1073
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1074
						$appId = $defaultApp;
1075
						break;
1076
					}
1077
				}
1078
1079
				if(getenv('front_controller_active') === 'true') {
1080
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1081
				} else {
1082
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1083
				}
1084
			}
1085
		}
1086
		return $location;
1087
	}
1088
1089
	/**
1090
	 * Redirect to the user default page
1091
	 *
1092
	 * @return void
1093
	 */
1094
	public static function redirectToDefaultPage() {
1095
		$location = self::getDefaultPageUrl();
1096
		header('Location: ' . $location);
1097
		exit();
1098
	}
1099
1100
	/**
1101
	 * get an id unique for this instance
1102
	 *
1103
	 * @return string
1104
	 */
1105
	public static function getInstanceId() {
1106
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1107
		if (is_null($id)) {
1108
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1109
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1110
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1111
		}
1112
		return $id;
1113
	}
1114
1115
	/**
1116
	 * Public function to sanitize HTML
1117
	 *
1118
	 * This function is used to sanitize HTML and should be applied on any
1119
	 * string or array of strings before displaying it on a web page.
1120
	 *
1121
	 * @param string|array $value
1122
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1123
	 */
1124
	public static function sanitizeHTML($value) {
1125
		if (is_array($value)) {
1126
			$value = array_map(function($value) {
1127
				return self::sanitizeHTML($value);
1128
			}, $value);
1129
		} else {
1130
			// Specify encoding for PHP<5.4
1131
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1132
		}
1133
		return $value;
1134
	}
1135
1136
	/**
1137
	 * Public function to encode url parameters
1138
	 *
1139
	 * This function is used to encode path to file before output.
1140
	 * Encoding is done according to RFC 3986 with one exception:
1141
	 * Character '/' is preserved as is.
1142
	 *
1143
	 * @param string $component part of URI to encode
1144
	 * @return string
1145
	 */
1146
	public static function encodePath($component) {
1147
		$encoded = rawurlencode($component);
1148
		$encoded = str_replace('%2F', '/', $encoded);
1149
		return $encoded;
1150
	}
1151
1152
1153
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1154
		// php dev server does not support htaccess
1155
		if (php_sapi_name() === 'cli-server') {
1156
			return false;
1157
		}
1158
1159
		// testdata
1160
		$fileName = '/htaccesstest.txt';
1161
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1162
1163
		// creating a test file
1164
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1165
1166
		if (file_exists($testFile)) {// already running this test, possible recursive call
1167
			return false;
1168
		}
1169
1170
		$fp = @fopen($testFile, 'w');
1171
		if (!$fp) {
1172
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1173
				'Make sure it is possible for the webserver to write to ' . $testFile);
1174
		}
1175
		fwrite($fp, $testContent);
1176
		fclose($fp);
1177
	}
1178
1179
	/**
1180
	 * Check if the .htaccess file is working
1181
	 * @param \OCP\IConfig $config
1182
	 * @return bool
1183
	 * @throws Exception
1184
	 * @throws \OC\HintException If the test file can't get written.
1185
	 */
1186
	public function isHtaccessWorking(\OCP\IConfig $config) {
1187
1188
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1189
			return true;
1190
		}
1191
1192
		$testContent = $this->createHtaccessTestFile($config);
1193
		if ($testContent === false) {
1194
			return false;
1195
		}
1196
1197
		$fileName = '/htaccesstest.txt';
1198
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1199
1200
		// accessing the file via http
1201
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1202
		try {
1203
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1204
		} catch (\Exception $e) {
1205
			$content = false;
1206
		}
1207
1208
		// cleanup
1209
		@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...
1210
1211
		/*
1212
		 * If the content is not equal to test content our .htaccess
1213
		 * is working as required
1214
		 */
1215
		return $content !== $testContent;
1216
	}
1217
1218
	/**
1219
	 * Check if the setlocal call does not work. This can happen if the right
1220
	 * local packages are not available on the server.
1221
	 *
1222
	 * @return bool
1223
	 */
1224
	public static function isSetLocaleWorking() {
1225
		\Patchwork\Utf8\Bootup::initLocale();
1226
		if ('' === basename('§')) {
1227
			return false;
1228
		}
1229
		return true;
1230
	}
1231
1232
	/**
1233
	 * Check if it's possible to get the inline annotations
1234
	 *
1235
	 * @return bool
1236
	 */
1237
	public static function isAnnotationsWorking() {
1238
		$reflection = new \ReflectionMethod(__METHOD__);
1239
		$docs = $reflection->getDocComment();
1240
1241
		return (is_string($docs) && strlen($docs) > 50);
1242
	}
1243
1244
	/**
1245
	 * Check if the PHP module fileinfo is loaded.
1246
	 *
1247
	 * @return bool
1248
	 */
1249
	public static function fileInfoLoaded() {
1250
		return function_exists('finfo_open');
1251
	}
1252
1253
	/**
1254
	 * clear all levels of output buffering
1255
	 *
1256
	 * @return void
1257
	 */
1258
	public static function obEnd() {
1259
		while (ob_get_level()) {
1260
			ob_end_clean();
1261
		}
1262
	}
1263
1264
	/**
1265
	 * Checks whether the server is running on Mac OS X
1266
	 *
1267
	 * @return bool true if running on Mac OS X, false otherwise
1268
	 */
1269
	public static function runningOnMac() {
1270
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1271
	}
1272
1273
	/**
1274
	 * Checks whether server is running on HHVM
1275
	 *
1276
	 * @return bool True if running on HHVM, false otherwise
1277
	 */
1278
	public static function runningOnHhvm() {
1279
		return defined('HHVM_VERSION');
1280
	}
1281
1282
	/**
1283
	 * Handles the case that there may not be a theme, then check if a "default"
1284
	 * theme exists and take that one
1285
	 *
1286
	 * @return string the theme
1287
	 */
1288
	public static function getTheme() {
1289
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1290
1291
		if ($theme === '') {
1292
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1293
				$theme = 'default';
1294
			}
1295
		}
1296
1297
		return $theme;
1298
	}
1299
1300
	/**
1301
	 * Clear a single file from the opcode cache
1302
	 * This is useful for writing to the config file
1303
	 * in case the opcode cache does not re-validate files
1304
	 * Returns true if successful, false if unsuccessful:
1305
	 * caller should fall back on clearing the entire cache
1306
	 * with clearOpcodeCache() if unsuccessful
1307
	 *
1308
	 * @param string $path the path of the file to clear from the cache
1309
	 * @return bool true if underlying function returns true, otherwise false
1310
	 */
1311
	public static function deleteFromOpcodeCache($path) {
1312
		$ret = false;
1313
		if ($path) {
1314
			// APC >= 3.1.1
1315
			if (function_exists('apc_delete_file')) {
1316
				$ret = @apc_delete_file($path);
1317
			}
1318
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1319
			if (function_exists('opcache_invalidate')) {
1320
				$ret = opcache_invalidate($path);
1321
			}
1322
		}
1323
		return $ret;
1324
	}
1325
1326
	/**
1327
	 * Clear the opcode cache if one exists
1328
	 * This is necessary for writing to the config file
1329
	 * in case the opcode cache does not re-validate files
1330
	 *
1331
	 * @return void
1332
	 */
1333
	public static function clearOpcodeCache() {
1334
		// APC
1335
		if (function_exists('apc_clear_cache')) {
1336
			apc_clear_cache();
1337
		}
1338
		// Zend Opcache
1339
		if (function_exists('accelerator_reset')) {
1340
			accelerator_reset();
1341
		}
1342
		// XCache
1343
		if (function_exists('xcache_clear_cache')) {
1344
			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1345
				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1346
			} else {
1347
				@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...
1348
			}
1349
		}
1350
		// Opcache (PHP >= 5.5)
1351
		if (function_exists('opcache_reset')) {
1352
			opcache_reset();
1353
		}
1354
	}
1355
1356
	/**
1357
	 * Normalize a unicode string
1358
	 *
1359
	 * @param string $value a not normalized string
1360
	 * @return bool|string
1361
	 */
1362
	public static function normalizeUnicode($value) {
1363
		if(Normalizer::isNormalized($value)) {
1364
			return $value;
1365
		}
1366
1367
		$normalizedValue = Normalizer::normalize($value);
1368
		if ($normalizedValue === null || $normalizedValue === false) {
1369
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1370
			return $value;
1371
		}
1372
1373
		return $normalizedValue;
1374
	}
1375
1376
	/**
1377
	 * @param boolean|string $file
1378
	 * @return string
1379
	 */
1380
	public static function basename($file) {
1381
		$file = rtrim($file, '/');
1382
		$t = explode('/', $file);
1383
		return array_pop($t);
1384
	}
1385
1386
	/**
1387
	 * A human readable string is generated based on version, channel and build number
1388
	 *
1389
	 * @return string
1390
	 */
1391
	public static function getHumanVersion() {
1392
		$version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
1393
		$build = OC_Util::getBuild();
1394
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1395
			$version .= ' Build:' . $build;
1396
		}
1397
		return $version;
1398
	}
1399
1400
	/**
1401
	 * Returns whether the given file name is valid
1402
	 *
1403
	 * @param string $file file name to check
1404
	 * @return bool true if the file name is valid, false otherwise
1405
	 * @deprecated use \OC\Files\View::verifyPath()
1406
	 */
1407
	public static function isValidFileName($file) {
1408
		$trimmed = trim($file);
1409
		if ($trimmed === '') {
1410
			return false;
1411
		}
1412
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1413
			return false;
1414
		}
1415
		foreach (str_split($trimmed) as $char) {
1416
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1417
				return false;
1418
			}
1419
		}
1420
		return true;
1421
	}
1422
1423
	/**
1424
	 * Check whether the instance needs to perform an upgrade,
1425
	 * either when the core version is higher or any app requires
1426
	 * an upgrade.
1427
	 *
1428
	 * @param \OCP\IConfig $config
1429
	 * @return bool whether the core or any app needs an upgrade
1430
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1431
	 */
1432
	public static function needUpgrade(\OCP\IConfig $config) {
1433
		if ($config->getSystemValue('installed', false)) {
1434
			$installedVersion = $config->getSystemValue('version', '0.0.0');
1435
			$currentVersion = implode('.', \OCP\Util::getVersion());
1436
			$versionDiff = version_compare($currentVersion, $installedVersion);
1437
			if ($versionDiff > 0) {
1438
				return true;
1439
			} else if ($config->getSystemValue('debug', false) && $versionDiff < 0) {
1440
				// downgrade with debug
1441
				$installedMajor = explode('.', $installedVersion);
1442
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1443
				$currentMajor = explode('.', $currentVersion);
1444
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1445
				if ($installedMajor === $currentMajor) {
1446
					// Same major, allow downgrade for developers
1447
					return true;
1448
				} else {
1449
					// downgrade attempt, throw exception
1450
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1451
				}
1452
			} else if ($versionDiff < 0) {
1453
				// downgrade attempt, throw exception
1454
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1455
			}
1456
1457
			// also check for upgrades for apps (independently from the user)
1458
			$apps = \OC_App::getEnabledApps(false, true);
1459
			$shouldUpgrade = false;
1460
			foreach ($apps as $app) {
1461
				if (\OC_App::shouldUpgrade($app)) {
1462
					$shouldUpgrade = true;
1463
					break;
1464
				}
1465
			}
1466
			return $shouldUpgrade;
1467
		} else {
1468
			return false;
1469
		}
1470
	}
1471
1472
}
1473