Completed
Pull Request — stable8.2 (#24656)
by Joas
12:22
created

OC_Util::checkServer()   F

Complexity

Conditions 42
Paths > 20000

Size

Total Lines 236
Code Lines 140

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 80
CRAP Score 320.0752
Metric Value
dl 0
loc 236
ccs 80
cts 174
cp 0.4598
rs 2
cc 42
eloc 140
nc 291842
nop 1
crap 320.0752

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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