Completed
Push — stable10 ( 65dd17...7321ba )
by Björn
10:16
created

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