Issues (1798)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

lib/private/Files/Filesystem.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Christopher Schäpers <[email protected]>
6
 * @author Florin Peter <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Martin Mattel <[email protected]>
11
 * @author Michael Gapczynski <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Robin Appelman <[email protected]>
14
 * @author Robin McCorkell <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Sam Tuke <[email protected]>
17
 * @author Stephan Peijnik <[email protected]>
18
 * @author Thomas Müller <[email protected]>
19
 * @author Vincent Petry <[email protected]>
20
 *
21
 * @copyright Copyright (c) 2018, ownCloud GmbH
22
 * @license AGPL-3.0
23
 *
24
 * This code is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License, version 3,
26
 * as published by the Free Software Foundation.
27
 *
28
 * This program is distributed in the hope that it will be useful,
29
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
 * GNU Affero General Public License for more details.
32
 *
33
 * You should have received a copy of the GNU Affero General Public License, version 3,
34
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
35
 *
36
 */
37
38
/**
39
 * Class for abstraction of filesystem functions
40
 * This class won't call any filesystem functions for itself but will pass them to the correct OC_Filestorage object
41
 * this class should also handle all the file permission related stuff
42
 *
43
 * Hooks provided:
44
 *   read(path)
45
 *   write(path, &run)
46
 *   post_write(path)
47
 *   create(path, &run) (when a file is created, both create and write will be emitted in that order)
48
 *   post_create(path)
49
 *   delete(path, &run)
50
 *   post_delete(path)
51
 *   rename(oldpath,newpath, &run)
52
 *   post_rename(oldpath,newpath)
53
 *   copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order)
54
 *   post_rename(oldpath,newpath)
55
 *   post_initMountPoints(user, user_dir)
56
 *
57
 *   the &run parameter can be set to false to prevent the operation from occurring
58
 */
59
60
namespace OC\Files;
61
62
use OC\Cache\CappedMemoryCache;
63
use OC\Files\Config\MountProviderCollection;
64
use OC\Files\Mount\MountPoint;
65
use OC\Files\Storage\StorageFactory;
66
use OCP\Files\Config\IMountProvider;
67
use OCP\Files\NotFoundException;
68
use OCP\IUserManager;
69
70
class Filesystem {
71
72
	/**
73
	 * @var Mount\Manager $mounts
74
	 */
75
	private static $mounts;
76
77
	public static $loaded = false;
78
	/**
79
	 * @var \OC\Files\View $defaultInstance
80
	 */
81
	private static $defaultInstance;
82
83
	private static $usersSetup = [];
84
85
	private static $normalizedPathCache = null;
86
87
	private static $listeningForProviders = false;
88
89
	/**
90
	 * classname which used for hooks handling
91
	 * used as signalclass in OC_Hooks::emit()
92
	 */
93
	const CLASSNAME = 'OC_Filesystem';
94
95
	/**
96
	 * signalname emitted before file renaming
97
	 *
98
	 * @param string $oldpath
99
	 * @param string $newpath
100
	 */
101
	const signal_rename = 'rename';
102
103
	/**
104
	 * signal emitted after file renaming
105
	 *
106
	 * @param string $oldpath
107
	 * @param string $newpath
108
	 */
109
	const signal_post_rename = 'post_rename';
110
111
	/**
112
	 * signal emitted before file/dir creation
113
	 *
114
	 * @param string $path
115
	 * @param bool $run changing this flag to false in hook handler will cancel event
116
	 */
117
	const signal_create = 'create';
118
119
	/**
120
	 * signal emitted after file/dir creation
121
	 *
122
	 * @param string $path
123
	 * @param bool $run changing this flag to false in hook handler will cancel event
124
	 */
125
	const signal_post_create = 'post_create';
126
127
	/**
128
	 * signal emits before file/dir copy
129
	 *
130
	 * @param string $oldpath
131
	 * @param string $newpath
132
	 * @param bool $run changing this flag to false in hook handler will cancel event
133
	 */
134
	const signal_copy = 'copy';
135
136
	/**
137
	 * signal emits after file/dir copy
138
	 *
139
	 * @param string $oldpath
140
	 * @param string $newpath
141
	 */
142
	const signal_post_copy = 'post_copy';
143
144
	/**
145
	 * signal emits before file/dir save
146
	 *
147
	 * @param string $path
148
	 * @param bool $run changing this flag to false in hook handler will cancel event
149
	 */
150
	const signal_write = 'write';
151
152
	/**
153
	 * signal emits after file/dir save
154
	 *
155
	 * @param string $path
156
	 */
157
	const signal_post_write = 'post_write';
158
159
	/**
160
	 * signal emitted before file/dir update
161
	 *
162
	 * @param string $path
163
	 * @param bool $run changing this flag to false in hook handler will cancel event
164
	 */
165
	const signal_update = 'update';
166
167
	/**
168
	 * signal emitted after file/dir update
169
	 *
170
	 * @param string $path
171
	 * @param bool $run changing this flag to false in hook handler will cancel event
172
	 */
173
	const signal_post_update = 'post_update';
174
175
	/**
176
	 * signal emits when reading file/dir
177
	 *
178
	 * @param string $path
179
	 */
180
	const signal_read = 'read';
181
182
	/**
183
	 * signal emits when removing file/dir
184
	 *
185
	 * @param string $path
186
	 */
187
	const signal_delete = 'delete';
188
189
	/**
190
	 * parameters definitions for signals
191
	 */
192
	const signal_param_path = 'path';
193
	const signal_param_oldpath = 'oldpath';
194
	const signal_param_newpath = 'newpath';
195
196
	/**
197
	 * run - changing this flag to false in hook handler will cancel event
198
	 */
199
	const signal_param_run = 'run';
200
201
	const signal_create_mount = 'create_mount';
202
	const signal_delete_mount = 'delete_mount';
203
	const signal_param_mount_type = 'mounttype';
204
	const signal_param_users = 'users';
205
206
	/**
207
	 * @var \OC\Files\Storage\StorageFactory $loader
208
	 */
209
	private static $loader;
210
211
	/** @var bool */
212
	private static $logWarningWhenAddingStorageWrapper = true;
213
214
	/**
215
	 * @param bool $shouldLog
216
	 * @return bool previous value
217
	 * @internal
218
	 */
219
	public static function logWarningWhenAddingStorageWrapper($shouldLog) {
220
		$previousValue = self::$logWarningWhenAddingStorageWrapper;
221
		self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
222
		return $previousValue;
223
	}
224
225
	/**
226
	 * @param string $wrapperName
227
	 * @param callable $wrapper
228
	 * @param int $priority
229
	 */
230
	public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
231
		if (self::$logWarningWhenAddingStorageWrapper) {
232
			\OC::$server->getLogger()->debug("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [
233
				'wrapper' => $wrapperName,
234
				'app' => 'filesystem',
235
			]);
236
		}
237
238
		$mounts = self::getMountManager()->getAll();
239
		if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
240
			// do not re-wrap if storage with this name already existed
241
			return;
242
		}
243
	}
244
245
	/**
246
	 * Returns the storage factory
247
	 *
248
	 * @return \OCP\Files\Storage\IStorageFactory
249
	 */
250
	public static function getLoader() {
251
		if (!self::$loader) {
252
			self::$loader = new StorageFactory();
253
		}
254
		return self::$loader;
255
	}
256
257
	/**
258
	 * Returns the mount manager
259
	 *
260
	 * @return \OC\Files\Mount\Manager
261
	 */
262
	public static function getMountManager($user = '') {
263
		if (!self::$mounts) {
264
			\OC_Util::setupFS($user);
265
		}
266
		return self::$mounts;
267
	}
268
269
	/**
270
	 * get the mountpoint of the storage object for a path
271
	 * ( note: because a storage is not always mounted inside the fakeroot, the
272
	 * returned mountpoint is relative to the absolute root of the filesystem
273
	 * and doesn't take the chroot into account )
274
	 *
275
	 * @param string $path
276
	 * @return string
277
	 */
278
	public static function getMountPoint($path) {
279
		if (!self::$mounts) {
280
			\OC_Util::setupFS();
281
		}
282
		$mount = self::$mounts->find($path);
283
		if ($mount) {
284
			return $mount->getMountPoint();
285
		} else {
286
			return '';
287
		}
288
	}
289
290
	/**
291
	 * get a list of all mount points in a directory
292
	 *
293
	 * @param string $path
294
	 * @return string[]
295
	 */
296
	public static function getMountPoints($path) {
297
		if (!self::$mounts) {
298
			\OC_Util::setupFS();
299
		}
300
		$result = [];
301
		$mounts = self::$mounts->findIn($path);
302
		foreach ($mounts as $mount) {
303
			$result[] = $mount->getMountPoint();
304
		}
305
		return $result;
306
	}
307
308
	/**
309
	 * get the storage mounted at $mountPoint
310
	 *
311
	 * @param string $mountPoint
312
	 * @return \OC\Files\Storage\Storage
313
	 */
314
	public static function getStorage($mountPoint) {
315
		if (!self::$mounts) {
316
			\OC_Util::setupFS();
317
		}
318
		$mount = self::$mounts->find($mountPoint);
319
		return $mount->getStorage();
320
	}
321
322
	/**
323
	 * @param string $id
324
	 * @return Mount\MountPoint[]
325
	 */
326
	public static function getMountByStorageId($id) {
327
		if (!self::$mounts) {
328
			\OC_Util::setupFS();
329
		}
330
		return self::$mounts->findByStorageId($id);
331
	}
332
333
	/**
334
	 * @param int $id
335
	 * @return Mount\MountPoint[]
336
	 */
337
	public static function getMountByNumericId($id) {
338
		if (!self::$mounts) {
339
			\OC_Util::setupFS();
340
		}
341
		return self::$mounts->findByNumericId($id);
342
	}
343
344
	/**
345
	 * resolve a path to a storage and internal path
346
	 *
347
	 * @param string $path
348
	 * @return array an array consisting of the storage and the internal path
349
	 */
350
	public static function resolvePath($path) {
351
		if (!self::$mounts) {
352
			\OC_Util::setupFS();
353
		}
354
		$mount = self::$mounts->find($path);
355
		if ($mount) {
356
			return [$mount->getStorage(), \rtrim($mount->getInternalPath($path), '/')];
357
		} else {
358
			return [null, null];
359
		}
360
	}
361
362
	public static function init($user, $root) {
363
		if (self::$defaultInstance) {
364
			return false;
365
		}
366
		self::getLoader();
367
		self::$defaultInstance = new View($root);
368
369
		if (!self::$mounts) {
370
			self::$mounts = \OC::$server->getMountManager();
371
		}
372
373
		//load custom mount config
374
		self::initMountPoints($user);
375
376
		self::$loaded = true;
377
378
		return true;
379
	}
380
381
	public static function initMountManager() {
382
		if (!self::$mounts) {
383
			self::$mounts = \OC::$server->getMountManager();
384
		}
385
	}
386
387
	/**
388
	 * Initialize system and personal mount points for a user
389
	 *
390
	 * @param string $user
391
	 * @throws \OC\User\NoUserException if the user is not available
392
	 */
393
	public static function initMountPoints($user = '') {
394
		if ($user == '') {
395
			$user = \OC_User::getUser();
396
		}
397
		if ($user === null || $user === false || $user === '') {
398
			throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
399
		}
400
401
		if (isset(self::$usersSetup[$user])) {
402
			return;
403
		}
404
405
		self::$usersSetup[$user] = true;
406
407
		$userManager = \OC::$server->getUserManager();
408
		$userObject = $userManager->get($user);
409
410 View Code Duplication
		if ($userObject === null) {
411
			$msg = "Backends provided no user object for $user";
412
			\OC::$server->getLogger()->error($msg, ['app' => __CLASS__]);
413
			// reset flag, this will make it possible to rethrow the exception if called again
414
			unset(self::$usersSetup[$user]);
415
			throw new \OC\User\NoUserException($msg);
416
		}
417
418
		$realUid = $userObject->getUID();
419
		// workaround in case of different casings
420
		if ($user !== $realUid) {
421
			$stack = \json_encode(\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50));
422
			\OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN);
423
			$user = $realUid;
424
425
			// again with the correct casing
426
			if (isset(self::$usersSetup[$user])) {
427
				return;
428
			}
429
430
			self::$usersSetup[$user] = true;
431
		}
432
433
		/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
434
		$mountConfigManager = \OC::$server->getMountProviderCollection();
435
436
		// home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
437
		$homeMount = $mountConfigManager->getHomeMountForUser($userObject);
438
439
		self::getMountManager()->addMount($homeMount);
440
441
		\OC\Files\Filesystem::getStorage($user);
442
443
		// Chance to mount for other storages
444
		if ($userObject) {
445
			$mounts = $mountConfigManager->getMountsForUser($userObject);
446
			\array_walk($mounts, [self::$mounts, 'addMount']);
447
			$mounts[] = $homeMount;
448
			$mountConfigManager->registerMounts($userObject, $mounts);
449
		}
450
451
		self::listenForNewMountProviders($mountConfigManager, $userManager);
452
		\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user]);
453
	}
454
455
	/**
456
	 * Get mounts from mount providers that are registered after setup
457
	 *
458
	 * @param MountProviderCollection $mountConfigManager
459
	 * @param IUserManager $userManager
460
	 */
461
	private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
462
		if (!self::$listeningForProviders) {
463
			self::$listeningForProviders = true;
464
			$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
465
				foreach (Filesystem::$usersSetup as $user => $setup) {
466
					$userObject = $userManager->get($user);
467
					if ($userObject) {
468
						$mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
469
						\array_walk($mounts, [self::$mounts, 'addMount']);
470
					}
471
				}
472
			});
473
		}
474
	}
475
476
	/**
477
	 * get the default filesystem view
478
	 *
479
	 * @return View
480
	 */
481
	public static function getView() {
482
		return self::$defaultInstance;
483
	}
484
485
	/**
486
	 * tear down the filesystem, removing all storage providers
487
	 */
488
	public static function tearDown() {
489
		self::clearMounts();
490
		self::$defaultInstance = null;
491
	}
492
493
	/**
494
	 * get the relative path of the root data directory for the current user
495
	 *
496
	 * @return string
497
	 *
498
	 * Returns path like /admin/files
499
	 */
500
	public static function getRoot() {
501
		if (!self::$defaultInstance) {
502
			return null;
503
		}
504
		return self::$defaultInstance->getRoot();
505
	}
506
507
	/**
508
	 * clear all mounts and storage backends
509
	 */
510
	public static function clearMounts() {
511
		if (self::$mounts) {
512
			self::$usersSetup = [];
513
			self::$mounts->clear();
514
		}
515
	}
516
517
	/**
518
	 * mount an \OC\Files\Storage\Storage in our virtual filesystem
519
	 *
520
	 * @param \OC\Files\Storage\Storage|string $class
521
	 * @param array $arguments
522
	 * @param string $mountpoint
523
	 */
524
	public static function mount($class, $arguments, $mountpoint) {
525
		if (!self::$mounts) {
526
			\OC_Util::setupFS();
527
		}
528
		$mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
529
		self::$mounts->addMount($mount);
530
	}
531
532
	/**
533
	 * return the path to a local version of the file
534
	 * we need this because we can't know if a file is stored local or not from
535
	 * outside the filestorage and for some purposes a local file is needed
536
	 *
537
	 * @param string $path
538
	 * @return string
539
	 */
540
	public static function getLocalFile($path) {
541
		return self::$defaultInstance->getLocalFile($path);
542
	}
543
544
	/**
545
	 * @param string $path
546
	 * @return string
547
	 */
548
	public static function getLocalFolder($path) {
549
		return self::$defaultInstance->getLocalFolder($path);
550
	}
551
552
	/**
553
	 * return path to file which reflects one visible in browser
554
	 *
555
	 * @param string $path
556
	 * @return string
557
	 */
558
	public static function getLocalPath($path) {
559
		$datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
560
		$newpath = $path;
561
		if (\strncmp($newpath, $datadir, \strlen($datadir)) == 0) {
562
			$newpath = \substr($path, \strlen($datadir));
563
		}
564
		return $newpath;
565
	}
566
567
	/**
568
	 * check if the requested path is valid
569
	 *
570
	 * @param string $path
571
	 * @return bool
572
	 */
573
	public static function isValidPath($path) {
574
		$path = self::normalizePath($path);
575
		if (!$path || $path[0] !== '/') {
576
			$path = '/' . $path;
577
		}
578
		if (\strpos($path, '/../') !== false || \strrchr($path, '/') === '/..') {
579
			return false;
580
		}
581
		return true;
582
	}
583
584
	/**
585
	 * checks if a file is blacklisted for storage in the filesystem
586
	 * Listens to write and rename hooks
587
	 *
588
	 * @param array $data from hook
589
	 */
590
	public static function isForbiddenFileOrDir_Hook($data) {
591
		if (isset($data['path'])) {
592
			$path = $data['path'];
593
		} elseif (isset($data['newpath'])) {
594
			$path = $data['newpath'];
595
		}
596
		if (isset($path)) {
597
			if (self::isForbiddenFileOrDir($path)) {
598
				$data['run'] = false;
599
			}
600
		}
601
	}
602
603
	/**
604
	 * depriciated, replaced by isForbiddenFileOrDir
605
	 * @param string $filename
606
	 * @return boolean
607
	 */
608
	public static function isFileBlacklisted($filename) {
609
		return self::isForbiddenFileOrDir($filename);
610
	}
611
612
	/**
613
	 * check if the directory should be ignored when scanning
614
	 * NOTE: the special directories . and .. would cause never ending recursion
615
	 *
616
	 * @param String $dir
617
	 * @return boolean
618
	 */
619
	public static function isIgnoredDir($dir) {
620
		if ($dir === '.' || $dir === '..') {
621
			return true;
622
		}
623
		return false;
624
	}
625
626
	/**
627
	* Check if the directory path / file name contains a Blacklisted or Excluded name
628
	* config.php parameter arrays can contain file names to be blacklisted or directory names to be excluded
629
	* Blacklist ... files that may harm the owncloud environment like a foreign .htaccess file
630
	* Excluded  ... directories that are excluded from beeing further processed, like snapshot directories
631
	* The parameter $ed is only used in conjunction with unit tests as we handover here the excluded
632
	* directory name to be tested against. $ed and the query with can be redesigned if filesystem.php will get
633
	* a constructor where it is then possible to define the excluded directory names for unit tests.
634
	* @param string $FileOrDir
635
	* @param array $ed
636
	* @return boolean
637
	*/
638
	public static function isForbiddenFileOrDir($FileOrDir, $ed = []) {
639
		$excluded = [];
640
		$blacklist = [];
641
		$path_parts = [];
642
		$ppx = [];
643
		$blacklist = \OC::$server->getSystemConfig()->getValue('blacklisted_files', ['.htaccess']);
644
		if ($ed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ed of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
645
			$excluded = $ed;
646
		} else {
647
			$excluded = \OC::$server->getSystemConfig()->getValue('excluded_directories', $ed);
648
		}
649
		// explode '/'
650
		$ppx = \array_filter(\explode('/', $FileOrDir), 'strlen');
651
		$ppx = \array_map('strtolower', $ppx);
652
		// further explode each array element with '\' and add to result array if found
653
		foreach ($ppx as $pp) {
654
			// only add an array element if strlen != 0
655
			$path_parts = \array_merge($path_parts, \array_filter(\explode('\\', $pp), 'strlen'));
656
		}
657
		if ($excluded) {
658
			$excluded = \array_map('trim', $excluded);
659
			$excluded = \array_map('strtolower', $excluded);
660
			$match = \array_intersect($path_parts, $excluded);
661
			if ($match) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $match of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
662
				return true;
663
			}
664
		}
665
		$blacklist = \array_map('trim', $blacklist);
666
		$blacklist = \array_map('strtolower', $blacklist);
667
		$match = \array_intersect($path_parts, $blacklist);
668
		if ($match) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $match of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
669
			return true;
670
		}
671
		return false;
672
	}
673
674
	/**
675
	 * following functions are equivalent to their php builtin equivalents for arguments/return values.
676
	 */
677
	public static function mkdir($path) {
678
		return self::$defaultInstance->mkdir($path);
679
	}
680
681
	public static function rmdir($path) {
682
		return self::$defaultInstance->rmdir($path);
683
	}
684
685
	public static function opendir($path) {
686
		return self::$defaultInstance->opendir($path);
687
	}
688
689
	public static function is_dir($path) {
690
		return self::$defaultInstance->is_dir($path);
691
	}
692
693
	public static function is_file($path) {
694
		return self::$defaultInstance->is_file($path);
695
	}
696
697
	public static function stat($path) {
698
		return self::$defaultInstance->stat($path);
699
	}
700
701
	public static function filetype($path) {
702
		return self::$defaultInstance->filetype($path);
703
	}
704
705
	public static function filesize($path) {
706
		return self::$defaultInstance->filesize($path);
707
	}
708
709
	public static function readfile($path) {
710
		return self::$defaultInstance->readfile($path);
711
	}
712
713
	public static function isCreatable($path) {
714
		return self::$defaultInstance->isCreatable($path);
715
	}
716
717
	public static function isReadable($path) {
718
		return self::$defaultInstance->isReadable($path);
719
	}
720
721
	public static function isUpdatable($path) {
722
		return self::$defaultInstance->isUpdatable($path);
723
	}
724
725
	public static function isDeletable($path) {
726
		return self::$defaultInstance->isDeletable($path);
727
	}
728
729
	public static function isSharable($path) {
730
		return self::$defaultInstance->isSharable($path);
731
	}
732
733
	public static function file_exists($path) {
734
		return self::$defaultInstance->file_exists($path);
735
	}
736
737
	public static function filemtime($path) {
738
		return self::$defaultInstance->filemtime($path);
739
	}
740
741
	public static function touch($path, $mtime = null) {
742
		return self::$defaultInstance->touch($path, $mtime);
743
	}
744
745
	/**
746
	 * @return string
747
	 */
748
	public static function file_get_contents($path) {
749
		return self::$defaultInstance->file_get_contents($path);
750
	}
751
752
	public static function file_put_contents($path, $data) {
753
		return self::$defaultInstance->file_put_contents($path, $data);
754
	}
755
756
	public static function unlink($path) {
757
		return self::$defaultInstance->unlink($path);
758
	}
759
760
	public static function rename($path1, $path2) {
761
		return self::$defaultInstance->rename($path1, $path2);
762
	}
763
764
	public static function copy($path1, $path2) {
765
		return self::$defaultInstance->copy($path1, $path2);
766
	}
767
768
	public static function fopen($path, $mode) {
769
		return self::$defaultInstance->fopen($path, $mode);
770
	}
771
772
	/**
773
	 * @return string
774
	 */
775
	public static function toTmpFile($path) {
776
		return self::$defaultInstance->toTmpFile($path);
777
	}
778
779
	public static function fromTmpFile($tmpFile, $path) {
780
		return self::$defaultInstance->fromTmpFile($tmpFile, $path);
781
	}
782
783
	public static function getMimeType($path) {
784
		return self::$defaultInstance->getMimeType($path);
785
	}
786
787
	public static function hash($type, $path, $raw = false) {
788
		return self::$defaultInstance->hash($type, $path, $raw);
789
	}
790
791
	public static function free_space($path = '/') {
792
		return self::$defaultInstance->free_space($path);
793
	}
794
795
	public static function search($query) {
796
		return self::$defaultInstance->search($query);
797
	}
798
799
	/**
800
	 * @param string $query
801
	 * @return FileInfo[] array of file info
802
	 */
803
	public static function searchByMime($query) {
804
		return self::$defaultInstance->searchByMime($query);
805
	}
806
807
	/**
808
	 * @param string|int $tag name or tag id
809
	 * @param string $userId owner of the tags
810
	 * @return FileInfo[] array of file info
811
	 */
812
	public static function searchByTag($tag, $userId) {
813
		return self::$defaultInstance->searchByTag($tag, $userId);
814
	}
815
816
	/**
817
	 * check if a file or folder has been updated since $time
818
	 *
819
	 * @param string $path
820
	 * @param int $time
821
	 * @return bool
822
	 */
823
	public static function hasUpdated($path, $time) {
824
		return self::$defaultInstance->hasUpdated($path, $time);
825
	}
826
827
	/**
828
	 * Fix common problems with a file path
829
	 *
830
	 * @param string $path
831
	 * @param bool $stripTrailingSlash whether to strip the trailing slash
832
	 * @param bool $isAbsolutePath whether the given path is absolute
833
	 * @param bool $keepUnicode true to disable unicode normalization
834
	 * @return string
835
	 */
836
	public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) {
837
		if (self::$normalizedPathCache === null) {
838
			self::$normalizedPathCache = new CappedMemoryCache();
839
		}
840
841
		/**
842
		 * FIXME: This is a workaround for existing classes and files which call
843
		 *        this function with another type than a valid string. This
844
		 *        conversion should get removed as soon as all existing
845
		 *        function calls have been fixed.
846
		 */
847
		$path = (string)$path;
848
849
		$cacheKey = \json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]);
850
851
		if (isset(self::$normalizedPathCache[$cacheKey])) {
852
			return self::$normalizedPathCache[$cacheKey];
853
		}
854
855
		if ($path == '') {
856
			return '/';
857
		}
858
859
		//normalize unicode if possible
860
		if (!$keepUnicode) {
861
			$path = \OC_Util::normalizeUnicode($path);
862
		}
863
864
		//no windows style slashes
865
		$path = \str_replace('\\', '/', $path);
866
867
		//add leading slash
868
		if ($path[0] !== '/') {
869
			$path = '/' . $path;
870
		}
871
872
		// remove '/./'
873
		// ugly, but str_replace() can't replace them all in one go
874
		// as the replacement itself is part of the search string
875
		// which will only be found during the next iteration
876
		while (\strpos($path, '/./') !== false) {
877
			$path = \str_replace('/./', '/', $path);
878
		}
879
		// remove sequences of slashes
880
		$path = \preg_replace('#/{2,}#', '/', $path);
881
882
		//remove trailing slash
883
		if ($stripTrailingSlash and \strlen($path) > 1 and \substr($path, -1, 1) === '/') {
884
			$path = \substr($path, 0, -1);
885
		}
886
887
		// remove trailing '/.'
888
		if (\substr($path, -2) == '/.') {
889
			$path = \substr($path, 0, -2);
890
		}
891
892
		$normalizedPath = $path;
893
		self::$normalizedPathCache[$cacheKey] = $normalizedPath;
894
895
		return $normalizedPath;
896
	}
897
898
	/**
899
	 * get the filesystem info
900
	 *
901
	 * @param string $path
902
	 * @param boolean $includeMountPoints whether to add mountpoint sizes,
903
	 * defaults to true
904
	 * @return \OC\Files\FileInfo|bool False if file does not exist
905
	 */
906
	public static function getFileInfo($path, $includeMountPoints = true) {
907
		return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
908
	}
909
910
	/**
911
	 * change file metadata
912
	 *
913
	 * @param string $path
914
	 * @param array $data
915
	 * @return int
916
	 *
917
	 * returns the fileid of the updated file
918
	 */
919
	public static function putFileInfo($path, $data) {
920
		return self::$defaultInstance->putFileInfo($path, $data);
921
	}
922
923
	/**
924
	 * get the content of a directory
925
	 *
926
	 * @param string $directory path under datadirectory
927
	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
928
	 * @return \OC\Files\FileInfo[]
929
	 */
930
	public static function getDirectoryContent($directory, $mimetype_filter = '') {
931
		return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
932
	}
933
934
	/**
935
	 * Get the path of a file by id
936
	 *
937
	 * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
938
	 *
939
	 * @param int $id
940
	 * @throws NotFoundException
941
	 * @return string
942
	 */
943
	public static function getPath($id) {
944
		if (self::$defaultInstance === null) {
945
			throw new NotFoundException("defaultInstance is null");
946
		}
947
		return self::$defaultInstance->getPath($id);
948
	}
949
950
	/**
951
	 * Get the owner for a file or folder
952
	 *
953
	 * @param string $path
954
	 * @return string
955
	 */
956
	public static function getOwner($path) {
957
		return self::$defaultInstance->getOwner($path);
958
	}
959
960
	/**
961
	 * get the ETag for a file or folder
962
	 *
963
	 * @param string $path
964
	 * @return string
965
	 */
966
	public static function getETag($path) {
967
		return self::$defaultInstance->getETag($path);
968
	}
969
}
970