Completed
Push — stable10 ( c20532...cf752b )
by Lukas
06:06
created

Shared::getPropagator()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 11
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 1
dl 11
loc 11
rs 9.4285
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bart Visscher <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Michael Gapczynski <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author scambra <[email protected]>
14
 * @author Vincent Petry <[email protected]>
15
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OC\Files\Storage;
33
34
use OC\Files\Filesystem;
35
use OC\Files\Cache\FailedCache;
36
use OCA\Files_Sharing\ISharedStorage;
37
use OC\Files\Storage\Wrapper\PermissionsMask;
38
use OCP\Constants;
39
use OCP\Files\Cache\ICacheEntry;
40
use OCP\Files\NotFoundException;
41
use OCP\Files\Storage\IStorage;
42
use OCP\Lock\ILockingProvider;
43
44
/**
45
 * Convert target path to source path and pass the function call to the correct storage provider
46
 */
47
class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage {
48
	/** @var \OCP\Share\IShare */
49
	private $superShare;
50
51
	/** @var \OCP\Share\IShare[] */
52
	private $groupedShares;
53
54
	/**
55
	 * @var \OC\Files\View
56
	 */
57
	private $ownerView;
58
59
	private $initialized = false;
60
61
	/**
62
	 * @var ICacheEntry
63
	 */
64
	private $sourceRootInfo;
65
66
	/** @var string */
67
	private $user;
68
69
	/**
70
	 * @var \OCP\ILogger
71
	 */
72
	private $logger;
73
74
	/** @var  IStorage */
75
	private $nonMaskedStorage;
76
77
	public function __construct($arguments) {
78
		$this->ownerView = $arguments['ownerView'];
79
		$this->logger = \OC::$server->getLogger();
80
81
		$this->superShare = $arguments['superShare'];
82
		$this->groupedShares = $arguments['groupedShares'];
83
84
		$this->user = $arguments['user'];
85
86
		parent::__construct([
87
			'storage' => null,
88
			'root' => null,
89
		]);
90
	}
91
92
	private function init() {
93
		if ($this->initialized) {
94
			return;
95
		}
96
		$this->initialized = true;
97
		try {
98
			Filesystem::initMountPoints($this->superShare->getShareOwner());
99
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
100
			list($this->nonMaskedStorage, $this->rootPath) = $this->ownerView->resolvePath($sourcePath);
101
			$this->storage = new PermissionsMask([
102
				'storage' => $this->nonMaskedStorage,
103
				'mask' => $this->superShare->getPermissions()
104
			]);
105
			$this->sourceRootInfo = $this->nonMaskedStorage->getCache()->get($this->rootPath);
106
		} catch (NotFoundException $e) {
107
			$this->storage = new FailedStorage(['exception' => $e]);
108
			$this->rootPath = '';
109
		} catch (\Exception $e) {
110
			$this->storage = new FailedStorage(['exception' => $e]);
111
			$this->rootPath = '';
112
			$this->logger->logException($e);
113
		}
114
	}
115
116
	/**
117
	 * @inheritdoc
118
	 */
119
	public function instanceOfStorage($class) {
120
		if (in_array($class, ['\OC\Files\Storage\Home', '\OC\Files\ObjectStore\HomeObjectStoreStorage'])) {
121
			return false;
122
		}
123
		return parent::instanceOfStorage($class);
124
	}
125
126
	/**
127
	 * @return string
128
	 */
129
	public function getShareId() {
130
		return $this->superShare->getId();
131
	}
132
133
	private function isValid() {
134
		$this->init();
135
		return $this->sourceRootInfo && ($this->sourceRootInfo->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
136
	}
137
138
	/**
139
	 * get id of the mount point
140
	 *
141
	 * @return string
142
	 */
143
	public function getId() {
144
		return 'shared::' . $this->getMountPoint();
145
	}
146
147
	/**
148
	 * Get the permissions granted for a shared file
149
	 *
150
	 * @param string $target Shared target file path
151
	 * @return int CRUDS permissions granted
152
	 */
153
	public function getPermissions($target = '') {
154
		if (!$this->isValid()) {
155
			return 0;
156
		}
157
		$permissions = $this->superShare->getPermissions();
158
		// part files and the mount point always have delete permissions
159
		if ($target === '' || pathinfo($target, PATHINFO_EXTENSION) === 'part') {
160
			$permissions |= \OCP\Constants::PERMISSION_DELETE;
161
		}
162
163
		if (\OCP\Util::isSharingDisabledForUser()) {
0 ignored issues
show
Deprecated Code introduced by
The method OCP\Util::isSharingDisabledForUser() has been deprecated with message: 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
164
			$permissions &= ~\OCP\Constants::PERMISSION_SHARE;
165
		}
166
167
		return $permissions;
168
	}
169
170
	public function isCreatable($path) {
171
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_CREATE);
172
	}
173
174
	public function isReadable($path) {
175
		if (!$this->isValid()) {
176
			return false;
177
		}
178
		if (!$this->file_exists($path)) {
179
			return false;
180
		}
181
		list($storage, $internalPath) = $this->resolvePath($path);
182
		return $storage->isReadable($internalPath);
183
	}
184
185
	public function isUpdatable($path) {
186
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_UPDATE);
187
	}
188
189
	public function isDeletable($path) {
190
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_DELETE);
191
	}
192
193
	public function isSharable($path) {
194
		if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
0 ignored issues
show
Deprecated Code introduced by
The method OCP\Util::isSharingDisabledForUser() has been deprecated with message: 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
195
			return false;
196
		}
197
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE);
198
	}
199
200
	public function fopen($path, $mode) {
201
		if ($source = $this->getSourcePath($path)) {
202
			switch ($mode) {
203
				case 'r+':
204
				case 'rb+':
205
				case 'w+':
206
				case 'wb+':
207
				case 'x+':
208
				case 'xb+':
209
				case 'a+':
210
				case 'ab+':
211
				case 'w':
212
				case 'wb':
213
				case 'x':
214
				case 'xb':
215
				case 'a':
216
				case 'ab':
217
					$creatable = $this->isCreatable($path);
218
					$updatable = $this->isUpdatable($path);
219
					// if neither permissions given, no need to continue
220
					if (!$creatable && !$updatable) {
221
						return false;
222
					}
223
224
					$exists = $this->file_exists($path);
225
					// if a file exists, updatable permissions are required
226
					if ($exists && !$updatable) {
227
						return false;
228
					}
229
230
					// part file is allowed if !$creatable but the final file is $updatable
231
					if (pathinfo($path, PATHINFO_EXTENSION) !== 'part') {
232
						if (!$exists && !$creatable) {
233
							return false;
234
						}
235
					}
236
			}
237
			$info = array(
238
				'target' => $this->getMountPoint() . $path,
239
				'source' => $source,
240
				'mode' => $mode,
241
			);
242
			\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
243
			return $this->nonMaskedStorage->fopen($this->getSourcePath($path), $mode);
244
		}
245
		return false;
246
	}
247
248
	/**
249
	 * see http://php.net/manual/en/function.rename.php
250
	 *
251
	 * @param string $path1
252
	 * @param string $path2
253
	 * @return bool
254
	 */
255
	public function rename($path1, $path2) {
256
		$this->init();
257
		$isPartFile = pathinfo($path1, PATHINFO_EXTENSION) === 'part';
258
		$targetExists = $this->file_exists($path2);
259
		$sameFodler = dirname($path1) === dirname($path2);
260
261
		if ($targetExists || ($sameFodler && !$isPartFile)) {
262
			if (!$this->isUpdatable('')) {
263
				return false;
264
			}
265
		} else {
266
			if (!$this->isCreatable('')) {
267
				return false;
268
			}
269
		}
270
271
		return $this->nonMaskedStorage->rename($this->getSourcePath($path1), $this->getSourcePath($path2));
272
	}
273
274
	/**
275
	 * return mount point of share, relative to data/user/files
276
	 *
277
	 * @return string
278
	 */
279
	public function getMountPoint() {
280
		return $this->superShare->getTarget();
281
	}
282
283
	/**
284
	 * @param string $path
285
	 */
286
	public function setMountPoint($path) {
287
		$this->superShare->setTarget($path);
288
289
		foreach ($this->groupedShares as $share) {
290
			$share->setTarget($path);
291
		}
292
	}
293
294
	/**
295
	 * get the user who shared the file
296
	 *
297
	 * @return string
298
	 */
299
	public function getSharedFrom() {
300
		return $this->superShare->getShareOwner();
301
	}
302
303
	/**
304
	 * @return \OCP\Share\IShare
305
	 */
306
	public function getShare() {
307
		return $this->superShare;
308
	}
309
310
	/**
311
	 * return share type, can be "file" or "folder"
312
	 *
313
	 * @return string
314
	 */
315
	public function getItemType() {
316
		return $this->superShare->getNodeType();
317
	}
318
319
	public function getCache($path = '', $storage = null) {
320
		$this->init();
321
		if (is_null($this->storage) || $this->storage instanceof FailedStorage) {
322
			return new FailedCache(false);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \OC\Files\Cache\FailedCache(false); (OC\Files\Cache\FailedCache) is incompatible with the return type declared by the interface OC\Files\Storage\Storage::getCache of type OC\Files\Cache\Cache.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
323
		}
324
		if (!$storage) {
325
			$storage = $this;
326
		}
327
		return new \OCA\Files_Sharing\Cache($storage, $this->storage, $this->sourceRootInfo);
328
	}
329
330
	public function getScanner($path = '', $storage = null) {
331
		if (!$storage) {
332
			$storage = $this;
333
		}
334
		return new \OCA\Files_Sharing\Scanner($storage);
335
	}
336
337 View Code Duplication
	public function getPropagator($storage = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
338
		if (isset($this->propagator)) {
339
			return $this->propagator;
340
		}
341
342
		if (!$storage) {
343
			$storage = $this;
344
		}
345
		$this->propagator = new \OCA\Files_Sharing\SharedPropagator($storage, \OC::$server->getDatabaseConnection());
346
		return $this->propagator;
347
	}
348
349
	public function getOwner($path) {
350
		return $this->superShare->getShareOwner();
351
	}
352
353
	/**
354
	 * unshare complete storage, also the grouped shares
355
	 *
356
	 * @return bool
357
	 */
358
	public function unshareStorage() {
359
		foreach ($this->groupedShares as $share) {
360
			\OC::$server->getShareManager()->deleteFromSelf($share, $this->user);
361
		}
362
		return true;
363
	}
364
365
	/**
366
	 * @param string $path
367
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
368
	 * @param \OCP\Lock\ILockingProvider $provider
369
	 * @throws \OCP\Lock\LockedException
370
	 */
371 View Code Duplication
	public function acquireLock($path, $type, ILockingProvider $provider) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
372
		/** @var \OCP\Files\Storage $targetStorage */
373
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
374
		$targetStorage->acquireLock($targetInternalPath, $type, $provider);
375
		// lock the parent folders of the owner when locking the share as recipient
376
		if ($path === '') {
377
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
378
			$this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
379
		}
380
	}
381
382
	/**
383
	 * @param string $path
384
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
385
	 * @param \OCP\Lock\ILockingProvider $provider
386
	 */
387 View Code Duplication
	public function releaseLock($path, $type, ILockingProvider $provider) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
388
		/** @var \OCP\Files\Storage $targetStorage */
389
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
390
		$targetStorage->releaseLock($targetInternalPath, $type, $provider);
391
		// unlock the parent folders of the owner when unlocking the share as recipient
392
		if ($path === '') {
393
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
394
			$this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
395
		}
396
	}
397
398
	/**
399
	 * @param string $path
400
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
401
	 * @param \OCP\Lock\ILockingProvider $provider
402
	 */
403
	public function changeLock($path, $type, ILockingProvider $provider) {
404
		/** @var \OCP\Files\Storage $targetStorage */
405
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
406
		$targetStorage->changeLock($targetInternalPath, $type, $provider);
407
	}
408
409
	/**
410
	 * @return array [ available, last_checked ]
411
	 */
412
	public function getAvailability() {
413
		// shares do not participate in availability logic
414
		return [
415
			'available' => true,
416
			'last_checked' => 0
417
		];
418
	}
419
420
	/**
421
	 * @param bool $available
422
	 */
423
	public function setAvailability($available) {
424
		// shares do not participate in availability logic
425
	}
426
427
	public function getSourceStorage() {
428
		return $this->getWrapperStorage();
429
	}
430
431
	public function getWrapperStorage() {
432
		$this->init();
433
		return $this->storage;
434
	}
435
436 View Code Duplication
	public function file_get_contents($path) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
437
		$info = [
438
			'target' => $this->getMountPoint() . '/' . $path,
439
			'source' => $this->getSourcePath($path),
440
		];
441
		\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
442
		return parent::file_get_contents($path);
443
	}
444
445 View Code Duplication
	public function file_put_contents($path, $data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
446
		$info = [
447
			'target' => $this->getMountPoint() . '/' . $path,
448
			'source' => $this->getSourcePath($path),
449
		];
450
		\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
451
		return parent::file_put_contents($path, $data);
452
	}
453
454
}
455