Completed
Push — stable10 ( 351d01...f4d4e3 )
by Morris
35:55 queued 23:48
created

Shared::getId()   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 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 OCP\Constants;
38
use OCP\Files\Cache\ICacheEntry;
39
use OCP\Files\NotFoundException;
40
use OCP\Files\Storage\IStorage;
41
use OCP\Lock\ILockingProvider;
42
43
/**
44
 * Convert target path to source path and pass the function call to the correct storage provider
45
 */
46
class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage {
47
	/** @var \OCP\Share\IShare */
48
	private $superShare;
49
50
	/** @var \OCP\Share\IShare[] */
51
	private $groupedShares;
52
53
	/**
54
	 * @var \OC\Files\View
55
	 */
56
	private $ownerView;
57
58
	private $initialized = false;
59
60
	/**
61
	 * @var ICacheEntry
62
	 */
63
	private $sourceRootInfo;
64
65
	/** @var string */
66
	private $user;
67
68
	/**
69
	 * @var \OCP\ILogger
70
	 */
71
	private $logger;
72
73
	public function __construct($arguments) {
74
		$this->ownerView = $arguments['ownerView'];
75
		$this->logger = \OC::$server->getLogger();
76
77
		$this->superShare = $arguments['superShare'];
78
		$this->groupedShares = $arguments['groupedShares'];
79
80
		$this->user = $arguments['user'];
81
82
		parent::__construct([
83
			'storage' => null,
84
			'root' => null,
85
		]);
86
	}
87
88
	private function init() {
89
		if ($this->initialized) {
90
			return;
91
		}
92
		$this->initialized = true;
93
		try {
94
			Filesystem::initMountPoints($this->superShare->getShareOwner());
95
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
96
			list($this->storage, $this->rootPath) = $this->ownerView->resolvePath($sourcePath);
97
			$this->sourceRootInfo = $this->storage->getCache()->get($this->rootPath);
98
		} catch (NotFoundException $e) {
99
			$this->storage = new FailedStorage(['exception' => $e]);
100
			$this->rootPath = '';
101
		} catch (\Exception $e) {
102
			$this->storage = new FailedStorage(['exception' => $e]);
103
			$this->rootPath = '';
104
			$this->logger->logException($e);
105
		}
106
	}
107
108
	/**
109
	 * @inheritdoc
110
	 */
111
	public function instanceOfStorage($class) {
112
		if (in_array($class, ['\OC\Files\Storage\Home', '\OC\Files\ObjectStore\HomeObjectStoreStorage'])) {
113
			return false;
114
		}
115
		return parent::instanceOfStorage($class);
116
	}
117
118
	/**
119
	 * @return string
120
	 */
121
	public function getShareId() {
122
		return $this->superShare->getId();
123
	}
124
125
	private function isValid() {
126
		$this->init();
127
		return $this->sourceRootInfo && ($this->sourceRootInfo->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
128
	}
129
130
	/**
131
	 * get id of the mount point
132
	 *
133
	 * @return string
134
	 */
135
	public function getId() {
136
		return 'shared::' . $this->getMountPoint();
137
	}
138
139
	/**
140
	 * Get the permissions granted for a shared file
141
	 *
142
	 * @param string $target Shared target file path
143
	 * @return int CRUDS permissions granted
144
	 */
145
	public function getPermissions($target = '') {
146
		if (!$this->isValid()) {
147
			return 0;
148
		}
149
		$permissions = $this->superShare->getPermissions();
150
		// part files and the mount point always have delete permissions
151
		if ($target === '' || pathinfo($target, PATHINFO_EXTENSION) === 'part') {
152
			$permissions |= \OCP\Constants::PERMISSION_DELETE;
153
		}
154
155
		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...
156
			$permissions &= ~\OCP\Constants::PERMISSION_SHARE;
157
		}
158
159
		return $permissions;
160
	}
161
162
	public function isCreatable($path) {
163
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_CREATE);
164
	}
165
166
	public function isReadable($path) {
167
		if (!$this->isValid()) {
168
			return false;
169
		}
170
		if (!$this->file_exists($path)) {
171
			return false;
172
		}
173
		list($storage, $internalPath) = $this->resolvePath($path);
174
		return $storage->isReadable($internalPath);
175
	}
176
177
	public function isUpdatable($path) {
178
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_UPDATE);
179
	}
180
181
	public function isDeletable($path) {
182
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_DELETE);
183
	}
184
185
	public function isSharable($path) {
186
		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...
187
			return false;
188
		}
189
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE);
190
	}
191
192
	public function fopen($path, $mode) {
193
		if ($source = $this->getSourcePath($path)) {
194
			switch ($mode) {
195
				case 'r+':
196
				case 'rb+':
197
				case 'w+':
198
				case 'wb+':
199
				case 'x+':
200
				case 'xb+':
201
				case 'a+':
202
				case 'ab+':
203
				case 'w':
204
				case 'wb':
205
				case 'x':
206
				case 'xb':
207
				case 'a':
208
				case 'ab':
209
					$creatable = $this->isCreatable($path);
210
					$updatable = $this->isUpdatable($path);
211
					// if neither permissions given, no need to continue
212
					if (!$creatable && !$updatable) {
213
						return false;
214
					}
215
216
					$exists = $this->file_exists($path);
217
					// if a file exists, updatable permissions are required
218
					if ($exists && !$updatable) {
219
						return false;
220
					}
221
222
					// part file is allowed if !$creatable but the final file is $updatable
223
					if (pathinfo($path, PATHINFO_EXTENSION) !== 'part') {
224
						if (!$exists && !$creatable) {
225
							return false;
226
						}
227
					}
228
			}
229
			$info = array(
230
				'target' => $this->getMountPoint() . $path,
231
				'source' => $source,
232
				'mode' => $mode,
233
			);
234
			\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
235
			return parent::fopen($path, $mode);
236
		}
237
		return false;
238
	}
239
240
	/**
241
	 * see http://php.net/manual/en/function.rename.php
242
	 *
243
	 * @param string $path1
244
	 * @param string $path2
245
	 * @return bool
246
	 */
247
	public function rename($path1, $path2) {
248
		$isPartFile = pathinfo($path1, PATHINFO_EXTENSION) === 'part';
249
		$targetExists = $this->file_exists($path2);
250
		$sameFodler = dirname($path1) === dirname($path2);
251
252
		if ($targetExists || ($sameFodler && !$isPartFile)) {
253
			if (!$this->isUpdatable('')) {
254
				return false;
255
			}
256
		} else {
257
			if (!$this->isCreatable('')) {
258
				return false;
259
			}
260
		}
261
262
		return parent::rename($path1, $path2);
263
	}
264
265
	/**
266
	 * return mount point of share, relative to data/user/files
267
	 *
268
	 * @return string
269
	 */
270
	public function getMountPoint() {
271
		return $this->superShare->getTarget();
272
	}
273
274
	/**
275
	 * @param string $path
276
	 */
277
	public function setMountPoint($path) {
278
		$this->superShare->setTarget($path);
279
280
		foreach ($this->groupedShares as $share) {
281
			$share->setTarget($path);
282
		}
283
	}
284
285
	/**
286
	 * get the user who shared the file
287
	 *
288
	 * @return string
289
	 */
290
	public function getSharedFrom() {
291
		return $this->superShare->getShareOwner();
292
	}
293
294
	/**
295
	 * @return \OCP\Share\IShare
296
	 */
297
	public function getShare() {
298
		return $this->superShare;
299
	}
300
301
	/**
302
	 * return share type, can be "file" or "folder"
303
	 *
304
	 * @return string
305
	 */
306
	public function getItemType() {
307
		return $this->superShare->getNodeType();
308
	}
309
310
	public function getCache($path = '', $storage = null) {
311
		$this->init();
312
		if (is_null($this->storage) || $this->storage instanceof FailedStorage) {
313
			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...
314
		}
315
		if (!$storage) {
316
			$storage = $this;
317
		}
318
		return new \OCA\Files_Sharing\Cache($storage, $this->storage, $this->sourceRootInfo);
319
	}
320
321
	public function getScanner($path = '', $storage = null) {
322
		if (!$storage) {
323
			$storage = $this;
324
		}
325
		return new \OCA\Files_Sharing\Scanner($storage);
326
	}
327
328 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...
329
		if (isset($this->propagator)) {
330
			return $this->propagator;
331
		}
332
333
		if (!$storage) {
334
			$storage = $this;
335
		}
336
		$this->propagator = new \OCA\Files_Sharing\SharedPropagator($storage, \OC::$server->getDatabaseConnection());
337
		return $this->propagator;
338
	}
339
340
	public function getOwner($path) {
341
		return $this->superShare->getShareOwner();
342
	}
343
344
	/**
345
	 * unshare complete storage, also the grouped shares
346
	 *
347
	 * @return bool
348
	 */
349
	public function unshareStorage() {
350
		foreach ($this->groupedShares as $share) {
351
			\OC::$server->getShareManager()->deleteFromSelf($share, $this->user);
352
		}
353
		return true;
354
	}
355
356
	/**
357
	 * @param string $path
358
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
359
	 * @param \OCP\Lock\ILockingProvider $provider
360
	 * @throws \OCP\Lock\LockedException
361
	 */
362 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...
363
		/** @var \OCP\Files\Storage $targetStorage */
364
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
365
		$targetStorage->acquireLock($targetInternalPath, $type, $provider);
366
		// lock the parent folders of the owner when locking the share as recipient
367
		if ($path === '') {
368
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
369
			$this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
370
		}
371
	}
372
373
	/**
374
	 * @param string $path
375
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
376
	 * @param \OCP\Lock\ILockingProvider $provider
377
	 */
378 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...
379
		/** @var \OCP\Files\Storage $targetStorage */
380
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
381
		$targetStorage->releaseLock($targetInternalPath, $type, $provider);
382
		// unlock the parent folders of the owner when unlocking the share as recipient
383
		if ($path === '') {
384
			$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
385
			$this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
386
		}
387
	}
388
389
	/**
390
	 * @param string $path
391
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
392
	 * @param \OCP\Lock\ILockingProvider $provider
393
	 */
394
	public function changeLock($path, $type, ILockingProvider $provider) {
395
		/** @var \OCP\Files\Storage $targetStorage */
396
		list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
397
		$targetStorage->changeLock($targetInternalPath, $type, $provider);
398
	}
399
400
	/**
401
	 * @return array [ available, last_checked ]
402
	 */
403
	public function getAvailability() {
404
		// shares do not participate in availability logic
405
		return [
406
			'available' => true,
407
			'last_checked' => 0
408
		];
409
	}
410
411
	/**
412
	 * @param bool $available
413
	 */
414
	public function setAvailability($available) {
415
		// shares do not participate in availability logic
416
	}
417
418
	public function getSourceStorage() {
419
		return $this->getWrapperStorage();
420
	}
421
422
	public function getWrapperStorage() {
423
		$this->init();
424
		return $this->storage;
425
	}
426
427 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...
428
		$info = [
429
			'target' => $this->getMountPoint() . '/' . $path,
430
			'source' => $this->getSourcePath($path),
431
		];
432
		\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
433
		return parent::file_get_contents($path);
434
	}
435
436 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...
437
		$info = [
438
			'target' => $this->getMountPoint() . '/' . $path,
439
			'source' => $this->getSourcePath($path),
440
		];
441
		\OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
442
		return parent::file_put_contents($path, $data);
443
	}
444
445
}
446