Completed
Push — stable10 ( 766abe...3102af )
by Lukas
09:50
created

OC_Util::copyr()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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