Completed
Push — master ( 74e8c2...8931ba )
by Lukas
72:41
created

OC_Util::getDefaultPageUrl()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

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