Completed
Push — master ( ff3e8c...ea9b1c )
by Lukas
13:22 queued 02:44
created

OC_Util::fileInfoLoaded()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
	protected static function getAppManager() {
70
		return \OC::$server->getAppManager();
71
	}
72
73
	private static function initLocalStorageRootFS() {
74
		// mount local file backend as root
75
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
76
		//first set up the local "root" storage
77
		\OC\Files\Filesystem::initMountManager();
78
		if (!self::$rootMounted) {
79
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
80
			self::$rootMounted = true;
81
		}
82
	}
83
84
	/**
85
	 * mounting an object storage as the root fs will in essence remove the
86
	 * necessity of a data folder being present.
87
	 * TODO make home storage aware of this and use the object storage instead of local disk access
88
	 *
89
	 * @param array $config containing 'class' and optional 'arguments'
90
	 */
91
	private static function initObjectStoreRootFS($config) {
92
		// check misconfiguration
93
		if (empty($config['class'])) {
94
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
95
		}
96
		if (!isset($config['arguments'])) {
97
			$config['arguments'] = array();
98
		}
99
100
		// instantiate object store implementation
101
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
102
		// mount with plain / root object store implementation
103
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
104
105
		// mount object storage as root
106
		\OC\Files\Filesystem::initMountManager();
107
		if (!self::$rootMounted) {
108
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
109
			self::$rootMounted = true;
110
		}
111
	}
112
113
	/**
114
	 * Can be set up
115
	 *
116
	 * @param string $user
117
	 * @return boolean
118
	 * @description configure the initial filesystem based on the configuration
119
	 */
120
	public static function setupFS($user = '') {
121
		//setting up the filesystem twice can only lead to trouble
122
		if (self::$fsSetup) {
123
			return false;
124
		}
125
126
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
127
128
		// If we are not forced to load a specific user we load the one that is logged in
129
		if ($user === null) {
130
			$user = '';
131
		} 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...
132
			$user = OC_User::getUser();
133
		}
134
135
		// load all filesystem apps before, so no setup-hook gets lost
136
		OC_App::loadApps(array('filesystem'));
137
138
		// the filesystem will finish when $user is not empty,
139
		// mark fs setup here to avoid doing the setup from loading
140
		// OC_Filesystem
141
		if ($user != '') {
142
			self::$fsSetup = true;
143
		}
144
145
		\OC\Files\Filesystem::initMountManager();
146
147
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
148
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
149
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
150
				/** @var \OC\Files\Storage\Common $storage */
151
				$storage->setMountOptions($mount->getOptions());
152
			}
153
			return $storage;
154
		});
155
156
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
157
			if (!$mount->getOption('enable_sharing', true)) {
158
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
159
					'storage' => $storage,
160
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
161
				]);
162
			}
163
			return $storage;
164
		});
165
166
		// install storage availability wrapper, before most other wrappers
167
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
168
			/** @var \OCP\Files\Storage $storage */
169
			if (!$storage->instanceOfStorage('\OC\Files\Storage\Shared') && !$storage->isLocal()) {
170
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
171
			}
172
			return $storage;
173
		});
174
175
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
176
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OC\Files\Storage\Shared') && !$storage->isLocal()) {
177
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
178
			}
179
			return $storage;
180
		});
181
182
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
183
			// set up quota for home storages, even for other users
184
			// which can happen when using sharing
185
186
			/**
187
			 * @var \OC\Files\Storage\Storage $storage
188
			 */
189
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
190
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
191
			) {
192
				/** @var \OC\Files\Storage\Home $storage */
193
				if (is_object($storage->getUser())) {
194
					$user = $storage->getUser()->getUID();
195
					$quota = OC_Util::getUserQuota($user);
196
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
197
						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
198
					}
199
				}
200
			}
201
202
			return $storage;
203
		});
204
205
		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
206
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
207
208
		//check if we are using an object storage
209
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
210
		if (isset($objectStore)) {
211
			self::initObjectStoreRootFS($objectStore);
212
		} else {
213
			self::initLocalStorageRootFS();
214
		}
215
216
		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...
217
			\OC::$server->getEventLogger()->end('setup_fs');
218
			return false;
219
		}
220
221
		//if we aren't logged in, there is no use to set up the filesystem
222
		if ($user != "") {
223
224
			$userDir = '/' . $user . '/files';
225
226
			//jail the user into his "home" directory
227
			\OC\Files\Filesystem::init($user, $userDir);
228
229
			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
230
		}
231
		\OC::$server->getEventLogger()->end('setup_fs');
232
		return true;
233
	}
234
235
	/**
236
	 * check if a password is required for each public link
237
	 *
238
	 * @return boolean
239
	 */
240
	public static function isPublicLinkPasswordRequired() {
241
		$appConfig = \OC::$server->getAppConfig();
242
		$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...
243
		return ($enforcePassword === 'yes') ? true : false;
244
	}
245
246
	/**
247
	 * check if sharing is disabled for the current user
248
	 * @param IConfig $config
249
	 * @param IGroupManager $groupManager
250
	 * @param IUser|null $user
251
	 * @return bool
252
	 */
253
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
254
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
255
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
256
			$excludedGroups = json_decode($groupsList);
257 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...
258
				$excludedGroups = explode(',', $groupsList);
259
				$newValue = json_encode($excludedGroups);
260
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
261
			}
262
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 253 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...
263 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...
264
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
265
				// if the user is only in groups which are disabled for sharing then
266
				// sharing is also disabled for the user
267
				if (empty($remainingGroups)) {
268
					return true;
269
				}
270
			}
271
		}
272
		return false;
273
	}
274
275
	/**
276
	 * check if share API enforces a default expire date
277
	 *
278
	 * @return boolean
279
	 */
280
	public static function isDefaultExpireDateEnforced() {
281
		$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...
282
		$enforceDefaultExpireDate = false;
283
		if ($isDefaultExpireDateEnabled === 'yes') {
284
			$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...
285
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
286
		}
287
288
		return $enforceDefaultExpireDate;
289
	}
290
291
	/**
292
	 * Get the quota of a user
293
	 *
294
	 * @param string $userId
295
	 * @return int Quota bytes
296
	 */
297
	public static function getUserQuota($userId) {
298
		$user = \OC::$server->getUserManager()->get($userId);
299
		if (is_null($user)) {
300
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
301
		}
302
		$userQuota = $user->getQuota();
303
		if($userQuota === 'none') {
304
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
305
		}
306
		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 306 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...
307
	}
308
309
	/**
310
	 * copies the skeleton to the users /files
311
	 *
312
	 * @param String $userId
313
	 * @param \OCP\Files\Folder $userDirectory
314
	 * @throws \RuntimeException
315
	 */
316
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
317
318
		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
319
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
320
321
		if ($instanceId === null) {
322
			throw new \RuntimeException('no instance id!');
323
		}
324
		$appdata = 'appdata_' . $instanceId;
325
		if ($userId === $appdata) {
326
			throw new \RuntimeException('username is reserved name: ' . $appdata);
327
		}
328
329
		if (!empty($skeletonDirectory)) {
330
			\OCP\Util::writeLog(
331
				'files_skeleton',
332
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
333
				\OCP\Util::DEBUG
334
			);
335
			self::copyr($skeletonDirectory, $userDirectory);
336
			// update the file cache
337
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
338
		}
339
	}
340
341
	/**
342
	 * copies a directory recursively by using streams
343
	 *
344
	 * @param string $source
345
	 * @param \OCP\Files\Folder $target
346
	 * @return void
347
	 */
348
	public static function copyr($source, \OCP\Files\Folder $target) {
349
		$logger = \OC::$server->getLogger();
350
351
		// Verify if folder exists
352
		$dir = opendir($source);
353
		if($dir === false) {
354
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
355
			return;
356
		}
357
358
		// Copy the files
359
		while (false !== ($file = readdir($dir))) {
360
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
361
				if (is_dir($source . '/' . $file)) {
362
					$child = $target->newFolder($file);
363
					self::copyr($source . '/' . $file, $child);
364
				} else {
365
					$child = $target->newFile($file);
366
					$sourceStream = fopen($source . '/' . $file, 'r');
367
					if($sourceStream === false) {
368
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
369
						closedir($dir);
370
						return;
371
					}
372
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
373
				}
374
			}
375
		}
376
		closedir($dir);
377
	}
378
379
	/**
380
	 * @return void
381
	 */
382
	public static function tearDownFS() {
383
		\OC\Files\Filesystem::tearDown();
384
		\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...
385
		self::$fsSetup = false;
386
		self::$rootMounted = false;
387
	}
388
389
	/**
390
	 * get the current installed version of ownCloud
391
	 *
392
	 * @return array
393
	 */
394
	public static function getVersion() {
395
		OC_Util::loadVersion();
396
		return \OC::$server->getSession()->get('OC_Version');
397
	}
398
399
	/**
400
	 * get the current installed version string of ownCloud
401
	 *
402
	 * @return string
403
	 */
404
	public static function getVersionString() {
405
		OC_Util::loadVersion();
406
		return \OC::$server->getSession()->get('OC_VersionString');
407
	}
408
409
	/**
410
	 * @deprecated the value is of no use anymore
411
	 * @return string
412
	 */
413
	public static function getEditionString() {
414
		return '';
415
	}
416
417
	/**
418
	 * @description get the update channel of the current installed of ownCloud.
419
	 * @return string
420
	 */
421
	public static function getChannel() {
422
		OC_Util::loadVersion();
423
		return \OC::$server->getSession()->get('OC_Channel');
424
	}
425
426
	/**
427
	 * @description get the build number of the current installed of ownCloud.
428
	 * @return string
429
	 */
430
	public static function getBuild() {
431
		OC_Util::loadVersion();
432
		return \OC::$server->getSession()->get('OC_Build');
433
	}
434
435
	/**
436
	 * @description load the version.php into the session as cache
437
	 */
438
	private static function loadVersion() {
439
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
440
		if (!\OC::$server->getSession()->exists('OC_Version') or OC::$server->getSession()->get('OC_Version_Timestamp') != $timestamp) {
441
			require OC::$SERVERROOT . '/version.php';
442
			$session = \OC::$server->getSession();
443
			/** @var $timestamp int */
444
			$session->set('OC_Version_Timestamp', $timestamp);
445
			/** @var $OC_Version string */
446
			$session->set('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...
447
			/** @var $OC_VersionString string */
448
			$session->set('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...
449
			/** @var $OC_Build string */
450
			$session->set('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...
451
			
452
			// Allow overriding update channel
453
			
454 View Code Duplication
			if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
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...
455
				$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...
456
			} else {
457
				/** @var $OC_Channel string */
458
				$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...
459
			}
460
			
461
			if (!is_null($channel)) {
462
				$session->set('OC_Channel', $channel);
463
			} else {
464
				/** @var $OC_Channel string */
465
				$session->set('OC_Channel', $OC_Channel);
466
			}
467
		}
468
	}
469
470
	/**
471
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
472
	 *
473
	 * @param string $application application to get the files from
474
	 * @param string $directory directory within this application (css, js, vendor, etc)
475
	 * @param string $file the file inside of the above folder
476
	 * @return string the path
477
	 */
478
	private static function generatePath($application, $directory, $file) {
479
		if (is_null($file)) {
480
			$file = $application;
481
			$application = "";
482
		}
483
		if (!empty($application)) {
484
			return "$application/$directory/$file";
485
		} else {
486
			return "$directory/$file";
487
		}
488
	}
489
490
	/**
491
	 * add a javascript file
492
	 *
493
	 * @param string $application application id
494
	 * @param string|null $file filename
495
	 * @param bool $prepend prepend the Script to the beginning of the list
496
	 * @return void
497
	 */
498
	public static function addScript($application, $file = null, $prepend = false) {
499
		$path = OC_Util::generatePath($application, 'js', $file);
500
501
		// core js files need separate handling
502
		if ($application !== 'core' && $file !== null) {
503
			self::addTranslations ( $application );
504
		}
505
		self::addExternalResource($application, $prepend, $path, "script");
506
	}
507
508
	/**
509
	 * add a javascript file from the vendor sub folder
510
	 *
511
	 * @param string $application application id
512
	 * @param string|null $file filename
513
	 * @param bool $prepend prepend the Script to the beginning of the list
514
	 * @return void
515
	 */
516
	public static function addVendorScript($application, $file = null, $prepend = false) {
517
		$path = OC_Util::generatePath($application, 'vendor', $file);
518
		self::addExternalResource($application, $prepend, $path, "script");
519
	}
520
521
	/**
522
	 * add a translation JS file
523
	 *
524
	 * @param string $application application id
525
	 * @param string $languageCode language code, defaults to the current language
526
	 * @param bool $prepend prepend the Script to the beginning of the list 
527
	 */
528
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
529
		if (is_null($languageCode)) {
530
			$languageCode = \OC_L10N::findLanguage($application);
0 ignored issues
show
Deprecated Code introduced by
The method OC_L10N::findLanguage() has been deprecated with message: 9.0.0 Use \OC::$server->getL10NFactory()->findLanguage() instead

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...
531
		}
532
		if (!empty($application)) {
533
			$path = "$application/l10n/$languageCode";
534
		} else {
535
			$path = "l10n/$languageCode";
536
		}
537
		self::addExternalResource($application, $prepend, $path, "script");
538
	}
539
540
	/**
541
	 * add a css file
542
	 *
543
	 * @param string $application application id
544
	 * @param string|null $file filename
545
	 * @param bool $prepend prepend the Style to the beginning of the list
546
	 * @return void
547
	 */
548
	public static function addStyle($application, $file = null, $prepend = false) {
549
		$path = OC_Util::generatePath($application, 'css', $file);
550
		self::addExternalResource($application, $prepend, $path, "style");
551
	}
552
553
	/**
554
	 * add a css file from the vendor sub folder
555
	 *
556
	 * @param string $application application id
557
	 * @param string|null $file filename
558
	 * @param bool $prepend prepend the Style to the beginning of the list
559
	 * @return void
560
	 */
561
	public static function addVendorStyle($application, $file = null, $prepend = false) {
562
		$path = OC_Util::generatePath($application, 'vendor', $file);
563
		self::addExternalResource($application, $prepend, $path, "style");
564
	}
565
566
	/**
567
	 * add an external resource css/js file
568
	 *
569
	 * @param string $application application id
570
	 * @param bool $prepend prepend the file to the beginning of the list
571
	 * @param string $path 
572
	 * @param string $type (script or style)
573
	 * @return void
574
	 */
575
	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...
576
577
		if ($type === "style") {
578 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...
579
				if ($prepend === true) {
580
					array_unshift ( self::$styles, $path );
581
				} else {
582
					self::$styles[] = $path;
583
				}
584
			}
585 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...
586
			if (!in_array($path, self::$scripts)) {
587
				if ($prepend === true) {
588
					array_unshift ( self::$scripts, $path );
589
				} else {
590
					self::$scripts [] = $path;
591
				}
592
			}
593
		}
594
	}
595
596
	/**
597
	 * Add a custom element to the header
598
	 * If $text is null then the element will be written as empty element.
599
	 * So use "" to get a closing tag.
600
	 * @param string $tag tag name of the element
601
	 * @param array $attributes array of attributes for the element
602
	 * @param string $text the text content for the element
603
	 */
604 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...
605
		self::$headers[] = array(
606
			'tag' => $tag,
607
			'attributes' => $attributes,
608
			'text' => $text
609
		);
610
	}
611
612
	/**
613
	 * formats a timestamp in the "right" way
614
	 *
615
	 * @param int $timestamp
616
	 * @param bool $dateOnly option to omit time from the result
617
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
618
	 * @return string timestamp
619
	 *
620
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
621
	 */
622
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
623
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
624
			$timeZone = new \DateTimeZone($timeZone);
625
		}
626
627
		/** @var \OC\DateTimeFormatter $formatter */
628
		$formatter = \OC::$server->query('DateTimeFormatter');
629
		if ($dateOnly) {
630
			return $formatter->formatDate($timestamp, 'long', $timeZone);
631
		}
632
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
633
	}
634
635
	/**
636
	 * check if the current server configuration is suitable for ownCloud
637
	 *
638
	 * @param \OCP\IConfig $config
639
	 * @return array arrays with error messages and hints
640
	 */
641
	public static function checkServer(\OCP\IConfig $config) {
642
		$l = \OC::$server->getL10N('lib');
643
		$errors = array();
644
		$CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
645
646
		if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
647
			// this check needs to be done every time
648
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
649
		}
650
651
		// Assume that if checkServer() succeeded before in this session, then all is fine.
652
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
653
			return $errors;
654
		}
655
656
		$webServerRestart = false;
657
		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
658
			\OC::$server->getThemingDefaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
659
660
		$urlGenerator = \OC::$server->getURLGenerator();
661
662
		$availableDatabases = $setup->getSupportedDatabases();
663
		if (empty($availableDatabases)) {
664
			$errors[] = array(
665
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
666
				'hint' => '' //TODO: sane hint
667
			);
668
			$webServerRestart = true;
669
		}
670
671
		// Check if server running on Windows platform
672 View Code Duplication
		if(OC_Util::runningOnWindows()) {
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...
673
			$errors[] = [
674
				'error' => $l->t('Microsoft Windows Platform is not supported'),
675
				'hint' => $l->t('Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you ' .
676
					'use a Linux server in a virtual machine if you have no option for migrating the server itself.')
677
			];
678
		}
679
680
		// Check if config folder is writable.
681
		if(!OC_Helper::isReadOnlyConfigEnabled()) {
682
			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
683
				$errors[] = array(
684
					'error' => $l->t('Cannot write into "config" directory'),
685
					'hint' => $l->t('This can usually be fixed by '
686
						. '%sgiving the webserver write access to the config directory%s.',
687
						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
688
				);
689
			}
690
		}
691
692
		// Check if there is a writable install folder.
693
		if ($config->getSystemValue('appstoreenabled', true)) {
694
			if (OC_App::getInstallPath() === null
695
				|| !is_writable(OC_App::getInstallPath())
696
				|| !is_readable(OC_App::getInstallPath())
697
			) {
698
				$errors[] = array(
699
					'error' => $l->t('Cannot write into "apps" directory'),
700
					'hint' => $l->t('This can usually be fixed by '
701
						. '%sgiving the webserver write access to the apps directory%s'
702
						. ' or disabling the appstore in the config file.',
703
						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
704
				);
705
			}
706
		}
707
		// Create root dir.
708
		if ($config->getSystemValue('installed', false)) {
709
			if (!is_dir($CONFIG_DATADIRECTORY)) {
710
				$success = @mkdir($CONFIG_DATADIRECTORY);
711
				if ($success) {
712
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
713
				} else {
714
					$errors[] = array(
715
						'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
716
						'hint' => $l->t('This can usually be fixed by '
717
							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
718
							array($urlGenerator->linkToDocs('admin-dir_permissions')))
719
					);
720
				}
721
			} else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
722
				//common hint for all file permissions error messages
723
				$permissionsHint = $l->t('Permissions can usually be fixed by '
724
					. '%sgiving the webserver write access to the root directory%s.',
725
					array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
726
				$errors[] = array(
727
					'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
728
					'hint' => $permissionsHint
729
				);
730
			} else {
731
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
732
			}
733
		}
734
735 View Code Duplication
		if (!OC_Util::isSetLocaleWorking()) {
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...
736
			$errors[] = array(
737
				'error' => $l->t('Setting locale to %s failed',
738
					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
739
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
740
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
741
			);
742
		}
743
744
		// Contains the dependencies that should be checked against
745
		// classes = class_exists
746
		// functions = function_exists
747
		// defined = defined
748
		// ini = ini_get
749
		// If the dependency is not found the missing module name is shown to the EndUser
750
		// When adding new checks always verify that they pass on Travis as well
751
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
752
		$dependencies = array(
753
			'classes' => array(
754
				'ZipArchive' => 'zip',
755
				'DOMDocument' => 'dom',
756
				'XMLWriter' => 'XMLWriter',
757
				'XMLReader' => 'XMLReader',
758
			),
759
			'functions' => [
760
				'xml_parser_create' => 'libxml',
761
				'mb_strcut' => 'mb multibyte',
762
				'ctype_digit' => 'ctype',
763
				'json_encode' => 'JSON',
764
				'gd_info' => 'GD',
765
				'gzencode' => 'zlib',
766
				'iconv' => 'iconv',
767
				'simplexml_load_string' => 'SimpleXML',
768
				'hash' => 'HASH Message Digest Framework',
769
				'curl_init' => 'cURL',
770
			],
771
			'defined' => array(
772
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
773
			),
774
			'ini' => [
775
				'default_charset' => 'UTF-8',
776
			],
777
		);
778
		$missingDependencies = array();
779
		$invalidIniSettings = [];
780
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
781
782
		/**
783
		 * FIXME: The dependency check does not work properly on HHVM on the moment
784
		 *        and prevents installation. Once HHVM is more compatible with our
785
		 *        approach to check for these values we should re-enable those
786
		 *        checks.
787
		 */
788
		$iniWrapper = \OC::$server->getIniWrapper();
789
		if (!self::runningOnHhvm()) {
790
			foreach ($dependencies['classes'] as $class => $module) {
791
				if (!class_exists($class)) {
792
					$missingDependencies[] = $module;
793
				}
794
			}
795
			foreach ($dependencies['functions'] as $function => $module) {
796
				if (!function_exists($function)) {
797
					$missingDependencies[] = $module;
798
				}
799
			}
800
			foreach ($dependencies['defined'] as $defined => $module) {
801
				if (!defined($defined)) {
802
					$missingDependencies[] = $module;
803
				}
804
			}
805
			foreach ($dependencies['ini'] as $setting => $expected) {
806
				if (is_bool($expected)) {
807
					if ($iniWrapper->getBool($setting) !== $expected) {
808
						$invalidIniSettings[] = [$setting, $expected];
809
					}
810
				}
811
				if (is_int($expected)) {
812
					if ($iniWrapper->getNumeric($setting) !== $expected) {
813
						$invalidIniSettings[] = [$setting, $expected];
814
					}
815
				}
816
				if (is_string($expected)) {
817
					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
818
						$invalidIniSettings[] = [$setting, $expected];
819
					}
820
				}
821
			}
822
		}
823
824
		foreach($missingDependencies as $missingDependency) {
825
			$errors[] = array(
826
				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
827
				'hint' => $moduleHint
828
			);
829
			$webServerRestart = true;
830
		}
831
		foreach($invalidIniSettings as $setting) {
832
			if(is_bool($setting[1])) {
833
				$setting[1] = ($setting[1]) ? 'on' : 'off';
834
			}
835
			$errors[] = [
836
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
837
				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
838
			];
839
			$webServerRestart = true;
840
		}
841
842
		/**
843
		 * The mbstring.func_overload check can only be performed if the mbstring
844
		 * module is installed as it will return null if the checking setting is
845
		 * not available and thus a check on the boolean value fails.
846
		 *
847
		 * TODO: Should probably be implemented in the above generic dependency
848
		 *       check somehow in the long-term.
849
		 */
850
		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
851
			$iniWrapper->getBool('mbstring.func_overload') === true) {
852
			$errors[] = array(
853
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
854
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
855
			);
856
		}
857
858
		if(function_exists('xml_parser_create') &&
859
			LIBXML_LOADED_VERSION < 20700 ) {
860
			$version = LIBXML_LOADED_VERSION;
861
			$major = floor($version/10000);
862
			$version -= ($major * 10000);
863
			$minor = floor($version/100);
864
			$version -= ($minor * 100);
865
			$patch = $version;
866
			$errors[] = array(
867
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
868
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
869
			);
870
		}
871
872
		if (!self::isAnnotationsWorking()) {
873
			$errors[] = array(
874
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
875
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
876
			);
877
		}
878
879
		if (!\OC::$CLI && $webServerRestart) {
880
			$errors[] = array(
881
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
882
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
883
			);
884
		}
885
886
		$errors = array_merge($errors, self::checkDatabaseVersion());
887
888
		// Cache the result of this function
889
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
890
891
		return $errors;
892
	}
893
894
	/**
895
	 * Check the database version
896
	 *
897
	 * @return array errors array
898
	 */
899
	public static function checkDatabaseVersion() {
900
		$l = \OC::$server->getL10N('lib');
901
		$errors = array();
902
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
903
		if ($dbType === 'pgsql') {
904
			// check PostgreSQL version
905
			try {
906
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
907
				$data = $result->fetchRow();
908
				if (isset($data['server_version'])) {
909
					$version = $data['server_version'];
910
					if (version_compare($version, '9.0.0', '<')) {
911
						$errors[] = array(
912
							'error' => $l->t('PostgreSQL >= 9 required'),
913
							'hint' => $l->t('Please upgrade your database version')
914
						);
915
					}
916
				}
917
			} 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...
918
				$logger = \OC::$server->getLogger();
919
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
920
				$logger->logException($e);
921
			}
922
		}
923
		return $errors;
924
	}
925
926
	/**
927
	 * Check for correct file permissions of data directory
928
	 *
929
	 * @param string $dataDirectory
930
	 * @return array arrays with error messages and hints
931
	 */
932
	public static function checkDataDirectoryPermissions($dataDirectory) {
933
		$l = \OC::$server->getL10N('lib');
934
		$errors = array();
935
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
936
			. ' cannot be listed by other users.');
937
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
938
		if (substr($perms, -1) != '0') {
939
			chmod($dataDirectory, 0770);
940
			clearstatcache();
941
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
942 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...
943
				$errors[] = array(
944
					'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
945
					'hint' => $permissionsModHint
946
				);
947
			}
948
		}
949
		return $errors;
950
	}
951
952
	/**
953
	 * Check that the data directory exists and is valid by
954
	 * checking the existence of the ".ocdata" file.
955
	 *
956
	 * @param string $dataDirectory data directory path
957
	 * @return array errors found
958
	 */
959
	public static function checkDataDirectoryValidity($dataDirectory) {
960
		$l = \OC::$server->getL10N('lib');
961
		$errors = [];
962 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...
963
			$errors[] = [
964
				'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
965
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
966
			];
967
		}
968 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...
969
			$errors[] = [
970
				'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
971
				'hint' => $l->t('Please check that the data directory contains a file' .
972
					' ".ocdata" in its root.')
973
			];
974
		}
975
		return $errors;
976
	}
977
978
	/**
979
	 * Check if the user is logged in, redirects to home if not. With
980
	 * redirect URL parameter to the request URI.
981
	 *
982
	 * @return void
983
	 */
984
	public static function checkLoggedIn() {
985
		// Check if we are a user
986
		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...
987
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
988
						'core.login.showLoginForm',
989
						[
990
							'redirect_url' => urlencode(\OC::$server->getRequest()->getRequestUri()),
991
						]
992
					)
993
			);
994
			exit();
995
		}
996
		// Redirect to index page if 2FA challenge was not solved yet
997
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
998
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
999
			exit();
1000
		}
1001
	}
1002
1003
	/**
1004
	 * Check if the user is a admin, redirects to home if not
1005
	 *
1006
	 * @return void
1007
	 */
1008
	public static function checkAdminUser() {
1009
		OC_Util::checkLoggedIn();
1010
		if (!OC_User::isAdminUser(OC_User::getUser())) {
1011
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1012
			exit();
1013
		}
1014
	}
1015
1016
	/**
1017
	 * Check if it is allowed to remember login.
1018
	 *
1019
	 * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
1020
	 *
1021
	 * @return bool
1022
	 */
1023
	public static function rememberLoginAllowed() {
1024
1025
		$apps = OC_App::getEnabledApps();
1026
1027
		foreach ($apps as $app) {
1028
			$appInfo = OC_App::getAppInfo($app);
1029
			if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
1030
				return false;
1031
			}
1032
1033
		}
1034
		return true;
1035
	}
1036
1037
	/**
1038
	 * Check if the user is a subadmin, redirects to home if not
1039
	 *
1040
	 * @return null|boolean $groups where the current user is subadmin
1041
	 */
1042
	public static function checkSubAdminUser() {
1043
		OC_Util::checkLoggedIn();
1044
		$userObject = \OC::$server->getUserSession()->getUser();
1045
		$isSubAdmin = false;
1046
		if($userObject !== null) {
1047
			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1048
		}
1049
1050
		if (!$isSubAdmin) {
1051
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1052
			exit();
1053
		}
1054
		return true;
1055
	}
1056
1057
	/**
1058
	 * Returns the URL of the default page
1059
	 * based on the system configuration and
1060
	 * the apps visible for the current user
1061
	 *
1062
	 * @return string URL
1063
	 */
1064
	public static function getDefaultPageUrl() {
1065
		$urlGenerator = \OC::$server->getURLGenerator();
1066
		// Deny the redirect if the URL contains a @
1067
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1068
		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1069
			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1070
		} else {
1071
			$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...
1072
			if ($defaultPage) {
1073
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1074
			} else {
1075
				$appId = 'files';
1076
				$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...
1077
				// find the first app that is enabled for the current user
1078
				foreach ($defaultApps as $defaultApp) {
1079
					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1080
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1081
						$appId = $defaultApp;
1082
						break;
1083
					}
1084
				}
1085
1086
				if(getenv('front_controller_active') === 'true') {
1087
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1088
				} else {
1089
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1090
				}
1091
			}
1092
		}
1093
		return $location;
1094
	}
1095
1096
	/**
1097
	 * Redirect to the user default page
1098
	 *
1099
	 * @return void
1100
	 */
1101
	public static function redirectToDefaultPage() {
1102
		$location = self::getDefaultPageUrl();
1103
		header('Location: ' . $location);
1104
		exit();
1105
	}
1106
1107
	/**
1108
	 * get an id unique for this instance
1109
	 *
1110
	 * @return string
1111
	 */
1112
	public static function getInstanceId() {
1113
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1114
		if (is_null($id)) {
1115
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1116
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1117
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1118
		}
1119
		return $id;
1120
	}
1121
1122
	/**
1123
	 * Public function to sanitize HTML
1124
	 *
1125
	 * This function is used to sanitize HTML and should be applied on any
1126
	 * string or array of strings before displaying it on a web page.
1127
	 *
1128
	 * @param string|array $value
1129
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1130
	 */
1131
	public static function sanitizeHTML($value) {
1132
		if (is_array($value)) {
1133
			$value = array_map(function($value) {
1134
				return self::sanitizeHTML($value);
1135
			}, $value);
1136
		} else {
1137
			// Specify encoding for PHP<5.4
1138
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1139
		}
1140
		return $value;
1141
	}
1142
1143
	/**
1144
	 * Public function to encode url parameters
1145
	 *
1146
	 * This function is used to encode path to file before output.
1147
	 * Encoding is done according to RFC 3986 with one exception:
1148
	 * Character '/' is preserved as is.
1149
	 *
1150
	 * @param string $component part of URI to encode
1151
	 * @return string
1152
	 */
1153
	public static function encodePath($component) {
1154
		$encoded = rawurlencode($component);
1155
		$encoded = str_replace('%2F', '/', $encoded);
1156
		return $encoded;
1157
	}
1158
1159
1160
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1161
		// php dev server does not support htaccess
1162
		if (php_sapi_name() === 'cli-server') {
1163
			return false;
1164
		}
1165
1166
		// testdata
1167
		$fileName = '/htaccesstest.txt';
1168
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1169
1170
		// creating a test file
1171
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1172
1173
		if (file_exists($testFile)) {// already running this test, possible recursive call
1174
			return false;
1175
		}
1176
1177
		$fp = @fopen($testFile, 'w');
1178
		if (!$fp) {
1179
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1180
				'Make sure it is possible for the webserver to write to ' . $testFile);
1181
		}
1182
		fwrite($fp, $testContent);
1183
		fclose($fp);
1184
	}
1185
1186
	/**
1187
	 * Check if the .htaccess file is working
1188
	 * @param \OCP\IConfig $config
1189
	 * @return bool
1190
	 * @throws Exception
1191
	 * @throws \OC\HintException If the test file can't get written.
1192
	 */
1193
	public function isHtaccessWorking(\OCP\IConfig $config) {
1194
1195
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1196
			return true;
1197
		}
1198
1199
		$testContent = $this->createHtaccessTestFile($config);
1200
		if ($testContent === false) {
1201
			return false;
1202
		}
1203
1204
		$fileName = '/htaccesstest.txt';
1205
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1206
1207
		// accessing the file via http
1208
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1209
		try {
1210
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1211
		} catch (\Exception $e) {
1212
			$content = false;
1213
		}
1214
1215
		// cleanup
1216
		@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...
1217
1218
		/*
1219
		 * If the content is not equal to test content our .htaccess
1220
		 * is working as required
1221
		 */
1222
		return $content !== $testContent;
1223
	}
1224
1225
	/**
1226
	 * Check if the setlocal call does not work. This can happen if the right
1227
	 * local packages are not available on the server.
1228
	 *
1229
	 * @return bool
1230
	 */
1231
	public static function isSetLocaleWorking() {
1232
		\Patchwork\Utf8\Bootup::initLocale();
1233
		if ('' === basename('§')) {
1234
			return false;
1235
		}
1236
		return true;
1237
	}
1238
1239
	/**
1240
	 * Check if it's possible to get the inline annotations
1241
	 *
1242
	 * @return bool
1243
	 */
1244
	public static function isAnnotationsWorking() {
1245
		$reflection = new \ReflectionMethod(__METHOD__);
1246
		$docs = $reflection->getDocComment();
1247
1248
		return (is_string($docs) && strlen($docs) > 50);
1249
	}
1250
1251
	/**
1252
	 * Check if the PHP module fileinfo is loaded.
1253
	 *
1254
	 * @return bool
1255
	 */
1256
	public static function fileInfoLoaded() {
1257
		return function_exists('finfo_open');
1258
	}
1259
1260
	/**
1261
	 * clear all levels of output buffering
1262
	 *
1263
	 * @return void
1264
	 */
1265
	public static function obEnd() {
1266
		while (ob_get_level()) {
1267
			ob_end_clean();
1268
		}
1269
	}
1270
1271
	/**
1272
	 * Checks whether the server is running on Windows
1273
	 *
1274
	 * @return bool true if running on Windows, false otherwise
1275
	 */
1276
	public static function runningOnWindows() {
1277
		return (substr(PHP_OS, 0, 3) === "WIN");
1278
	}
1279
1280
	/**
1281
	 * Checks whether the server is running on Mac OS X
1282
	 *
1283
	 * @return bool true if running on Mac OS X, false otherwise
1284
	 */
1285
	public static function runningOnMac() {
1286
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1287
	}
1288
1289
	/**
1290
	 * Checks whether server is running on HHVM
1291
	 *
1292
	 * @return bool True if running on HHVM, false otherwise
1293
	 */
1294
	public static function runningOnHhvm() {
1295
		return defined('HHVM_VERSION');
1296
	}
1297
1298
	/**
1299
	 * Handles the case that there may not be a theme, then check if a "default"
1300
	 * theme exists and take that one
1301
	 *
1302
	 * @return string the theme
1303
	 */
1304
	public static function getTheme() {
1305
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1306
1307
		if ($theme === '') {
1308
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1309
				$theme = 'default';
1310
			}
1311
		}
1312
1313
		return $theme;
1314
	}
1315
1316
	/**
1317
	 * Clear a single file from the opcode cache
1318
	 * This is useful for writing to the config file
1319
	 * in case the opcode cache does not re-validate files
1320
	 * Returns true if successful, false if unsuccessful:
1321
	 * caller should fall back on clearing the entire cache
1322
	 * with clearOpcodeCache() if unsuccessful
1323
	 *
1324
	 * @param string $path the path of the file to clear from the cache
1325
	 * @return bool true if underlying function returns true, otherwise false
1326
	 */
1327
	public static function deleteFromOpcodeCache($path) {
1328
		$ret = false;
1329
		if ($path) {
1330
			// APC >= 3.1.1
1331
			if (function_exists('apc_delete_file')) {
1332
				$ret = @apc_delete_file($path);
1333
			}
1334
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1335
			if (function_exists('opcache_invalidate')) {
1336
				$ret = opcache_invalidate($path);
1337
			}
1338
		}
1339
		return $ret;
1340
	}
1341
1342
	/**
1343
	 * Clear the opcode cache if one exists
1344
	 * This is necessary for writing to the config file
1345
	 * in case the opcode cache does not re-validate files
1346
	 *
1347
	 * @return void
1348
	 */
1349
	public static function clearOpcodeCache() {
1350
		// APC
1351
		if (function_exists('apc_clear_cache')) {
1352
			apc_clear_cache();
1353
		}
1354
		// Zend Opcache
1355
		if (function_exists('accelerator_reset')) {
1356
			accelerator_reset();
1357
		}
1358
		// XCache
1359
		if (function_exists('xcache_clear_cache')) {
1360
			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1361
				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1362
			} else {
1363
				@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...
1364
			}
1365
		}
1366
		// Opcache (PHP >= 5.5)
1367
		if (function_exists('opcache_reset')) {
1368
			opcache_reset();
1369
		}
1370
	}
1371
1372
	/**
1373
	 * Normalize a unicode string
1374
	 *
1375
	 * @param string $value a not normalized string
1376
	 * @return bool|string
1377
	 */
1378
	public static function normalizeUnicode($value) {
1379
		if(Normalizer::isNormalized($value)) {
1380
			return $value;
1381
		}
1382
1383
		$normalizedValue = Normalizer::normalize($value);
1384
		if ($normalizedValue === null || $normalizedValue === false) {
1385
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1386
			return $value;
1387
		}
1388
1389
		return $normalizedValue;
1390
	}
1391
1392
	/**
1393
	 * @param boolean|string $file
1394
	 * @return string
1395
	 */
1396
	public static function basename($file) {
1397
		$file = rtrim($file, '/');
1398
		$t = explode('/', $file);
1399
		return array_pop($t);
1400
	}
1401
1402
	/**
1403
	 * A human readable string is generated based on version, channel and build number
1404
	 *
1405
	 * @return string
1406
	 */
1407
	public static function getHumanVersion() {
1408
		$version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
1409
		$build = OC_Util::getBuild();
1410
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1411
			$version .= ' Build:' . $build;
1412
		}
1413
		return $version;
1414
	}
1415
1416
	/**
1417
	 * Returns whether the given file name is valid
1418
	 *
1419
	 * @param string $file file name to check
1420
	 * @return bool true if the file name is valid, false otherwise
1421
	 * @deprecated use \OC\Files\View::verifyPath()
1422
	 */
1423
	public static function isValidFileName($file) {
1424
		$trimmed = trim($file);
1425
		if ($trimmed === '') {
1426
			return false;
1427
		}
1428
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1429
			return false;
1430
		}
1431
		foreach (str_split($trimmed) as $char) {
1432
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1433
				return false;
1434
			}
1435
		}
1436
		return true;
1437
	}
1438
1439
	/**
1440
	 * Check whether the instance needs to perform an upgrade,
1441
	 * either when the core version is higher or any app requires
1442
	 * an upgrade.
1443
	 *
1444
	 * @param \OCP\IConfig $config
1445
	 * @return bool whether the core or any app needs an upgrade
1446
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1447
	 */
1448
	public static function needUpgrade(\OCP\IConfig $config) {
1449
		if ($config->getSystemValue('installed', false)) {
1450
			$installedVersion = $config->getSystemValue('version', '0.0.0');
1451
			$currentVersion = implode('.', \OCP\Util::getVersion());
1452
			$versionDiff = version_compare($currentVersion, $installedVersion);
1453
			if ($versionDiff > 0) {
1454
				return true;
1455
			} else if ($config->getSystemValue('debug', false) && $versionDiff < 0) {
1456
				// downgrade with debug
1457
				$installedMajor = explode('.', $installedVersion);
1458
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1459
				$currentMajor = explode('.', $currentVersion);
1460
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1461
				if ($installedMajor === $currentMajor) {
1462
					// Same major, allow downgrade for developers
1463
					return true;
1464
				} else {
1465
					// downgrade attempt, throw exception
1466
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1467
				}
1468
			} else if ($versionDiff < 0) {
1469
				// downgrade attempt, throw exception
1470
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1471
			}
1472
1473
			// also check for upgrades for apps (independently from the user)
1474
			$apps = \OC_App::getEnabledApps(false, true);
1475
			$shouldUpgrade = false;
1476
			foreach ($apps as $app) {
1477
				if (\OC_App::shouldUpgrade($app)) {
1478
					$shouldUpgrade = true;
1479
					break;
1480
				}
1481
			}
1482
			return $shouldUpgrade;
1483
		} else {
1484
			return false;
1485
		}
1486
	}
1487
1488
}
1489