Completed
Push — master ( 3c4251...063036 )
by Roeland
11:46
created

OC_Util::runningOnHhvm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
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 Christoph Wurst <[email protected]>
14
 * @author Christopher Schäpers <[email protected]>
15
 * @author Clark Tomlinson <[email protected]>
16
 * @author cmeh <[email protected]>
17
 * @author Felix Epp <[email protected]>
18
 * @author Florin Peter <[email protected]>
19
 * @author Frank Karlitschek <[email protected]>
20
 * @author Georg Ehrke <[email protected]>
21
 * @author helix84 <[email protected]>
22
 * @author Ilja Neumann <[email protected]>
23
 * @author Individual IT Services <[email protected]>
24
 * @author Jakob Sack <[email protected]>
25
 * @author Joas Schilling <[email protected]>
26
 * @author Jörn Friedrich Dreyer <[email protected]>
27
 * @author Kawohl <[email protected]>
28
 * @author Lukas Reschke <[email protected]>
29
 * @author Markus Goetz <[email protected]>
30
 * @author Martin Mattel <[email protected]>
31
 * @author Marvin Thomas Rabe <[email protected]>
32
 * @author Michael Gapczynski <[email protected]>
33
 * @author Morris Jobke <[email protected]>
34
 * @author rakekniven <[email protected]>
35
 * @author Robin Appelman <[email protected]>
36
 * @author Robin McCorkell <[email protected]>
37
 * @author Roeland Jago Douma <[email protected]>
38
 * @author Sebastian Wessalowski <[email protected]>
39
 * @author Stefan Rado <[email protected]>
40
 * @author Stefan Weil <[email protected]>
41
 * @author Thomas Müller <[email protected]>
42
 * @author Thomas Tanghus <[email protected]>
43
 * @author Victor Dubiniuk <[email protected]>
44
 * @author Vincent Petry <[email protected]>
45
 * @author Volkan Gezer <[email protected]>
46
 *
47
 * @license AGPL-3.0
48
 *
49
 * This code is free software: you can redistribute it and/or modify
50
 * it under the terms of the GNU Affero General Public License, version 3,
51
 * as published by the Free Software Foundation.
52
 *
53
 * This program is distributed in the hope that it will be useful,
54
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
55
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
 * GNU Affero General Public License for more details.
57
 *
58
 * You should have received a copy of the GNU Affero General Public License, version 3,
59
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
60
 *
61
 */
62
63
use OCP\IConfig;
64
use OCP\IGroupManager;
65
use OCP\ILogger;
66
use OCP\IUser;
67
use OC\AppFramework\Http\Request;
68
69
class OC_Util {
70
	public static $scripts = array();
71
	public static $styles = array();
72
	public static $headers = array();
73
	private static $rootMounted = false;
74
	private static $fsSetup = false;
75
76
	/** @var array Local cache of version.php */
77
	private static $versionCache = null;
78
79
	protected static function getAppManager() {
80
		return \OC::$server->getAppManager();
81
	}
82
83
	private static function initLocalStorageRootFS() {
84
		// mount local file backend as root
85
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
86
		//first set up the local "root" storage
87
		\OC\Files\Filesystem::initMountManager();
88
		if (!self::$rootMounted) {
89
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
90
			self::$rootMounted = true;
91
		}
92
	}
93
94
	/**
95
	 * mounting an object storage as the root fs will in essence remove the
96
	 * necessity of a data folder being present.
97
	 * TODO make home storage aware of this and use the object storage instead of local disk access
98
	 *
99
	 * @param array $config containing 'class' and optional 'arguments'
100
	 * @suppress PhanDeprecatedFunction
101
	 */
102
	private static function initObjectStoreRootFS($config) {
103
		// check misconfiguration
104
		if (empty($config['class'])) {
105
			\OCP\Util::writeLog('files', 'No class given for objectstore', ILogger::ERROR);
106
		}
107
		if (!isset($config['arguments'])) {
108
			$config['arguments'] = array();
109
		}
110
111
		// instantiate object store implementation
112
		$name = $config['class'];
113 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
114
			$segments = explode('\\', $name);
115
			OC_App::loadApp(strtolower($segments[1]));
116
		}
117
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
118
		// mount with plain / root object store implementation
119
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
120
121
		// mount object storage as root
122
		\OC\Files\Filesystem::initMountManager();
123 View Code Duplication
		if (!self::$rootMounted) {
124
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
125
			self::$rootMounted = true;
126
		}
127
	}
128
129
	/**
130
	 * mounting an object storage as the root fs will in essence remove the
131
	 * necessity of a data folder being present.
132
	 *
133
	 * @param array $config containing 'class' and optional 'arguments'
134
	 * @suppress PhanDeprecatedFunction
135
	 */
136
	private static function initObjectStoreMultibucketRootFS($config) {
137
		// check misconfiguration
138
		if (empty($config['class'])) {
139
			\OCP\Util::writeLog('files', 'No class given for objectstore', ILogger::ERROR);
140
		}
141
		if (!isset($config['arguments'])) {
142
			$config['arguments'] = array();
143
		}
144
145
		// instantiate object store implementation
146
		$name = $config['class'];
147 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
148
			$segments = explode('\\', $name);
149
			OC_App::loadApp(strtolower($segments[1]));
150
		}
151
152
		if (!isset($config['arguments']['bucket'])) {
153
			$config['arguments']['bucket'] = '';
154
		}
155
		// put the root FS always in first bucket for multibucket configuration
156
		$config['arguments']['bucket'] .= '0';
157
158
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
159
		// mount with plain / root object store implementation
160
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
161
162
		// mount object storage as root
163
		\OC\Files\Filesystem::initMountManager();
164 View Code Duplication
		if (!self::$rootMounted) {
165
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
166
			self::$rootMounted = true;
167
		}
168
	}
169
170
	/**
171
	 * Can be set up
172
	 *
173
	 * @param string $user
174
	 * @return boolean
175
	 * @description configure the initial filesystem based on the configuration
176
	 * @suppress PhanDeprecatedFunction
177
	 * @suppress PhanAccessMethodInternal
178
	 */
179
	public static function setupFS($user = '') {
180
		//setting up the filesystem twice can only lead to trouble
181
		if (self::$fsSetup) {
182
			return false;
183
		}
184
185
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
186
187
		// If we are not forced to load a specific user we load the one that is logged in
188
		if ($user === null) {
189
			$user = '';
190
		} else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) {
191
			$user = OC_User::getUser();
192
		}
193
194
		// load all filesystem apps before, so no setup-hook gets lost
195
		OC_App::loadApps(array('filesystem'));
196
197
		// the filesystem will finish when $user is not empty,
198
		// mark fs setup here to avoid doing the setup from loading
199
		// OC_Filesystem
200
		if ($user != '') {
201
			self::$fsSetup = true;
202
		}
203
204
		\OC\Files\Filesystem::initMountManager();
205
206
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
207
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
208
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
209
				/** @var \OC\Files\Storage\Common $storage */
210
				$storage->setMountOptions($mount->getOptions());
211
			}
212
			return $storage;
213
		});
214
215
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
216
			if (!$mount->getOption('enable_sharing', true)) {
217
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
218
					'storage' => $storage,
219
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
220
				]);
221
			}
222
			return $storage;
223
		});
224
225
		// install storage availability wrapper, before most other wrappers
226
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
227
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
228
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
229
			}
230
			return $storage;
231
		});
232
233
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
234
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
235
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
236
			}
237
			return $storage;
238
		});
239
240
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
241
			// set up quota for home storages, even for other users
242
			// which can happen when using sharing
243
244
			/**
245
			 * @var \OC\Files\Storage\Storage $storage
246
			 */
247
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
248
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
249
			) {
250
				/** @var \OC\Files\Storage\Home $storage */
251
				if (is_object($storage->getUser())) {
252
					$user = $storage->getUser()->getUID();
253
					$quota = OC_Util::getUserQuota($user);
254
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
255
						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
256
					}
257
				}
258
			}
259
260
			return $storage;
261
		});
262
263
		\OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
264
			/*
265
			 * Do not allow any operations that modify the storage
266
			 */
267
			if ($mount->getOption('readonly', false)) {
268
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
269
					'storage' => $storage,
270
					'mask' => \OCP\Constants::PERMISSION_ALL & ~(
271
						\OCP\Constants::PERMISSION_UPDATE |
272
						\OCP\Constants::PERMISSION_CREATE |
273
						\OCP\Constants::PERMISSION_DELETE
274
					),
275
				]);
276
			}
277
			return $storage;
278
		});
279
280
		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
281
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
282
283
		//check if we are using an object storage
284
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
285
		$objectStoreMultibucket = \OC::$server->getSystemConfig()->getValue('objectstore_multibucket', null);
286
287
		// use the same order as in ObjectHomeMountProvider
288
		if (isset($objectStoreMultibucket)) {
289
			self::initObjectStoreMultibucketRootFS($objectStoreMultibucket);
290
		} elseif (isset($objectStore)) {
291
			self::initObjectStoreRootFS($objectStore);
292
		} else {
293
			self::initLocalStorageRootFS();
294
		}
295
296
		if ($user != '' && !\OC::$server->getUserManager()->userExists($user)) {
297
			\OC::$server->getEventLogger()->end('setup_fs');
298
			return false;
299
		}
300
301
		//if we aren't logged in, there is no use to set up the filesystem
302
		if ($user != "") {
303
304
			$userDir = '/' . $user . '/files';
305
306
			//jail the user into his "home" directory
307
			\OC\Files\Filesystem::init($user, $userDir);
308
309
			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
310
		}
311
		\OC::$server->getEventLogger()->end('setup_fs');
312
		return true;
313
	}
314
315
	/**
316
	 * check if a password is required for each public link
317
	 *
318
	 * @return boolean
319
	 * @suppress PhanDeprecatedFunction
320
	 */
321
	public static function isPublicLinkPasswordRequired() {
322
		$enforcePassword = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_links_password', 'no');
323
		return $enforcePassword === 'yes';
324
	}
325
326
	/**
327
	 * check if sharing is disabled for the current user
328
	 * @param IConfig $config
329
	 * @param IGroupManager $groupManager
330
	 * @param IUser|null $user
331
	 * @return bool
332
	 */
333
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
334
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
335
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
336
			$excludedGroups = json_decode($groupsList);
337 View Code Duplication
			if (is_null($excludedGroups)) {
338
				$excludedGroups = explode(',', $groupsList);
339
				$newValue = json_encode($excludedGroups);
340
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
341
			}
342
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 333 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...
343 View Code Duplication
			if (!empty($usersGroups)) {
344
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
345
				// if the user is only in groups which are disabled for sharing then
346
				// sharing is also disabled for the user
347
				if (empty($remainingGroups)) {
348
					return true;
349
				}
350
			}
351
		}
352
		return false;
353
	}
354
355
	/**
356
	 * check if share API enforces a default expire date
357
	 *
358
	 * @return boolean
359
	 * @suppress PhanDeprecatedFunction
360
	 */
361
	public static function isDefaultExpireDateEnforced() {
362
		$isDefaultExpireDateEnabled = \OC::$server->getConfig()->getAppValue('core', 'shareapi_default_expire_date', 'no');
363
		$enforceDefaultExpireDate = false;
364
		if ($isDefaultExpireDateEnabled === 'yes') {
365
			$value = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
366
			$enforceDefaultExpireDate = $value === 'yes';
367
		}
368
369
		return $enforceDefaultExpireDate;
370
	}
371
372
	/**
373
	 * Get the quota of a user
374
	 *
375
	 * @param string $userId
376
	 * @return float Quota bytes
377
	 */
378
	public static function getUserQuota($userId) {
379
		$user = \OC::$server->getUserManager()->get($userId);
380
		if (is_null($user)) {
381
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
382
		}
383
		$userQuota = $user->getQuota();
384
		if($userQuota === 'none') {
385
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
386
		}
387
		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 387 which is incompatible with the return type documented by OC_Util::getUserQuota of type double. It seems like you forgot to handle an error condition.
Loading history...
388
	}
389
390
	/**
391
	 * copies the skeleton to the users /files
392
	 *
393
	 * @param String $userId
394
	 * @param \OCP\Files\Folder $userDirectory
395
	 * @throws \RuntimeException
396
	 * @suppress PhanDeprecatedFunction
397
	 */
398
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
399
400
		$plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
401
		$userLang = \OC::$server->getL10NFactory()->findLanguage();
402
		$skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory);
403
404
		if (!file_exists($skeletonDirectory)) {
405
			$dialectStart = strpos($userLang, '_');
406
			if ($dialectStart !== false) {
407
				$skeletonDirectory = str_replace('{lang}', substr($userLang, 0, $dialectStart), $plainSkeletonDirectory);
408
			}
409
			if ($dialectStart === false || !file_exists($skeletonDirectory)) {
410
				$skeletonDirectory = str_replace('{lang}', 'default', $plainSkeletonDirectory);
411
			}
412
			if (!file_exists($skeletonDirectory)) {
413
				$skeletonDirectory = '';
414
			}
415
		}
416
417
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
418
419
		if ($instanceId === null) {
420
			throw new \RuntimeException('no instance id!');
421
		}
422
		$appdata = 'appdata_' . $instanceId;
423
		if ($userId === $appdata) {
424
			throw new \RuntimeException('username is reserved name: ' . $appdata);
425
		}
426
427
		if (!empty($skeletonDirectory)) {
428
			\OCP\Util::writeLog(
429
				'files_skeleton',
430
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
431
				ILogger::DEBUG
432
			);
433
			self::copyr($skeletonDirectory, $userDirectory);
434
			// update the file cache
435
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
436
		}
437
	}
438
439
	/**
440
	 * copies a directory recursively by using streams
441
	 *
442
	 * @param string $source
443
	 * @param \OCP\Files\Folder $target
444
	 * @return void
445
	 */
446
	public static function copyr($source, \OCP\Files\Folder $target) {
447
		$logger = \OC::$server->getLogger();
448
449
		// Verify if folder exists
450
		$dir = opendir($source);
451
		if($dir === false) {
452
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
453
			return;
454
		}
455
456
		// Copy the files
457
		while (false !== ($file = readdir($dir))) {
458
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
459
				if (is_dir($source . '/' . $file)) {
460
					$child = $target->newFolder($file);
461
					self::copyr($source . '/' . $file, $child);
462
				} else {
463
					$child = $target->newFile($file);
464
					$sourceStream = fopen($source . '/' . $file, 'r');
465
					if($sourceStream === false) {
466
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
467
						closedir($dir);
468
						return;
469
					}
470
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
471
				}
472
			}
473
		}
474
		closedir($dir);
475
	}
476
477
	/**
478
	 * @return void
479
	 * @suppress PhanUndeclaredMethod
480
	 */
481
	public static function tearDownFS() {
482
		\OC\Files\Filesystem::tearDown();
483
		\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...
484
		self::$fsSetup = false;
485
		self::$rootMounted = false;
486
	}
487
488
	/**
489
	 * get the current installed version of ownCloud
490
	 *
491
	 * @return array
492
	 */
493
	public static function getVersion() {
494
		OC_Util::loadVersion();
495
		return self::$versionCache['OC_Version'];
496
	}
497
498
	/**
499
	 * get the current installed version string of ownCloud
500
	 *
501
	 * @return string
502
	 */
503
	public static function getVersionString() {
504
		OC_Util::loadVersion();
505
		return self::$versionCache['OC_VersionString'];
506
	}
507
508
	/**
509
	 * @deprecated the value is of no use anymore
510
	 * @return string
511
	 */
512
	public static function getEditionString() {
513
		return '';
514
	}
515
516
	/**
517
	 * @description get the update channel of the current installed of ownCloud.
518
	 * @return string
519
	 */
520
	public static function getChannel() {
521
		OC_Util::loadVersion();
522
		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
523
	}
524
525
	/**
526
	 * @description get the build number of the current installed of ownCloud.
527
	 * @return string
528
	 */
529
	public static function getBuild() {
530
		OC_Util::loadVersion();
531
		return self::$versionCache['OC_Build'];
532
	}
533
534
	/**
535
	 * @description load the version.php into the session as cache
536
	 * @suppress PhanUndeclaredVariable
537
	 */
538
	private static function loadVersion() {
539
		if (self::$versionCache !== null) {
540
			return;
541
		}
542
543
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
544
		require OC::$SERVERROOT . '/version.php';
545
		/** @var $timestamp int */
546
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
547
		/** @var $OC_Version string */
548
		self::$versionCache['OC_Version'] = $OC_Version;
0 ignored issues
show
Bug introduced by
The variable $OC_Version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
549
		/** @var $OC_VersionString string */
550
		self::$versionCache['OC_VersionString'] = $OC_VersionString;
0 ignored issues
show
Bug introduced by
The variable $OC_VersionString does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
551
		/** @var $OC_Build string */
552
		self::$versionCache['OC_Build'] = $OC_Build;
0 ignored issues
show
Bug introduced by
The variable $OC_Build does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
553
554
		/** @var $OC_Channel string */
555
		self::$versionCache['OC_Channel'] = $OC_Channel;
0 ignored issues
show
Bug introduced by
The variable $OC_Channel does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
556
	}
557
558
	/**
559
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
560
	 *
561
	 * @param string $application application to get the files from
562
	 * @param string $directory directory within this application (css, js, vendor, etc)
563
	 * @param string $file the file inside of the above folder
564
	 * @return string the path
565
	 */
566
	private static function generatePath($application, $directory, $file) {
567
		if (is_null($file)) {
568
			$file = $application;
569
			$application = "";
570
		}
571
		if (!empty($application)) {
572
			return "$application/$directory/$file";
573
		} else {
574
			return "$directory/$file";
575
		}
576
	}
577
578
	/**
579
	 * add a javascript file
580
	 *
581
	 * @param string $application application id
582
	 * @param string|null $file filename
583
	 * @param bool $prepend prepend the Script to the beginning of the list
584
	 * @return void
585
	 */
586
	public static function addScript($application, $file = null, $prepend = false) {
587
		$path = OC_Util::generatePath($application, 'js', $file);
588
589
		// core js files need separate handling
590
		if ($application !== 'core' && $file !== null) {
591
			self::addTranslations ( $application );
592
		}
593
		self::addExternalResource($application, $prepend, $path, "script");
594
	}
595
596
	/**
597
	 * add a javascript file from the vendor sub folder
598
	 *
599
	 * @param string $application application id
600
	 * @param string|null $file filename
601
	 * @param bool $prepend prepend the Script to the beginning of the list
602
	 * @return void
603
	 */
604
	public static function addVendorScript($application, $file = null, $prepend = false) {
605
		$path = OC_Util::generatePath($application, 'vendor', $file);
606
		self::addExternalResource($application, $prepend, $path, "script");
607
	}
608
609
	/**
610
	 * add a translation JS file
611
	 *
612
	 * @param string $application application id
613
	 * @param string|null $languageCode language code, defaults to the current language
614
	 * @param bool|null $prepend prepend the Script to the beginning of the list
615
	 */
616
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
617
		if (is_null($languageCode)) {
618
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
619
		}
620
		if (!empty($application)) {
621
			$path = "$application/l10n/$languageCode";
622
		} else {
623
			$path = "l10n/$languageCode";
624
		}
625
		self::addExternalResource($application, $prepend, $path, "script");
0 ignored issues
show
Bug introduced by
It seems like $prepend defined by parameter $prepend on line 616 can also be of type null; however, OC_Util::addExternalResource() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

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