Passed
Push — master ( ccc0a5...5195be )
by Roeland
10:43 queued 11s
created

MountProvider::getMountsForUser()   B

Complexity

Conditions 9
Paths 13

Size

Total Lines 60
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 39
nc 13
nop 2
dl 0
loc 60
rs 7.7404
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Maxence Lange <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OCA\Files_Sharing;
29
30
use OC\Cache\CappedMemoryCache;
31
use OC\Files\View;
32
use OCP\Files\Config\IMountProvider;
33
use OCP\Files\Storage\IStorageFactory;
34
use OCP\IConfig;
35
use OCP\ILogger;
36
use OCP\IUser;
37
use OCP\Share\IManager;
38
use OCP\Share\IShare;
39
40
class MountProvider implements IMountProvider {
41
	/**
42
	 * @var \OCP\IConfig
43
	 */
44
	protected $config;
45
46
	/**
47
	 * @var IManager
48
	 */
49
	protected $shareManager;
50
51
	/**
52
	 * @var ILogger
53
	 */
54
	protected $logger;
55
56
	/**
57
	 * @param \OCP\IConfig $config
58
	 * @param IManager $shareManager
59
	 * @param ILogger $logger
60
	 */
61
	public function __construct(IConfig $config, IManager $shareManager, ILogger $logger) {
62
		$this->config = $config;
63
		$this->shareManager = $shareManager;
64
		$this->logger = $logger;
65
	}
66
67
68
	/**
69
	 * Get all mountpoints applicable for the user and check for shares where we need to update the etags
70
	 *
71
	 * @param \OCP\IUser $user
72
	 * @param \OCP\Files\Storage\IStorageFactory $storageFactory
73
	 * @return \OCP\Files\Mount\IMountPoint[]
74
	 */
75
	public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) {
76
77
		$shares = $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_USER, null, -1);
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

77
		$shares = $this->shareManager->getSharedWith($user->getUID(), /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_USER, null, -1);

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
78
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_GROUP, null, -1));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

78
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_GROUP, null, -1));

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
79
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_CIRCLE, null, -1));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

79
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_CIRCLE, null, -1));

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
80
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), \OCP\Share::SHARE_TYPE_ROOM, null, -1));
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

80
		$shares = array_merge($shares, $this->shareManager->getSharedWith($user->getUID(), /** @scrutinizer ignore-deprecated */ \OCP\Share::SHARE_TYPE_ROOM, null, -1));

This class constant 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 constant will be removed from the class and what other constant to use instead.

Loading history...
81
82
		// filter out excluded shares and group shares that includes self
83
		$shares = array_filter($shares, function (\OCP\Share\IShare $share) use ($user) {
84
			return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
85
		});
86
87
		$superShares = $this->buildSuperShares($shares, $user);
88
89
		$mounts = [];
90
		$view = new View('/' . $user->getUID() . '/files');
91
		$ownerViews = [];
92
		$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
93
		$foldersExistCache = new CappedMemoryCache();
94
		foreach ($superShares as $share) {
95
			try {
96
				/** @var \OCP\Share\IShare $parentShare */
97
				$parentShare = $share[0];
98
99
				if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED &&
100
					($parentShare->getShareType() === IShare::TYPE_GROUP ||
101
						$parentShare->getShareType() === IShare::TYPE_USERGROUP ||
102
						$parentShare->getShareType() === IShare::TYPE_USER)) {
103
					continue;
104
				}
105
106
				$owner = $parentShare->getShareOwner();
107
				if (!isset($ownerViews[$owner])) {
108
					$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
109
				}
110
				$mount = new SharedMount(
111
					'\OCA\Files_Sharing\SharedStorage',
112
					$mounts,
113
					[
114
						'user' => $user->getUID(),
115
						// parent share
116
						'superShare' => $parentShare,
117
						// children/component of the superShare
118
						'groupedShares' => $share[1],
119
						'ownerView' => $ownerViews[$owner],
120
						'sharingDisabledForUser' => $sharingDisabledForUser
121
					],
122
					$storageFactory,
123
					$view,
124
					$foldersExistCache
125
				);
126
				$mounts[$mount->getMountPoint()] = $mount;
127
			} catch (\Exception $e) {
128
				$this->logger->logException($e);
129
				$this->logger->error('Error while trying to create shared mount');
130
			}
131
		}
132
133
		// array_filter removes the null values from the array
134
		return array_values(array_filter($mounts));
135
	}
136
137
	/**
138
	 * Groups shares by path (nodeId) and target path
139
	 *
140
	 * @param \OCP\Share\IShare[] $shares
141
	 * @return \OCP\Share\IShare[][] array of grouped shares, each element in the
142
	 * array is a group which itself is an array of shares
143
	 */
144
	private function groupShares(array $shares) {
145
		$tmp = [];
146
147
		foreach ($shares as $share) {
148
			if (!isset($tmp[$share->getNodeId()])) {
149
				$tmp[$share->getNodeId()] = [];
150
			}
151
			$tmp[$share->getNodeId()][] = $share;
152
		}
153
154
		$result = [];
155
		// sort by stime, the super share will be based on the least recent share
156
		foreach ($tmp as &$tmp2) {
157
			@usort($tmp2, function($a, $b) {
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for usort(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

157
			/** @scrutinizer ignore-unhandled */ @usort($tmp2, function($a, $b) {

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
158
				if ($a->getShareTime() <= $b->getShareTime()) {
159
					return -1;
160
				}
161
				return 1;
162
			});
163
			$result[] = $tmp2;
164
		}
165
166
		return array_values($result);
167
	}
168
169
	/**
170
	 * Build super shares (virtual share) by grouping them by node id and target,
171
	 * then for each group compute the super share and return it along with the matching
172
	 * grouped shares. The most permissive permissions are used based on the permissions
173
	 * of all shares within the group.
174
	 *
175
	 * @param \OCP\Share\IShare[] $allShares
176
	 * @param \OCP\IUser $user user
177
	 * @return array Tuple of [superShare, groupedShares]
178
	 */
179
	private function buildSuperShares(array $allShares, \OCP\IUser $user) {
180
		$result = [];
181
182
		$groupedShares = $this->groupShares($allShares);
183
184
		/** @var \OCP\Share\IShare[] $shares */
185
		foreach ($groupedShares as $shares) {
186
			if (count($shares) === 0) {
187
				continue;
188
			}
189
190
			$superShare = $this->shareManager->newShare();
191
192
			// compute super share based on first entry of the group
193
			$superShare->setId($shares[0]->getId())
194
				->setShareOwner($shares[0]->getShareOwner())
195
				->setNodeId($shares[0]->getNodeId())
196
				->setShareType($shares[0]->getShareType())
197
				->setTarget($shares[0]->getTarget());
198
199
			// use most permissive permissions
200
			$permissions = 0;
201
			$status = IShare::STATUS_PENDING;
202
			foreach ($shares as $share) {
203
				$permissions |= $share->getPermissions();
204
				$status = max($status, $share->getStatus());
205
206
				if ($share->getTarget() !== $superShare->getTarget()) {
207
					// adjust target, for database consistency
208
					$share->setTarget($superShare->getTarget());
209
					try {
210
						$this->shareManager->moveShare($share, $user->getUID());
211
					} catch (\InvalidArgumentException $e) {
212
						// ignore as it is not important and we don't want to
213
						// block FS setup
214
215
						// the subsequent code anyway only uses the target of the
216
						// super share
217
218
						// such issue can usually happen when dealing with
219
						// null groups which usually appear with group backend
220
						// caching inconsistencies
221
						$this->logger->debug(
222
							'Could not adjust share target for share ' . $share->getId() . ' to make it consistent: ' . $e->getMessage(),
223
							['app' => 'files_sharing']
224
						);
225
					}
226
				}
227
				if (!is_null($share->getNodeCacheEntry())) {
228
					$superShare->setNodeCacheEntry($share->getNodeCacheEntry());
229
				}
230
			}
231
232
			$superShare->setPermissions($permissions)
233
				->setStatus($status);
234
235
			$result[] = [$superShare, $shares];
236
		}
237
238
		return $result;
239
	}
240
}
241