Passed
Push — master ( 821a0d...8b22a4 )
by Robin
22:41 queued 07:27
created

OC_Util::isPublicLinkPasswordRequired()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
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 Bjoern Schiessle <[email protected]>
10
 * @author Björn Schießle <[email protected]>
11
 * @author Brice Maron <[email protected]>
12
 * @author Christopher Schäpers <[email protected]>
13
 * @author Christoph Wurst <[email protected]>
14
 * @author Clark Tomlinson <[email protected]>
15
 * @author cmeh <[email protected]>
16
 * @author Eric Masseran <[email protected]>
17
 * @author Felix Epp <[email protected]>
18
 * @author Florin Peter <[email protected]>
19
 * @author Frank Karlitschek <[email protected]>
20
 * @author Georg Ehrke <[email protected]>
21
 * @author helix84 <[email protected]>
22
 * @author Ilja Neumann <[email protected]>
23
 * @author Individual IT Services <[email protected]>
24
 * @author Jakob Sack <[email protected]>
25
 * @author Joas Schilling <[email protected]>
26
 * @author John Molakvoæ <[email protected]>
27
 * @author Jörn Friedrich Dreyer <[email protected]>
28
 * @author Julius Härtl <[email protected]>
29
 * @author Kawohl <[email protected]>
30
 * @author Lukas Reschke <[email protected]>
31
 * @author Markus Goetz <[email protected]>
32
 * @author Martin Mattel <[email protected]>
33
 * @author Marvin Thomas Rabe <[email protected]>
34
 * @author Michael Gapczynski <[email protected]>
35
 * @author Morris Jobke <[email protected]>
36
 * @author rakekniven <[email protected]>
37
 * @author Robert Dailey <[email protected]>
38
 * @author Robin Appelman <[email protected]>
39
 * @author Robin McCorkell <[email protected]>
40
 * @author Roeland Jago Douma <[email protected]>
41
 * @author Sebastian Wessalowski <[email protected]>
42
 * @author Stefan Rado <[email protected]>
43
 * @author Stefan Weil <[email protected]>
44
 * @author Thomas Müller <[email protected]>
45
 * @author Thomas Tanghus <[email protected]>
46
 * @author Valdnet <[email protected]>
47
 * @author Victor Dubiniuk <[email protected]>
48
 * @author Vincent Petry <[email protected]>
49
 * @author Volkan Gezer <[email protected]>
50
 *
51
 * @license AGPL-3.0
52
 *
53
 * This code is free software: you can redistribute it and/or modify
54
 * it under the terms of the GNU Affero General Public License, version 3,
55
 * as published by the Free Software Foundation.
56
 *
57
 * This program is distributed in the hope that it will be useful,
58
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
59
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60
 * GNU Affero General Public License for more details.
61
 *
62
 * You should have received a copy of the GNU Affero General Public License, version 3,
63
 * along with this program. If not, see <http://www.gnu.org/licenses/>
64
 *
65
 */
66
67
use bantu\IniGetWrapper\IniGetWrapper;
68
use OC\AppFramework\Http\Request;
69
use OCP\Files\Template\ITemplateManager;
70
use OCP\IConfig;
71
use OCP\IGroupManager;
72
use OCP\IURLGenerator;
73
use OCP\IUser;
74
use OCP\Share\IManager;
75
use Psr\Log\LoggerInterface;
76
77
class OC_Util {
78
	public static $scripts = [];
79
	public static $styles = [];
80
	public static $headers = [];
81
	private static $rootFsSetup = false;
82
	private static $fsSetup = false;
83
84
	/** @var array Local cache of version.php */
85
	private static $versionCache = null;
86
87
	protected static function getAppManager() {
88
		return \OC::$server->getAppManager();
89
	}
90
91
	/**
92
	 * Can be set up
93
	 *
94
	 * @param string $user
95
	 * @return boolean
96
	 * @description configure the initial filesystem based on the configuration
97
	 * @suppress PhanDeprecatedFunction
98
	 * @suppress PhanAccessMethodInternal
99
	 */
100
	public static function setupRootFS(string $user = '') {
101
		//setting up the filesystem twice can only lead to trouble
102
		if (self::$rootFsSetup) {
103
			return false;
104
		}
105
106
		\OC::$server->getEventLogger()->start('setup_root_fs', 'Setup root filesystem');
107
108
		// load all filesystem apps before, so no setup-hook gets lost
109
		OC_App::loadApps(['filesystem']);
110
111
		self::$rootFsSetup = true;
112
113
		\OC\Files\Filesystem::initMountManager();
114
115
		$prevLogging = \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
116
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
117
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
118
				/** @var \OC\Files\Storage\Common $storage */
119
				$storage->setMountOptions($mount->getOptions());
120
			}
121
			return $storage;
122
		});
123
124
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
125
			if (!$mount->getOption('enable_sharing', true)) {
126
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
127
					'storage' => $storage,
128
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
129
				]);
130
			}
131
			return $storage;
132
		});
133
134
		// install storage availability wrapper, before most other wrappers
135
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
136
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
137
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
138
			}
139
			return $storage;
140
		});
141
142
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
143
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
144
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
145
			}
146
			return $storage;
147
		});
148
149
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
150
			// set up quota for home storages, even for other users
151
			// which can happen when using sharing
152
153
			/**
154
			 * @var \OC\Files\Storage\Storage $storage
155
			 */
156
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
157
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
158
			) {
159
				/** @var \OC\Files\Storage\Home $storage */
160
				if (is_object($storage->getUser())) {
161
					$quota = OC_Util::getUserQuota($storage->getUser());
162
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
163
						return new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => $quota, 'root' => 'files']);
164
					}
165
				}
166
			}
167
168
			return $storage;
169
		});
170
171
		\OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
172
			/*
173
			 * Do not allow any operations that modify the storage
174
			 */
175
			if ($mount->getOption('readonly', false)) {
176
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
177
					'storage' => $storage,
178
					'mask' => \OCP\Constants::PERMISSION_ALL & ~(
179
							\OCP\Constants::PERMISSION_UPDATE |
180
							\OCP\Constants::PERMISSION_CREATE |
181
							\OCP\Constants::PERMISSION_DELETE
182
						),
183
				]);
184
			}
185
			return $storage;
186
		});
187
188
		OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user]);
189
190
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
191
192
		/** @var \OCP\Files\Config\IMountProviderCollection $mountProviderCollection */
193
		$mountProviderCollection = \OC::$server->query(\OCP\Files\Config\IMountProviderCollection::class);
194
		$rootMountProviders = $mountProviderCollection->getRootMounts();
195
196
		/** @var \OC\Files\Mount\Manager $mountManager */
197
		$mountManager = \OC\Files\Filesystem::getMountManager();
198
		foreach ($rootMountProviders as $rootMountProvider) {
199
			$mountManager->addMount($rootMountProvider);
200
		}
201
202
		\OC::$server->getEventLogger()->end('setup_root_fs');
203
204
		return true;
205
	}
206
207
	/**
208
	 * Setup the file system
209
	 *
210
	 * @param string|null $user
211
	 * @return boolean
212
	 * @description configure the initial filesystem based on the configuration
213
	 * @suppress PhanDeprecatedFunction
214
	 * @suppress PhanAccessMethodInternal
215
	 */
216
	public static function setupFS(?string $user = '') {
217
		self::setupRootFS($user ?? '');
218
219
		if (self::$fsSetup) {
220
			return false;
221
		}
222
223
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
224
225
		// If we are not forced to load a specific user we load the one that is logged in
226
		if ($user === '') {
227
			$userObject = \OC::$server->get(\OCP\IUserSession::class)->getUser();
228
		} else {
229
			$userObject = \OC::$server->get(\OCP\IUserManager::class)->get($user);
230
		}
231
232
		//if we aren't logged in, or the user doesn't exist, there is no use to set up the filesystem
233
		if ($userObject) {
234
			self::$fsSetup = true;
235
236
			$userDir = '/' . $userObject->getUID() . '/files';
237
238
			//jail the user into his "home" directory
239
			\OC\Files\Filesystem::init($userObject, $userDir);
240
241
			OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $userObject->getUID(), 'user_dir' => $userDir]);
242
		}
243
		\OC::$server->getEventLogger()->end('setup_fs');
244
		return true;
245
	}
246
247
	/**
248
	 * check if a password is required for each public link
249
	 *
250
	 * @return boolean
251
	 * @suppress PhanDeprecatedFunction
252
	 */
253
	public static function isPublicLinkPasswordRequired() {
254
		/** @var IManager $shareManager */
255
		$shareManager = \OC::$server->get(IManager::class);
256
		return $shareManager->shareApiLinkEnforcePassword();
257
	}
258
259
	/**
260
	 * check if sharing is disabled for the current user
261
	 * @param IConfig $config
262
	 * @param IGroupManager $groupManager
263
	 * @param IUser|null $user
264
	 * @return bool
265
	 */
266
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
0 ignored issues
show
Unused Code introduced by
The parameter $groupManager is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

266
	public static function isSharingDisabledForUser(IConfig $config, /** @scrutinizer ignore-unused */ IGroupManager $groupManager, $user) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $config is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

266
	public static function isSharingDisabledForUser(/** @scrutinizer ignore-unused */ IConfig $config, IGroupManager $groupManager, $user) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
267
		/** @var IManager $shareManager */
268
		$shareManager = \OC::$server->get(IManager::class);
269
		$userId = $user ? $user->getUID() : null;
270
		return $shareManager->sharingDisabledForUser($userId);
271
	}
272
273
	/**
274
	 * check if share API enforces a default expire date
275
	 *
276
	 * @return boolean
277
	 * @suppress PhanDeprecatedFunction
278
	 */
279
	public static function isDefaultExpireDateEnforced() {
280
		/** @var IManager $shareManager */
281
		$shareManager = \OC::$server->get(IManager::class);
282
		return $shareManager->shareApiLinkDefaultExpireDateEnforced();
283
	}
284
285
	/**
286
	 * Get the quota of a user
287
	 *
288
	 * @param IUser|null $user
289
	 * @return float Quota bytes
290
	 */
291
	public static function getUserQuota(?IUser $user) {
292
		if (is_null($user)) {
293
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
294
		}
295
		$userQuota = $user->getQuota();
296
		if ($userQuota === 'none') {
297
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
298
		}
299
		return OC_Helper::computerFileSize($userQuota);
0 ignored issues
show
Bug Best Practice introduced by
The expression return OC_Helper::computerFileSize($userQuota) could also return false which is incompatible with the documented return type double. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
300
	}
301
302
	/**
303
	 * copies the skeleton to the users /files
304
	 *
305
	 * @param string $userId
306
	 * @param \OCP\Files\Folder $userDirectory
307
	 * @throws \OCP\Files\NotFoundException
308
	 * @throws \OCP\Files\NotPermittedException
309
	 * @suppress PhanDeprecatedFunction
310
	 */
311
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
312
		/** @var LoggerInterface $logger */
313
		$logger = \OC::$server->get(LoggerInterface::class);
314
315
		$plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
316
		$userLang = \OC::$server->getL10NFactory()->findLanguage();
317
		$skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory);
318
319
		if (!file_exists($skeletonDirectory)) {
320
			$dialectStart = strpos($userLang, '_');
321
			if ($dialectStart !== false) {
322
				$skeletonDirectory = str_replace('{lang}', substr($userLang, 0, $dialectStart), $plainSkeletonDirectory);
323
			}
324
			if ($dialectStart === false || !file_exists($skeletonDirectory)) {
325
				$skeletonDirectory = str_replace('{lang}', 'default', $plainSkeletonDirectory);
326
			}
327
			if (!file_exists($skeletonDirectory)) {
328
				$skeletonDirectory = '';
329
			}
330
		}
331
332
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
333
334
		if ($instanceId === null) {
335
			throw new \RuntimeException('no instance id!');
336
		}
337
		$appdata = 'appdata_' . $instanceId;
338
		if ($userId === $appdata) {
339
			throw new \RuntimeException('username is reserved name: ' . $appdata);
340
		}
341
342
		if (!empty($skeletonDirectory)) {
343
			$logger->debug('copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'), ['app' => 'files_skeleton']);
344
			self::copyr($skeletonDirectory, $userDirectory);
345
			// update the file cache
346
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
347
348
			/** @var ITemplateManager $templateManager */
349
			$templateManager = \OC::$server->get(ITemplateManager::class);
350
			$templateManager->initializeTemplateDirectory(null, $userId);
351
		}
352
	}
353
354
	/**
355
	 * copies a directory recursively by using streams
356
	 *
357
	 * @param string $source
358
	 * @param \OCP\Files\Folder $target
359
	 * @return void
360
	 */
361
	public static function copyr($source, \OCP\Files\Folder $target) {
362
		$logger = \OC::$server->getLogger();
363
364
		// Verify if folder exists
365
		$dir = opendir($source);
366
		if ($dir === false) {
367
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
368
			return;
369
		}
370
371
		// Copy the files
372
		while (false !== ($file = readdir($dir))) {
373
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
374
				if (is_dir($source . '/' . $file)) {
375
					$child = $target->newFolder($file);
376
					self::copyr($source . '/' . $file, $child);
377
				} else {
378
					$child = $target->newFile($file);
379
					$sourceStream = fopen($source . '/' . $file, 'r');
380
					if ($sourceStream === false) {
381
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
382
						closedir($dir);
383
						return;
384
					}
385
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
386
				}
387
			}
388
		}
389
		closedir($dir);
390
	}
391
392
	/**
393
	 * @return void
394
	 * @suppress PhanUndeclaredMethod
395
	 */
396
	public static function tearDownFS() {
397
		\OC\Files\Filesystem::tearDown();
398
		\OC::$server->getRootFolder()->clearCache();
0 ignored issues
show
Bug introduced by
The method clearCache() does not exist on OCP\Files\IRootFolder. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\Files\IRootFolder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

398
		\OC::$server->getRootFolder()->/** @scrutinizer ignore-call */ clearCache();
Loading history...
399
		self::$fsSetup = false;
400
		self::$rootFsSetup = false;
401
	}
402
403
	/**
404
	 * get the current installed version of ownCloud
405
	 *
406
	 * @return array
407
	 */
408
	public static function getVersion() {
409
		OC_Util::loadVersion();
410
		return self::$versionCache['OC_Version'];
411
	}
412
413
	/**
414
	 * get the current installed version string of ownCloud
415
	 *
416
	 * @return string
417
	 */
418
	public static function getVersionString() {
419
		OC_Util::loadVersion();
420
		return self::$versionCache['OC_VersionString'];
421
	}
422
423
	/**
424
	 * @deprecated the value is of no use anymore
425
	 * @return string
426
	 */
427
	public static function getEditionString() {
428
		return '';
429
	}
430
431
	/**
432
	 * @description get the update channel of the current installed of ownCloud.
433
	 * @return string
434
	 */
435
	public static function getChannel() {
436
		OC_Util::loadVersion();
437
		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
438
	}
439
440
	/**
441
	 * @description get the build number of the current installed of ownCloud.
442
	 * @return string
443
	 */
444
	public static function getBuild() {
445
		OC_Util::loadVersion();
446
		return self::$versionCache['OC_Build'];
447
	}
448
449
	/**
450
	 * @description load the version.php into the session as cache
451
	 * @suppress PhanUndeclaredVariable
452
	 */
453
	private static function loadVersion() {
454
		if (self::$versionCache !== null) {
0 ignored issues
show
introduced by
The condition self::versionCache !== null is always true.
Loading history...
455
			return;
456
		}
457
458
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
459
		require OC::$SERVERROOT . '/version.php';
460
		/** @var int $timestamp */
461
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
462
		/** @var string $OC_Version */
463
		self::$versionCache['OC_Version'] = $OC_Version;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $OC_Version seems to be never defined.
Loading history...
464
		/** @var string $OC_VersionString */
465
		self::$versionCache['OC_VersionString'] = $OC_VersionString;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $OC_VersionString seems to be never defined.
Loading history...
466
		/** @var string $OC_Build */
467
		self::$versionCache['OC_Build'] = $OC_Build;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $OC_Build seems to be never defined.
Loading history...
468
469
		/** @var string $OC_Channel */
470
		self::$versionCache['OC_Channel'] = $OC_Channel;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $OC_Channel seems to be never defined.
Loading history...
471
	}
472
473
	/**
474
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
475
	 *
476
	 * @param string $application application to get the files from
477
	 * @param string $directory directory within this application (css, js, vendor, etc)
478
	 * @param string $file the file inside of the above folder
479
	 * @return string the path
480
	 */
481
	private static function generatePath($application, $directory, $file) {
482
		if (is_null($file)) {
0 ignored issues
show
introduced by
The condition is_null($file) is always false.
Loading history...
483
			$file = $application;
484
			$application = "";
485
		}
486
		if (!empty($application)) {
487
			return "$application/$directory/$file";
488
		} else {
489
			return "$directory/$file";
490
		}
491
	}
492
493
	/**
494
	 * add a javascript file
495
	 *
496
	 * @deprecated 24.0.0 - Use \OCP\Util::addScript
497
	 *
498
	 * @param string $application application id
499
	 * @param string|null $file filename
500
	 * @param bool $prepend prepend the Script to the beginning of the list
501
	 * @return void
502
	 */
503
	public static function addScript($application, $file = null, $prepend = false) {
504
		$path = OC_Util::generatePath($application, 'js', $file);
505
506
		// core js files need separate handling
507
		if ($application !== 'core' && $file !== null) {
508
			self::addTranslations($application);
509
		}
510
		self::addExternalResource($application, $prepend, $path, "script");
511
	}
512
513
	/**
514
	 * add a javascript file from the vendor sub folder
515
	 *
516
	 * @param string $application application id
517
	 * @param string|null $file filename
518
	 * @param bool $prepend prepend the Script to the beginning of the list
519
	 * @return void
520
	 */
521
	public static function addVendorScript($application, $file = null, $prepend = false) {
522
		$path = OC_Util::generatePath($application, 'vendor', $file);
523
		self::addExternalResource($application, $prepend, $path, "script");
524
	}
525
526
	/**
527
	 * add a translation JS file
528
	 *
529
	 * @deprecated 24.0.0
530
	 *
531
	 * @param string $application application id
532
	 * @param string|null $languageCode language code, defaults to the current language
533
	 * @param bool|null $prepend prepend the Script to the beginning of the list
534
	 */
535
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
536
		if (is_null($languageCode)) {
537
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
538
		}
539
		if (!empty($application)) {
540
			$path = "$application/l10n/$languageCode";
541
		} else {
542
			$path = "l10n/$languageCode";
543
		}
544
		self::addExternalResource($application, $prepend, $path, "script");
545
	}
546
547
	/**
548
	 * add a css file
549
	 *
550
	 * @param string $application application id
551
	 * @param string|null $file filename
552
	 * @param bool $prepend prepend the Style to the beginning of the list
553
	 * @return void
554
	 */
555
	public static function addStyle($application, $file = null, $prepend = false) {
556
		$path = OC_Util::generatePath($application, 'css', $file);
557
		self::addExternalResource($application, $prepend, $path, "style");
558
	}
559
560
	/**
561
	 * add a css file from the vendor sub folder
562
	 *
563
	 * @param string $application application id
564
	 * @param string|null $file filename
565
	 * @param bool $prepend prepend the Style to the beginning of the list
566
	 * @return void
567
	 */
568
	public static function addVendorStyle($application, $file = null, $prepend = false) {
569
		$path = OC_Util::generatePath($application, 'vendor', $file);
570
		self::addExternalResource($application, $prepend, $path, "style");
571
	}
572
573
	/**
574
	 * add an external resource css/js file
575
	 *
576
	 * @param string $application application id
577
	 * @param bool $prepend prepend the file to the beginning of the list
578
	 * @param string $path
579
	 * @param string $type (script or style)
580
	 * @return void
581
	 */
582
	private static function addExternalResource($application, $prepend, $path, $type = "script") {
0 ignored issues
show
Unused Code introduced by
The parameter $application is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

582
	private static function addExternalResource(/** @scrutinizer ignore-unused */ $application, $prepend, $path, $type = "script") {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
583
		if ($type === "style") {
584
			if (!in_array($path, self::$styles)) {
585
				if ($prepend === true) {
586
					array_unshift(self::$styles, $path);
587
				} else {
588
					self::$styles[] = $path;
589
				}
590
			}
591
		} elseif ($type === "script") {
592
			if (!in_array($path, self::$scripts)) {
593
				if ($prepend === true) {
594
					array_unshift(self::$scripts, $path);
595
				} else {
596
					self::$scripts [] = $path;
597
				}
598
			}
599
		}
600
	}
601
602
	/**
603
	 * Add a custom element to the header
604
	 * If $text is null then the element will be written as empty element.
605
	 * So use "" to get a closing tag.
606
	 * @param string $tag tag name of the element
607
	 * @param array $attributes array of attributes for the element
608
	 * @param string $text the text content for the element
609
	 * @param bool $prepend prepend the header to the beginning of the list
610
	 */
611
	public static function addHeader($tag, $attributes, $text = null, $prepend = false) {
612
		$header = [
613
			'tag' => $tag,
614
			'attributes' => $attributes,
615
			'text' => $text
616
		];
617
		if ($prepend === true) {
618
			array_unshift(self::$headers, $header);
619
		} else {
620
			self::$headers[] = $header;
621
		}
622
	}
623
624
	/**
625
	 * check if the current server configuration is suitable for ownCloud
626
	 *
627
	 * @param \OC\SystemConfig $config
628
	 * @return array arrays with error messages and hints
629
	 */
630
	public static function checkServer(\OC\SystemConfig $config) {
631
		$l = \OC::$server->getL10N('lib');
632
		$errors = [];
633
		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
634
635
		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
636
			// this check needs to be done every time
637
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
638
		}
639
640
		// Assume that if checkServer() succeeded before in this session, then all is fine.
641
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
642
			return $errors;
643
		}
644
645
		$webServerRestart = false;
646
		$setup = new \OC\Setup(
647
			$config,
648
			\OC::$server->get(IniGetWrapper::class),
649
			\OC::$server->getL10N('lib'),
650
			\OC::$server->get(\OCP\Defaults::class),
651
			\OC::$server->get(LoggerInterface::class),
652
			\OC::$server->getSecureRandom(),
653
			\OC::$server->get(\OC\Installer::class)
654
		);
655
656
		$urlGenerator = \OC::$server->getURLGenerator();
657
658
		$availableDatabases = $setup->getSupportedDatabases();
659
		if (empty($availableDatabases)) {
660
			$errors[] = [
661
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
662
				'hint' => '' //TODO: sane hint
663
			];
664
			$webServerRestart = true;
665
		}
666
667
		// Check if config folder is writable.
668
		if (!OC_Helper::isReadOnlyConfigEnabled()) {
669
			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
670
				$errors[] = [
671
					'error' => $l->t('Cannot write into "config" directory.'),
672
					'hint' => $l->t('This can usually be fixed by giving the web server write access to the config directory. See %s',
673
						[ $urlGenerator->linkToDocs('admin-dir_permissions') ]) . '. '
674
						. $l->t('Or, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it. See %s',
675
						[ $urlGenerator->linkToDocs('admin-config') ])
676
				];
677
			}
678
		}
679
680
		// Check if there is a writable install folder.
681
		if ($config->getValue('appstoreenabled', true)) {
682
			if (OC_App::getInstallPath() === null
683
				|| !is_writable(OC_App::getInstallPath())
684
				|| !is_readable(OC_App::getInstallPath())
685
			) {
686
				$errors[] = [
687
					'error' => $l->t('Cannot write into "apps" directory.'),
688
					'hint' => $l->t('This can usually be fixed by giving the web server write access to the apps directory'
689
						. ' or disabling the App Store in the config file.')
690
				];
691
			}
692
		}
693
		// Create root dir.
694
		if ($config->getValue('installed', false)) {
695
			if (!is_dir($CONFIG_DATADIRECTORY)) {
696
				$success = @mkdir($CONFIG_DATADIRECTORY);
697
				if ($success) {
698
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
699
				} else {
700
					$errors[] = [
701
						'error' => $l->t('Cannot create "data" directory.'),
702
						'hint' => $l->t('This can usually be fixed by giving the web server write access to the root directory. See %s',
703
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
704
					];
705
				}
706
			} elseif (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
707
				// is_writable doesn't work for NFS mounts, so try to write a file and check if it exists.
708
				$testFile = sprintf('%s/%s.tmp', $CONFIG_DATADIRECTORY, uniqid('data_dir_writability_test_'));
709
				$handle = fopen($testFile, 'w');
710
				if (!$handle || fwrite($handle, 'Test write operation') === false) {
0 ignored issues
show
introduced by
$handle is of type resource, thus it always evaluated to false.
Loading history...
711
					$permissionsHint = $l->t('Permissions can usually be fixed by giving the web server write access to the root directory. See %s.',
712
						[$urlGenerator->linkToDocs('admin-dir_permissions')]);
713
					$errors[] = [
714
						'error' => $l->t('Your data directory is not writable.'),
715
						'hint' => $permissionsHint
716
					];
717
				} else {
718
					fclose($handle);
719
					unlink($testFile);
720
				}
721
			} else {
722
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
723
			}
724
		}
725
726
		if (!OC_Util::isSetLocaleWorking()) {
727
			$errors[] = [
728
				'error' => $l->t('Setting locale to %s failed.',
729
					['en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
730
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8']),
731
				'hint' => $l->t('Please install one of these locales on your system and restart your web server.')
732
			];
733
		}
734
735
		// Contains the dependencies that should be checked against
736
		// classes = class_exists
737
		// functions = function_exists
738
		// defined = defined
739
		// ini = ini_get
740
		// If the dependency is not found the missing module name is shown to the EndUser
741
		// When adding new checks always verify that they pass on Travis as well
742
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
743
		$dependencies = [
744
			'classes' => [
745
				'ZipArchive' => 'zip',
746
				'DOMDocument' => 'dom',
747
				'XMLWriter' => 'XMLWriter',
748
				'XMLReader' => 'XMLReader',
749
			],
750
			'functions' => [
751
				'xml_parser_create' => 'libxml',
752
				'mb_strcut' => 'mbstring',
753
				'ctype_digit' => 'ctype',
754
				'json_encode' => 'JSON',
755
				'gd_info' => 'GD',
756
				'gzencode' => 'zlib',
757
				'simplexml_load_string' => 'SimpleXML',
758
				'hash' => 'HASH Message Digest Framework',
759
				'curl_init' => 'cURL',
760
				'openssl_verify' => 'OpenSSL',
761
			],
762
			'defined' => [
763
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
764
			],
765
			'ini' => [
766
				'default_charset' => 'UTF-8',
767
			],
768
		];
769
		$missingDependencies = [];
770
		$invalidIniSettings = [];
771
772
		$iniWrapper = \OC::$server->get(IniGetWrapper::class);
773
		foreach ($dependencies['classes'] as $class => $module) {
774
			if (!class_exists($class)) {
775
				$missingDependencies[] = $module;
776
			}
777
		}
778
		foreach ($dependencies['functions'] as $function => $module) {
779
			if (!function_exists($function)) {
780
				$missingDependencies[] = $module;
781
			}
782
		}
783
		foreach ($dependencies['defined'] as $defined => $module) {
784
			if (!defined($defined)) {
785
				$missingDependencies[] = $module;
786
			}
787
		}
788
		foreach ($dependencies['ini'] as $setting => $expected) {
789
			if (is_bool($expected)) {
790
				if ($iniWrapper->getBool($setting) !== $expected) {
791
					$invalidIniSettings[] = [$setting, $expected];
792
				}
793
			}
794
			if (is_int($expected)) {
795
				if ($iniWrapper->getNumeric($setting) !== $expected) {
796
					$invalidIniSettings[] = [$setting, $expected];
797
				}
798
			}
799
			if (is_string($expected)) {
800
				if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
801
					$invalidIniSettings[] = [$setting, $expected];
802
				}
803
			}
804
		}
805
806
		foreach ($missingDependencies as $missingDependency) {
807
			$errors[] = [
808
				'error' => $l->t('PHP module %s not installed.', [$missingDependency]),
809
				'hint' => $l->t('Please ask your server administrator to install the module.'),
810
			];
811
			$webServerRestart = true;
812
		}
813
		foreach ($invalidIniSettings as $setting) {
814
			if (is_bool($setting[1])) {
815
				$setting[1] = $setting[1] ? 'on' : 'off';
816
			}
817
			$errors[] = [
818
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
819
				'hint' => $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
820
			];
821
			$webServerRestart = true;
822
		}
823
824
		/**
825
		 * The mbstring.func_overload check can only be performed if the mbstring
826
		 * module is installed as it will return null if the checking setting is
827
		 * not available and thus a check on the boolean value fails.
828
		 *
829
		 * TODO: Should probably be implemented in the above generic dependency
830
		 *       check somehow in the long-term.
831
		 */
832
		if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
833
			$iniWrapper->getBool('mbstring.func_overload') === true) {
834
			$errors[] = [
835
				'error' => $l->t('<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>.', [$iniWrapper->getString('mbstring.func_overload')]),
836
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini.')
837
			];
838
		}
839
840
		if (function_exists('xml_parser_create') &&
841
			LIBXML_LOADED_VERSION < 20700) {
842
			$version = LIBXML_LOADED_VERSION;
843
			$major = floor($version / 10000);
844
			$version -= ($major * 10000);
845
			$minor = floor($version / 100);
846
			$version -= ($minor * 100);
847
			$patch = $version;
848
			$errors[] = [
849
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
850
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
851
			];
852
		}
853
854
		if (!self::isAnnotationsWorking()) {
855
			$errors[] = [
856
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
857
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
858
			];
859
		}
860
861
		if (!\OC::$CLI && $webServerRestart) {
862
			$errors[] = [
863
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
864
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
865
			];
866
		}
867
868
		$errors = array_merge($errors, self::checkDatabaseVersion());
869
870
		// Cache the result of this function
871
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
872
873
		return $errors;
874
	}
875
876
	/**
877
	 * Check the database version
878
	 *
879
	 * @return array errors array
880
	 */
881
	public static function checkDatabaseVersion() {
882
		$l = \OC::$server->getL10N('lib');
883
		$errors = [];
884
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
885
		if ($dbType === 'pgsql') {
886
			// check PostgreSQL version
887
			try {
888
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
889
				$data = $result->fetchRow();
0 ignored issues
show
Deprecated Code introduced by
The function OC_DB_StatementWrapper::fetchRow() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

889
				$data = /** @scrutinizer ignore-deprecated */ $result->fetchRow();
Loading history...
890
				$result->closeCursor();
891
				if (isset($data['server_version'])) {
892
					$version = $data['server_version'];
893
					if (version_compare($version, '9.0.0', '<')) {
894
						$errors[] = [
895
							'error' => $l->t('PostgreSQL >= 9 required.'),
896
							'hint' => $l->t('Please upgrade your database version.')
897
						];
898
					}
899
				}
900
			} catch (\Doctrine\DBAL\Exception $e) {
901
				$logger = \OC::$server->getLogger();
902
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
903
				$logger->logException($e);
904
			}
905
		}
906
		return $errors;
907
	}
908
909
	/**
910
	 * Check for correct file permissions of data directory
911
	 *
912
	 * @param string $dataDirectory
913
	 * @return array arrays with error messages and hints
914
	 */
915
	public static function checkDataDirectoryPermissions($dataDirectory) {
916
		if (\OC::$server->getConfig()->getSystemValue('check_data_directory_permissions', true) === false) {
917
			return  [];
918
		}
919
920
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
921
		if (substr($perms, -1) !== '0') {
922
			chmod($dataDirectory, 0770);
923
			clearstatcache();
924
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
925
			if ($perms[2] !== '0') {
926
				$l = \OC::$server->getL10N('lib');
927
				return [[
928
					'error' => $l->t('Your data directory is readable by other users.'),
929
					'hint' => $l->t('Please change the permissions to 0770 so that the directory cannot be listed by other users.'),
930
				]];
931
			}
932
		}
933
		return [];
934
	}
935
936
	/**
937
	 * Check that the data directory exists and is valid by
938
	 * checking the existence of the ".ocdata" file.
939
	 *
940
	 * @param string $dataDirectory data directory path
941
	 * @return array errors found
942
	 */
943
	public static function checkDataDirectoryValidity($dataDirectory) {
944
		$l = \OC::$server->getL10N('lib');
945
		$errors = [];
946
		if ($dataDirectory[0] !== '/') {
947
			$errors[] = [
948
				'error' => $l->t('Your data directory must be an absolute path.'),
949
				'hint' => $l->t('Check the value of "datadirectory" in your configuration.')
950
			];
951
		}
952
		if (!file_exists($dataDirectory . '/.ocdata')) {
953
			$errors[] = [
954
				'error' => $l->t('Your data directory is invalid.'),
955
				'hint' => $l->t('Ensure there is a file called ".ocdata"' .
956
					' in the root of the data directory.')
957
			];
958
		}
959
		return $errors;
960
	}
961
962
	/**
963
	 * Check if the user is logged in, redirects to home if not. With
964
	 * redirect URL parameter to the request URI.
965
	 *
966
	 * @return void
967
	 */
968
	public static function checkLoggedIn() {
969
		// Check if we are a user
970
		if (!\OC::$server->getUserSession()->isLoggedIn()) {
971
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
972
						'core.login.showLoginForm',
973
						[
974
							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
975
						]
976
					)
977
			);
978
			exit();
979
		}
980
		// Redirect to 2FA challenge selection if 2FA challenge was not solved yet
981
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
982
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
983
			exit();
984
		}
985
	}
986
987
	/**
988
	 * Check if the user is a admin, redirects to home if not
989
	 *
990
	 * @return void
991
	 */
992
	public static function checkAdminUser() {
993
		OC_Util::checkLoggedIn();
994
		if (!OC_User::isAdminUser(OC_User::getUser())) {
995
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
996
			exit();
997
		}
998
	}
999
1000
	/**
1001
	 * Returns the URL of the default page
1002
	 * based on the system configuration and
1003
	 * the apps visible for the current user
1004
	 *
1005
	 * @return string URL
1006
	 * @suppress PhanDeprecatedFunction
1007
	 */
1008
	public static function getDefaultPageUrl() {
1009
		/** @var IURLGenerator $urlGenerator */
1010
		$urlGenerator = \OC::$server->get(IURLGenerator::class);
1011
		return $urlGenerator->linkToDefaultPageUrl();
1012
	}
1013
1014
	/**
1015
	 * Redirect to the user default page
1016
	 *
1017
	 * @return void
1018
	 */
1019
	public static function redirectToDefaultPage() {
1020
		$location = self::getDefaultPageUrl();
1021
		header('Location: ' . $location);
1022
		exit();
1023
	}
1024
1025
	/**
1026
	 * get an id unique for this instance
1027
	 *
1028
	 * @return string
1029
	 */
1030
	public static function getInstanceId() {
1031
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1032
		if (is_null($id)) {
1033
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1034
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1035
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1036
		}
1037
		return $id;
1038
	}
1039
1040
	/**
1041
	 * Public function to sanitize HTML
1042
	 *
1043
	 * This function is used to sanitize HTML and should be applied on any
1044
	 * string or array of strings before displaying it on a web page.
1045
	 *
1046
	 * @param string|string[] $value
1047
	 * @return string|string[] an array of sanitized strings or a single sanitized string, depends on the input parameter.
1048
	 */
1049
	public static function sanitizeHTML($value) {
1050
		if (is_array($value)) {
1051
			/** @var string[] $value */
1052
			$value = array_map(function ($value) {
1053
				return self::sanitizeHTML($value);
1054
			}, $value);
1055
		} else {
1056
			// Specify encoding for PHP<5.4
1057
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1058
		}
1059
		return $value;
1060
	}
1061
1062
	/**
1063
	 * Public function to encode url parameters
1064
	 *
1065
	 * This function is used to encode path to file before output.
1066
	 * Encoding is done according to RFC 3986 with one exception:
1067
	 * Character '/' is preserved as is.
1068
	 *
1069
	 * @param string $component part of URI to encode
1070
	 * @return string
1071
	 */
1072
	public static function encodePath($component) {
1073
		$encoded = rawurlencode($component);
1074
		$encoded = str_replace('%2F', '/', $encoded);
1075
		return $encoded;
1076
	}
1077
1078
1079
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1080
		// php dev server does not support htaccess
1081
		if (php_sapi_name() === 'cli-server') {
1082
			return false;
1083
		}
1084
1085
		// testdata
1086
		$fileName = '/htaccesstest.txt';
1087
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1088
1089
		// creating a test file
1090
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1091
1092
		if (file_exists($testFile)) {// already running this test, possible recursive call
1093
			return false;
1094
		}
1095
1096
		$fp = @fopen($testFile, 'w');
1097
		if (!$fp) {
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1098
			throw new \OCP\HintException('Can\'t create test file to check for working .htaccess file.',
1099
				'Make sure it is possible for the web server to write to ' . $testFile);
1100
		}
1101
		fwrite($fp, $testContent);
1102
		fclose($fp);
1103
1104
		return $testContent;
1105
	}
1106
1107
	/**
1108
	 * Check if the .htaccess file is working
1109
	 *
1110
	 * @param \OCP\IConfig $config
1111
	 * @return bool
1112
	 * @throws Exception
1113
	 * @throws \OCP\HintException If the test file can't get written.
1114
	 */
1115
	public function isHtaccessWorking(\OCP\IConfig $config) {
1116
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1117
			return true;
1118
		}
1119
1120
		$testContent = $this->createHtaccessTestFile($config);
1121
		if ($testContent === false) {
0 ignored issues
show
introduced by
The condition $testContent === false is always true.
Loading history...
1122
			return false;
1123
		}
1124
1125
		$fileName = '/htaccesstest.txt';
1126
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1127
1128
		// accessing the file via http
1129
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1130
		try {
1131
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1132
		} catch (\Exception $e) {
1133
			$content = false;
1134
		}
1135
1136
		if (strpos($url, 'https:') === 0) {
1137
			$url = 'http:' . substr($url, 6);
1138
		} else {
1139
			$url = 'https:' . substr($url, 5);
1140
		}
1141
1142
		try {
1143
			$fallbackContent = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1144
		} catch (\Exception $e) {
1145
			$fallbackContent = false;
1146
		}
1147
1148
		// cleanup
1149
		@unlink($testFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1149
		/** @scrutinizer ignore-unhandled */ @unlink($testFile);

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...
1150
1151
		/*
1152
		 * If the content is not equal to test content our .htaccess
1153
		 * is working as required
1154
		 */
1155
		return $content !== $testContent && $fallbackContent !== $testContent;
1156
	}
1157
1158
	/**
1159
	 * Check if current locale is non-UTF8
1160
	 *
1161
	 * @return bool
1162
	 */
1163
	private static function isNonUTF8Locale() {
1164
		if (function_exists('escapeshellcmd')) {
1165
			return '' === escapeshellcmd('§');
1166
		} elseif (function_exists('escapeshellarg')) {
1167
			return '\'\'' === escapeshellarg('§');
1168
		} else {
1169
			return 0 === preg_match('/utf-?8/i', setlocale(LC_CTYPE, 0));
1170
		}
1171
	}
1172
1173
	/**
1174
	 * Check if the setlocale call does not work. This can happen if the right
1175
	 * local packages are not available on the server.
1176
	 *
1177
	 * @return bool
1178
	 */
1179
	public static function isSetLocaleWorking() {
1180
		if (self::isNonUTF8Locale()) {
1181
			// Borrowed from \Patchwork\Utf8\Bootup::initLocale
1182
			setlocale(LC_ALL, 'C.UTF-8', 'C');
1183
			setlocale(LC_CTYPE, 'en_US.UTF-8', 'fr_FR.UTF-8', 'es_ES.UTF-8', 'de_DE.UTF-8', 'ru_RU.UTF-8', 'pt_BR.UTF-8', 'it_IT.UTF-8', 'ja_JP.UTF-8', 'zh_CN.UTF-8', '0');
1184
1185
			// Check again
1186
			if (self::isNonUTF8Locale()) {
1187
				return false;
1188
			}
1189
		}
1190
1191
		return true;
1192
	}
1193
1194
	/**
1195
	 * Check if it's possible to get the inline annotations
1196
	 *
1197
	 * @return bool
1198
	 */
1199
	public static function isAnnotationsWorking() {
1200
		$reflection = new \ReflectionMethod(__METHOD__);
1201
		$docs = $reflection->getDocComment();
1202
1203
		return (is_string($docs) && strlen($docs) > 50);
1204
	}
1205
1206
	/**
1207
	 * Check if the PHP module fileinfo is loaded.
1208
	 *
1209
	 * @return bool
1210
	 */
1211
	public static function fileInfoLoaded() {
1212
		return function_exists('finfo_open');
1213
	}
1214
1215
	/**
1216
	 * clear all levels of output buffering
1217
	 *
1218
	 * @return void
1219
	 */
1220
	public static function obEnd() {
1221
		while (ob_get_level()) {
1222
			ob_end_clean();
1223
		}
1224
	}
1225
1226
	/**
1227
	 * Checks whether the server is running on Mac OS X
1228
	 *
1229
	 * @return bool true if running on Mac OS X, false otherwise
1230
	 */
1231
	public static function runningOnMac() {
1232
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1233
	}
1234
1235
	/**
1236
	 * Handles the case that there may not be a theme, then check if a "default"
1237
	 * theme exists and take that one
1238
	 *
1239
	 * @return string the theme
1240
	 */
1241
	public static function getTheme() {
1242
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1243
1244
		if ($theme === '') {
1245
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1246
				$theme = 'default';
1247
			}
1248
		}
1249
1250
		return $theme;
1251
	}
1252
1253
	/**
1254
	 * Normalize a unicode string
1255
	 *
1256
	 * @param string $value a not normalized string
1257
	 * @return bool|string
1258
	 */
1259
	public static function normalizeUnicode($value) {
1260
		if (Normalizer::isNormalized($value)) {
1261
			return $value;
1262
		}
1263
1264
		$normalizedValue = Normalizer::normalize($value);
1265
		if ($normalizedValue === null || $normalizedValue === false) {
1266
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1267
			return $value;
1268
		}
1269
1270
		return $normalizedValue;
1271
	}
1272
1273
	/**
1274
	 * A human readable string is generated based on version and build number
1275
	 *
1276
	 * @return string
1277
	 */
1278
	public static function getHumanVersion() {
1279
		$version = OC_Util::getVersionString();
1280
		$build = OC_Util::getBuild();
1281
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1282
			$version .= ' Build:' . $build;
1283
		}
1284
		return $version;
1285
	}
1286
1287
	/**
1288
	 * Returns whether the given file name is valid
1289
	 *
1290
	 * @param string $file file name to check
1291
	 * @return bool true if the file name is valid, false otherwise
1292
	 * @deprecated use \OC\Files\View::verifyPath()
1293
	 */
1294
	public static function isValidFileName($file) {
1295
		$trimmed = trim($file);
1296
		if ($trimmed === '') {
1297
			return false;
1298
		}
1299
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1300
			return false;
1301
		}
1302
1303
		// detect part files
1304
		if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1305
			return false;
1306
		}
1307
1308
		foreach (str_split($trimmed) as $char) {
1309
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1310
				return false;
1311
			}
1312
		}
1313
		return true;
1314
	}
1315
1316
	/**
1317
	 * Check whether the instance needs to perform an upgrade,
1318
	 * either when the core version is higher or any app requires
1319
	 * an upgrade.
1320
	 *
1321
	 * @param \OC\SystemConfig $config
1322
	 * @return bool whether the core or any app needs an upgrade
1323
	 * @throws \OCP\HintException When the upgrade from the given version is not allowed
1324
	 */
1325
	public static function needUpgrade(\OC\SystemConfig $config) {
1326
		if ($config->getValue('installed', false)) {
1327
			$installedVersion = $config->getValue('version', '0.0.0');
1328
			$currentVersion = implode('.', \OCP\Util::getVersion());
1329
			$versionDiff = version_compare($currentVersion, $installedVersion);
1330
			if ($versionDiff > 0) {
1331
				return true;
1332
			} elseif ($config->getValue('debug', false) && $versionDiff < 0) {
1333
				// downgrade with debug
1334
				$installedMajor = explode('.', $installedVersion);
1335
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1336
				$currentMajor = explode('.', $currentVersion);
1337
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1338
				if ($installedMajor === $currentMajor) {
1339
					// Same major, allow downgrade for developers
1340
					return true;
1341
				} else {
1342
					// downgrade attempt, throw exception
1343
					throw new \OCP\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1344
				}
1345
			} elseif ($versionDiff < 0) {
1346
				// downgrade attempt, throw exception
1347
				throw new \OCP\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1348
			}
1349
1350
			// also check for upgrades for apps (independently from the user)
1351
			$apps = \OC_App::getEnabledApps(false, true);
1352
			$shouldUpgrade = false;
1353
			foreach ($apps as $app) {
1354
				if (\OC_App::shouldUpgrade($app)) {
1355
					$shouldUpgrade = true;
1356
					break;
1357
				}
1358
			}
1359
			return $shouldUpgrade;
1360
		} else {
1361
			return false;
1362
		}
1363
	}
1364
}
1365