Completed
Pull Request — master (#29676)
by Tom
09:20
created

Filesystem::readdir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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) 2017, 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\Mount\IMountPoint;
68
use OCP\Files\NotFoundException;
69
use OCP\IUserManager;
70
71
class Filesystem {
72
73
	/**
74
	 * @var Mount\Manager $mounts
75
	 */
76
	private static $mounts;
77
78
	public static $loaded = false;
79
	/**
80
	 * @var \OC\Files\View $defaultInstance
81
	 */
82
	static private $defaultInstance;
83
84
	static private $usersSetup = [];
85
86
	static private $normalizedPathCache = null;
87
88
	static private $listeningForProviders = false;
89
90
	/**
91
	 * classname which used for hooks handling
92
	 * used as signalclass in OC_Hooks::emit()
93
	 */
94
	const CLASSNAME = 'OC_Filesystem';
95
96
	/**
97
	 * signalname emitted before file renaming
98
	 *
99
	 * @param string $oldpath
100
	 * @param string $newpath
101
	 */
102
	const signal_rename = 'rename';
103
104
	/**
105
	 * signal emitted after file renaming
106
	 *
107
	 * @param string $oldpath
108
	 * @param string $newpath
109
	 */
110
	const signal_post_rename = 'post_rename';
111
112
	/**
113
	 * signal emitted before file/dir creation
114
	 *
115
	 * @param string $path
116
	 * @param bool $run changing this flag to false in hook handler will cancel event
117
	 */
118
	const signal_create = 'create';
119
120
	/**
121
	 * signal emitted after file/dir creation
122
	 *
123
	 * @param string $path
124
	 * @param bool $run changing this flag to false in hook handler will cancel event
125
	 */
126
	const signal_post_create = 'post_create';
127
128
	/**
129
	 * signal emits before file/dir copy
130
	 *
131
	 * @param string $oldpath
132
	 * @param string $newpath
133
	 * @param bool $run changing this flag to false in hook handler will cancel event
134
	 */
135
	const signal_copy = 'copy';
136
137
	/**
138
	 * signal emits after file/dir copy
139
	 *
140
	 * @param string $oldpath
141
	 * @param string $newpath
142
	 */
143
	const signal_post_copy = 'post_copy';
144
145
	/**
146
	 * signal emits before file/dir save
147
	 *
148
	 * @param string $path
149
	 * @param bool $run changing this flag to false in hook handler will cancel event
150
	 */
151
	const signal_write = 'write';
152
153
	/**
154
	 * signal emits after file/dir save
155
	 *
156
	 * @param string $path
157
	 */
158
	const signal_post_write = 'post_write';
159
160
	/**
161
	 * signal emitted before file/dir update
162
	 *
163
	 * @param string $path
164
	 * @param bool $run changing this flag to false in hook handler will cancel event
165
	 */
166
	const signal_update = 'update';
167
168
	/**
169
	 * signal emitted after file/dir update
170
	 *
171
	 * @param string $path
172
	 * @param bool $run changing this flag to false in hook handler will cancel event
173
	 */
174
	const signal_post_update = 'post_update';
175
176
	/**
177
	 * signal emits when reading file/dir
178
	 *
179
	 * @param string $path
180
	 */
181
	const signal_read = 'read';
182
183
	/**
184
	 * signal emits when removing file/dir
185
	 *
186
	 * @param string $path
187
	 */
188
	const signal_delete = 'delete';
189
190
	/**
191
	 * parameters definitions for signals
192
	 */
193
	const signal_param_path = 'path';
194
	const signal_param_oldpath = 'oldpath';
195
	const signal_param_newpath = 'newpath';
196
197
	/**
198
	 * run - changing this flag to false in hook handler will cancel event
199
	 */
200
	const signal_param_run = 'run';
201
202
	const signal_create_mount = 'create_mount';
203
	const signal_delete_mount = 'delete_mount';
204
	const signal_param_mount_type = 'mounttype';
205
	const signal_param_users = 'users';
206
207
	/**
208
	 * @var \OC\Files\Storage\StorageFactory $loader
209
	 */
210
	private static $loader;
211
212
	/** @var bool */
213
	private static $logWarningWhenAddingStorageWrapper = true;
214
215
	/**
216
	 * @param bool $shouldLog
217
	 * @return bool previous value
218
	 * @internal
219
	 */
220
	public static function logWarningWhenAddingStorageWrapper($shouldLog) {
221
		$previousValue = self::$logWarningWhenAddingStorageWrapper;
222
		self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
223
		return $previousValue;
224
	}
225
226
	/**
227
	 * @param string $wrapperName
228
	 * @param callable $wrapper
229
	 * @param int $priority
230
	 */
231
	public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
232
		if (self::$logWarningWhenAddingStorageWrapper) {
233
			\OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [
234
				'wrapper' => $wrapperName,
235
				'app' => 'filesystem',
236
			]);
237
		}
238
239
		$mounts = self::getMountManager()->getAll();
240
		if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
0 ignored issues
show
Unused Code introduced by
The call to IStorageFactory::addStorageWrapper() has too many arguments starting with $priority.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
241
			// do not re-wrap if storage with this name already existed
242
			return;
243
		}
244
	}
245
246
	/**
247
	 * Returns the storage factory
248
	 *
249
	 * @return \OCP\Files\Storage\IStorageFactory
250
	 */
251
	public static function getLoader() {
252
		if (!self::$loader) {
253
			self::$loader = new StorageFactory();
254
		}
255
		return self::$loader;
256
	}
257
258
	/**
259
	 * Returns the mount manager
260
	 *
261
	 * @return \OC\Files\Mount\Manager
262
	 */
263
	public static function getMountManager($user = '') {
264
		if (!self::$mounts) {
265
			\OC_Util::setupFS($user);
266
		}
267
		return self::$mounts;
268
	}
269
270
	/**
271
	 * get the mountpoint of the storage object for a path
272
	 * ( note: because a storage is not always mounted inside the fakeroot, the
273
	 * returned mountpoint is relative to the absolute root of the filesystem
274
	 * and doesn't take the chroot into account )
275
	 *
276
	 * @param string $path
277
	 * @return string
278
	 */
279
	static public function getMountPoint($path) {
280
		if (!self::$mounts) {
281
			\OC_Util::setupFS();
282
		}
283
		$mount = self::$mounts->find($path);
284
		if ($mount) {
285
			return $mount->getMountPoint();
286
		} else {
287
			return '';
288
		}
289
	}
290
291
	/**
292
	 * get a list of all mount points in a directory
293
	 *
294
	 * @param string $path
295
	 * @return string[]
296
	 */
297
	static public function getMountPoints($path) {
298
		if (!self::$mounts) {
299
			\OC_Util::setupFS();
300
		}
301
		$result = [];
302
		$mounts = self::$mounts->findIn($path);
303
		foreach ($mounts as $mount) {
304
			$result[] = $mount->getMountPoint();
305
		}
306
		return $result;
307
	}
308
309
	/**
310
	 * get the storage mounted at $mountPoint
311
	 *
312
	 * @param string $mountPoint
313
	 * @return \OC\Files\Storage\Storage
314
	 */
315
	public static function getStorage($mountPoint) {
316
		if (!self::$mounts) {
317
			\OC_Util::setupFS();
318
		}
319
		$mount = self::$mounts->find($mountPoint);
320
		return $mount->getStorage();
321
	}
322
323
	/**
324
	 * @param string $id
325
	 * @return Mount\MountPoint[]
326
	 */
327
	public static function getMountByStorageId($id) {
328
		if (!self::$mounts) {
329
			\OC_Util::setupFS();
330
		}
331
		return self::$mounts->findByStorageId($id);
332
	}
333
334
	/**
335
	 * @param int $id
336
	 * @return Mount\MountPoint[]
337
	 */
338
	public static function getMountByNumericId($id) {
339
		if (!self::$mounts) {
340
			\OC_Util::setupFS();
341
		}
342
		return self::$mounts->findByNumericId($id);
343
	}
344
345
	/**
346
	 * resolve a path to a storage and internal path
347
	 *
348
	 * @param string $path
349
	 * @return array an array consisting of the storage and the internal path
350
	 */
351
	static public function resolvePath($path) {
352
		if (!self::$mounts) {
353
			\OC_Util::setupFS();
354
		}
355
		$mount = self::$mounts->find($path);
356
		if ($mount) {
357
			return [$mount->getStorage(), rtrim($mount->getInternalPath($path), '/')];
358
		} else {
359
			return [null, null];
360
		}
361
	}
362
363
	static public function init($user, $root) {
364
		if (self::$defaultInstance) {
365
			return false;
366
		}
367
		self::getLoader();
368
		self::$defaultInstance = new View($root);
369
370
		if (!self::$mounts) {
371
			self::$mounts = \OC::$server->getMountManager();
372
		}
373
374
		//load custom mount config
375
		self::initMountPoints($user);
376
377
		self::$loaded = true;
378
379
		return true;
380
	}
381
382
	static public function initMountManager() {
383
		if (!self::$mounts) {
384
			self::$mounts = \OC::$server->getMountManager();
385
		}
386
	}
387
388
	/**
389
	 * Initialize system and personal mount points for a user
390
	 *
391
	 * @param string $user
392
	 * @throws \OC\User\NoUserException if the user is not available
393
	 */
394
	public static function initMountPoints($user = '') {
395
		if ($user == '') {
396
			$user = \OC_User::getUser();
397
		}
398
		if ($user === null || $user === false || $user === '') {
399
			throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
400
		}
401
402
		if (isset(self::$usersSetup[$user])) {
403
			return;
404
		}
405
406
		self::$usersSetup[$user] = true;
407
408
		$userManager = \OC::$server->getUserManager();
409
		$userObject = $userManager->get($user);
410
411 View Code Duplication
		if (is_null($userObject)) {
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...
412
			\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
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('Backends provided no user object for ' . $user);
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);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $userObject is correct as $userManager->get($user) (which targets OCP\IUserManager::get()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

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