Completed
Push — master ( 1c2ccd...167013 )
by Morris
14:20
created

OC_Util::initObjectStoreMultibucketRootFS()   C

Complexity

Conditions 7
Paths 32

Size

Total Lines 33
Code Lines 18

Duplication

Lines 8
Ratio 24.24 %

Importance

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