Passed
Push — master ( 023d18...e0d767 )
by Roeland
12:40 queued 12s
created

Jail::opendir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Julius Härtl <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program. If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OC\Files\Storage\Wrapper;
28
29
use OC\Files\Cache\Wrapper\CacheJail;
30
use OC\Files\Cache\Wrapper\JailPropagator;
31
use OC\Files\Filesystem;
32
use OCP\Files\Storage\IStorage;
33
use OCP\Files\Storage\IWriteStreamStorage;
34
use OCP\Lock\ILockingProvider;
35
36
/**
37
 * Jail to a subdirectory of the wrapped storage
38
 *
39
 * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage
40
 */
41
class Jail extends Wrapper {
42
	/**
43
	 * @var string
44
	 */
45
	protected $rootPath;
46
47
	/**
48
	 * @param array $arguments ['storage' => $storage, 'mask' => $root]
49
	 *
50
	 * $storage: The storage that will be wrapper
51
	 * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage
52
	 */
53
	public function __construct($arguments) {
54
		parent::__construct($arguments);
55
		$this->rootPath = $arguments['root'];
56
	}
57
58
	public function getUnjailedPath($path) {
59
		return trim(Filesystem::normalizePath($this->rootPath . '/' . $path), '/');
60
	}
61
62
	/**
63
	 * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper
64
	 */
65
	public function getUnjailedStorage() {
66
		return $this->storage;
67
	}
68
69
70
	public function getJailedPath($path) {
71
		$root = rtrim($this->rootPath, '/') . '/';
72
73
		if ($path !== $this->rootPath && strpos($path, $root) !== 0) {
74
			return null;
75
		} else {
76
			$path = substr($path, strlen($this->rootPath));
77
			return trim($path, '/');
78
		}
79
	}
80
81
	public function getId() {
82
		return parent::getId();
83
	}
84
85
	/**
86
	 * see http://php.net/manual/en/function.mkdir.php
87
	 *
88
	 * @param string $path
89
	 * @return bool
90
	 */
91
	public function mkdir($path) {
92
		return $this->getWrapperStorage()->mkdir($this->getUnjailedPath($path));
93
	}
94
95
	/**
96
	 * see http://php.net/manual/en/function.rmdir.php
97
	 *
98
	 * @param string $path
99
	 * @return bool
100
	 */
101
	public function rmdir($path) {
102
		return $this->getWrapperStorage()->rmdir($this->getUnjailedPath($path));
103
	}
104
105
	/**
106
	 * see http://php.net/manual/en/function.opendir.php
107
	 *
108
	 * @param string $path
109
	 * @return resource
110
	 */
111
	public function opendir($path) {
112
		return $this->getWrapperStorage()->opendir($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type resource. 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...
113
	}
114
115
	/**
116
	 * see http://php.net/manual/en/function.is_dir.php
117
	 *
118
	 * @param string $path
119
	 * @return bool
120
	 */
121
	public function is_dir($path) {
122
		return $this->getWrapperStorage()->is_dir($this->getUnjailedPath($path));
123
	}
124
125
	/**
126
	 * see http://php.net/manual/en/function.is_file.php
127
	 *
128
	 * @param string $path
129
	 * @return bool
130
	 */
131
	public function is_file($path) {
132
		return $this->getWrapperStorage()->is_file($this->getUnjailedPath($path));
133
	}
134
135
	/**
136
	 * see http://php.net/manual/en/function.stat.php
137
	 * only the following keys are required in the result: size and mtime
138
	 *
139
	 * @param string $path
140
	 * @return array
141
	 */
142
	public function stat($path) {
143
		return $this->getWrapperStorage()->stat($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type array. 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...
144
	}
145
146
	/**
147
	 * see http://php.net/manual/en/function.filetype.php
148
	 *
149
	 * @param string $path
150
	 * @return bool
151
	 */
152
	public function filetype($path) {
153
		return $this->getWrapperStorage()->filetype($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) also could return the type string which is incompatible with the documented return type boolean.
Loading history...
154
	}
155
156
	/**
157
	 * see http://php.net/manual/en/function.filesize.php
158
	 * The result for filesize when called on a folder is required to be 0
159
	 *
160
	 * @param string $path
161
	 * @return int
162
	 */
163
	public function filesize($path) {
164
		return $this->getWrapperStorage()->filesize($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type integer. 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...
165
	}
166
167
	/**
168
	 * check if a file can be created in $path
169
	 *
170
	 * @param string $path
171
	 * @return bool
172
	 */
173
	public function isCreatable($path) {
174
		return $this->getWrapperStorage()->isCreatable($this->getUnjailedPath($path));
175
	}
176
177
	/**
178
	 * check if a file can be read
179
	 *
180
	 * @param string $path
181
	 * @return bool
182
	 */
183
	public function isReadable($path) {
184
		return $this->getWrapperStorage()->isReadable($this->getUnjailedPath($path));
185
	}
186
187
	/**
188
	 * check if a file can be written to
189
	 *
190
	 * @param string $path
191
	 * @return bool
192
	 */
193
	public function isUpdatable($path) {
194
		return $this->getWrapperStorage()->isUpdatable($this->getUnjailedPath($path));
195
	}
196
197
	/**
198
	 * check if a file can be deleted
199
	 *
200
	 * @param string $path
201
	 * @return bool
202
	 */
203
	public function isDeletable($path) {
204
		return $this->getWrapperStorage()->isDeletable($this->getUnjailedPath($path));
205
	}
206
207
	/**
208
	 * check if a file can be shared
209
	 *
210
	 * @param string $path
211
	 * @return bool
212
	 */
213
	public function isSharable($path) {
214
		return $this->getWrapperStorage()->isSharable($this->getUnjailedPath($path));
215
	}
216
217
	/**
218
	 * get the full permissions of a path.
219
	 * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php
220
	 *
221
	 * @param string $path
222
	 * @return int
223
	 */
224
	public function getPermissions($path) {
225
		return $this->getWrapperStorage()->getPermissions($this->getUnjailedPath($path));
226
	}
227
228
	/**
229
	 * see http://php.net/manual/en/function.file_exists.php
230
	 *
231
	 * @param string $path
232
	 * @return bool
233
	 */
234
	public function file_exists($path) {
235
		return $this->getWrapperStorage()->file_exists($this->getUnjailedPath($path));
236
	}
237
238
	/**
239
	 * see http://php.net/manual/en/function.filemtime.php
240
	 *
241
	 * @param string $path
242
	 * @return int
243
	 */
244
	public function filemtime($path) {
245
		return $this->getWrapperStorage()->filemtime($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type integer. 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...
246
	}
247
248
	/**
249
	 * see http://php.net/manual/en/function.file_get_contents.php
250
	 *
251
	 * @param string $path
252
	 * @return string
253
	 */
254
	public function file_get_contents($path) {
255
		return $this->getWrapperStorage()->file_get_contents($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type string. 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...
256
	}
257
258
	/**
259
	 * see http://php.net/manual/en/function.file_put_contents.php
260
	 *
261
	 * @param string $path
262
	 * @param string $data
263
	 * @return bool
264
	 */
265
	public function file_put_contents($path, $data) {
266
		return $this->getWrapperStorage()->file_put_contents($this->getUnjailedPath($path), $data);
267
	}
268
269
	/**
270
	 * see http://php.net/manual/en/function.unlink.php
271
	 *
272
	 * @param string $path
273
	 * @return bool
274
	 */
275
	public function unlink($path) {
276
		return $this->getWrapperStorage()->unlink($this->getUnjailedPath($path));
277
	}
278
279
	/**
280
	 * see http://php.net/manual/en/function.rename.php
281
	 *
282
	 * @param string $path1
283
	 * @param string $path2
284
	 * @return bool
285
	 */
286
	public function rename($path1, $path2) {
287
		return $this->getWrapperStorage()->rename($this->getUnjailedPath($path1), $this->getUnjailedPath($path2));
288
	}
289
290
	/**
291
	 * see http://php.net/manual/en/function.copy.php
292
	 *
293
	 * @param string $path1
294
	 * @param string $path2
295
	 * @return bool
296
	 */
297
	public function copy($path1, $path2) {
298
		return $this->getWrapperStorage()->copy($this->getUnjailedPath($path1), $this->getUnjailedPath($path2));
299
	}
300
301
	/**
302
	 * see http://php.net/manual/en/function.fopen.php
303
	 *
304
	 * @param string $path
305
	 * @param string $mode
306
	 * @return resource
307
	 */
308
	public function fopen($path, $mode) {
309
		return $this->getWrapperStorage()->fopen($this->getUnjailedPath($path), $mode);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...iledPath($path), $mode) could also return false which is incompatible with the documented return type resource. 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...
310
	}
311
312
	/**
313
	 * get the mimetype for a file or folder
314
	 * The mimetype for a folder is required to be "httpd/unix-directory"
315
	 *
316
	 * @param string $path
317
	 * @return string
318
	 */
319
	public function getMimeType($path) {
320
		return $this->getWrapperStorage()->getMimeType($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type string. 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...
321
	}
322
323
	/**
324
	 * see http://php.net/manual/en/function.hash.php
325
	 *
326
	 * @param string $type
327
	 * @param string $path
328
	 * @param bool $raw
329
	 * @return string
330
	 */
331
	public function hash($type, $path, $raw = false) {
332
		return $this->getWrapperStorage()->hash($type, $this->getUnjailedPath($path), $raw);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...ailedPath($path), $raw) could also return false which is incompatible with the documented return type string. 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...
333
	}
334
335
	/**
336
	 * see http://php.net/manual/en/function.free_space.php
337
	 *
338
	 * @param string $path
339
	 * @return int
340
	 */
341
	public function free_space($path) {
342
		return $this->getWrapperStorage()->free_space($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type integer. 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...
343
	}
344
345
	/**
346
	 * search for occurrences of $query in file names
347
	 *
348
	 * @param string $query
349
	 * @return array
350
	 */
351
	public function search($query) {
352
		return $this->getWrapperStorage()->search($query);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapperStorage()->search($query) could also return false which is incompatible with the documented return type array. 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...
353
	}
354
355
	/**
356
	 * see http://php.net/manual/en/function.touch.php
357
	 * If the backend does not support the operation, false should be returned
358
	 *
359
	 * @param string $path
360
	 * @param int $mtime
361
	 * @return bool
362
	 */
363
	public function touch($path, $mtime = null) {
364
		return $this->getWrapperStorage()->touch($this->getUnjailedPath($path), $mtime);
365
	}
366
367
	/**
368
	 * get the path to a local version of the file.
369
	 * The local version of the file can be temporary and doesn't have to be persistent across requests
370
	 *
371
	 * @param string $path
372
	 * @return string
373
	 */
374
	public function getLocalFile($path) {
375
		return $this->getWrapperStorage()->getLocalFile($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type string. 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...
376
	}
377
378
	/**
379
	 * check if a file or folder has been updated since $time
380
	 *
381
	 * @param string $path
382
	 * @param int $time
383
	 * @return bool
384
	 *
385
	 * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed.
386
	 * returning true for other changes in the folder is optional
387
	 */
388
	public function hasUpdated($path, $time) {
389
		return $this->getWrapperStorage()->hasUpdated($this->getUnjailedPath($path), $time);
390
	}
391
392
	/**
393
	 * get a cache instance for the storage
394
	 *
395
	 * @param string $path
396
	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
0 ignored issues
show
Bug introduced by
The type OC\Files\Storage\Wrapper\optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
397
	 * @return \OC\Files\Cache\Cache
398
	 */
399
	public function getCache($path = '', $storage = null) {
400
		if (!$storage) {
401
			$storage = $this->getWrapperStorage();
402
		}
403
		$sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path), $storage);
404
		return new CacheJail($sourceCache, $this->rootPath);
405
	}
406
407
	/**
408
	 * get the user id of the owner of a file or folder
409
	 *
410
	 * @param string $path
411
	 * @return string
412
	 */
413
	public function getOwner($path) {
414
		return $this->getWrapperStorage()->getOwner($this->getUnjailedPath($path));
415
	}
416
417
	/**
418
	 * get a watcher instance for the cache
419
	 *
420
	 * @param string $path
421
	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
422
	 * @return \OC\Files\Cache\Watcher
423
	 */
424
	public function getWatcher($path = '', $storage = null) {
425
		if (!$storage) {
426
			$storage = $this;
427
		}
428
		return $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $storage);
429
	}
430
431
	/**
432
	 * get the ETag for a file or folder
433
	 *
434
	 * @param string $path
435
	 * @return string
436
	 */
437
	public function getETag($path) {
438
		return $this->getWrapperStorage()->getETag($this->getUnjailedPath($path));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getWrapper...getUnjailedPath($path)) could also return false which is incompatible with the documented return type string. 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...
439
	}
440
441
	/**
442
	 * @param string $path
443
	 * @return array
444
	 */
445
	public function getMetaData($path) {
446
		return $this->getWrapperStorage()->getMetaData($this->getUnjailedPath($path));
447
	}
448
449
	/**
450
	 * @param string $path
451
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
452
	 * @param \OCP\Lock\ILockingProvider $provider
453
	 * @throws \OCP\Lock\LockedException
454
	 */
455
	public function acquireLock($path, $type, ILockingProvider $provider) {
456
		$this->getWrapperStorage()->acquireLock($this->getUnjailedPath($path), $type, $provider);
457
	}
458
459
	/**
460
	 * @param string $path
461
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
462
	 * @param \OCP\Lock\ILockingProvider $provider
463
	 */
464
	public function releaseLock($path, $type, ILockingProvider $provider) {
465
		$this->getWrapperStorage()->releaseLock($this->getUnjailedPath($path), $type, $provider);
466
	}
467
468
	/**
469
	 * @param string $path
470
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
471
	 * @param \OCP\Lock\ILockingProvider $provider
472
	 */
473
	public function changeLock($path, $type, ILockingProvider $provider) {
474
		$this->getWrapperStorage()->changeLock($this->getUnjailedPath($path), $type, $provider);
475
	}
476
477
	/**
478
	 * Resolve the path for the source of the share
479
	 *
480
	 * @param string $path
481
	 * @return array
482
	 */
483
	public function resolvePath($path) {
484
		return [$this->getWrapperStorage(), $this->getUnjailedPath($path)];
485
	}
486
487
	/**
488
	 * @param IStorage $sourceStorage
489
	 * @param string $sourceInternalPath
490
	 * @param string $targetInternalPath
491
	 * @return bool
492
	 */
493
	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
494
		if ($sourceStorage === $this) {
0 ignored issues
show
introduced by
The condition $sourceStorage === $this is always false.
Loading history...
495
			return $this->copy($sourceInternalPath, $targetInternalPath);
496
		}
497
		return $this->getWrapperStorage()->copyFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
498
	}
499
500
	/**
501
	 * @param IStorage $sourceStorage
502
	 * @param string $sourceInternalPath
503
	 * @param string $targetInternalPath
504
	 * @return bool
505
	 */
506
	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
507
		if ($sourceStorage === $this) {
0 ignored issues
show
introduced by
The condition $sourceStorage === $this is always false.
Loading history...
508
			return $this->rename($sourceInternalPath, $targetInternalPath);
509
		}
510
		return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
511
	}
512
513
	public function getPropagator($storage = null) {
514
		if (isset($this->propagator)) {
515
			return $this->propagator;
516
		}
517
518
		if (!$storage) {
519
			$storage = $this;
520
		}
521
		$this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection());
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getDatabaseConnection() 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

521
		$this->propagator = new JailPropagator($storage, /** @scrutinizer ignore-deprecated */ \OC::$server->getDatabaseConnection());
Loading history...
522
		return $this->propagator;
523
	}
524
525
	public function writeStream(string $path, $stream, int $size = null): int {
526
		$storage = $this->getWrapperStorage();
527
		if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
528
			/** @var IWriteStreamStorage $storage */
529
			return $storage->writeStream($this->getUnjailedPath($path), $stream, $size);
530
		} else {
531
			$target = $this->fopen($path, 'w');
532
			list($count, $result) = \OC_Helper::streamCopy($stream, $target);
533
			fclose($stream);
534
			fclose($target);
535
			return $count;
536
		}
537
	}
538
539
	public function getDirectoryContent($directory): \Traversable {
540
		return $this->getWrapperStorage()->getDirectoryContent($this->getUnjailedPath($directory));
541
	}
542
}
543