|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
|
4
|
|
|
* |
|
5
|
|
|
* @author Björn Schießle <[email protected]> |
|
6
|
|
|
* @author Joas Schilling <[email protected]> |
|
7
|
|
|
* @author Roeland Jago Douma <[email protected]> |
|
8
|
|
|
* @author Thomas Müller <[email protected]> |
|
9
|
|
|
* |
|
10
|
|
|
* @license AGPL-3.0 |
|
11
|
|
|
* |
|
12
|
|
|
* This code is free software: you can redistribute it and/or modify |
|
13
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
|
14
|
|
|
* as published by the Free Software Foundation. |
|
15
|
|
|
* |
|
16
|
|
|
* This program is distributed in the hope that it will be useful, |
|
17
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19
|
|
|
* GNU Affero General Public License for more details. |
|
20
|
|
|
* |
|
21
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
|
22
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
|
23
|
|
|
* |
|
24
|
|
|
*/ |
|
25
|
|
|
namespace OCA\Files_Sharing\Controller; |
|
26
|
|
|
|
|
27
|
|
|
use OCP\AppFramework\Http\DataResponse; |
|
28
|
|
|
use OCP\AppFramework\OCS\OCSBadRequestException; |
|
29
|
|
|
use OCP\AppFramework\OCSController; |
|
30
|
|
|
use OCP\Contacts\IManager; |
|
31
|
|
|
use OCP\Federation\ICloudIdManager; |
|
32
|
|
|
use OCP\Http\Client\IClientService; |
|
33
|
|
|
use OCP\IGroup; |
|
34
|
|
|
use OCP\IGroupManager; |
|
35
|
|
|
use OCP\ILogger; |
|
36
|
|
|
use OCP\IRequest; |
|
37
|
|
|
use OCP\IUser; |
|
38
|
|
|
use OCP\IUserManager; |
|
39
|
|
|
use OCP\IConfig; |
|
40
|
|
|
use OCP\IUserSession; |
|
41
|
|
|
use OCP\IURLGenerator; |
|
42
|
|
|
use OCP\Share; |
|
43
|
|
|
|
|
44
|
|
|
class ShareesAPIController extends OCSController { |
|
45
|
|
|
|
|
46
|
|
|
/** @var IGroupManager */ |
|
47
|
|
|
protected $groupManager; |
|
48
|
|
|
|
|
49
|
|
|
/** @var IUserManager */ |
|
50
|
|
|
protected $userManager; |
|
51
|
|
|
|
|
52
|
|
|
/** @var IManager */ |
|
53
|
|
|
protected $contactsManager; |
|
54
|
|
|
|
|
55
|
|
|
/** @var IConfig */ |
|
56
|
|
|
protected $config; |
|
57
|
|
|
|
|
58
|
|
|
/** @var IUserSession */ |
|
59
|
|
|
protected $userSession; |
|
60
|
|
|
|
|
61
|
|
|
/** @var IURLGenerator */ |
|
62
|
|
|
protected $urlGenerator; |
|
63
|
|
|
|
|
64
|
|
|
/** @var ILogger */ |
|
65
|
|
|
protected $logger; |
|
66
|
|
|
|
|
67
|
|
|
/** @var \OCP\Share\IManager */ |
|
68
|
|
|
protected $shareManager; |
|
69
|
|
|
|
|
70
|
|
|
/** @var IClientService */ |
|
71
|
|
|
protected $clientService; |
|
72
|
|
|
|
|
73
|
|
|
/** @var ICloudIdManager */ |
|
74
|
|
|
protected $cloudIdManager; |
|
75
|
|
|
|
|
76
|
|
|
/** @var bool */ |
|
77
|
|
|
protected $shareWithGroupOnly = false; |
|
78
|
|
|
|
|
79
|
|
|
/** @var bool */ |
|
80
|
|
|
protected $shareeEnumeration = true; |
|
81
|
|
|
|
|
82
|
|
|
/** @var int */ |
|
83
|
|
|
protected $offset = 0; |
|
84
|
|
|
|
|
85
|
|
|
/** @var int */ |
|
86
|
|
|
protected $limit = 10; |
|
87
|
|
|
|
|
88
|
|
|
/** @var array */ |
|
89
|
|
|
protected $result = [ |
|
90
|
|
|
'exact' => [ |
|
91
|
|
|
'users' => [], |
|
92
|
|
|
'groups' => [], |
|
93
|
|
|
'remotes' => [], |
|
94
|
|
|
'emails' => [], |
|
95
|
|
|
'circles' => [], |
|
96
|
|
|
], |
|
97
|
|
|
'users' => [], |
|
98
|
|
|
'groups' => [], |
|
99
|
|
|
'remotes' => [], |
|
100
|
|
|
'emails' => [], |
|
101
|
|
|
'lookup' => [], |
|
102
|
|
|
'circles' => [], |
|
103
|
|
|
]; |
|
104
|
|
|
|
|
105
|
|
|
protected $reachedEndFor = []; |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* @param string $appName |
|
109
|
|
|
* @param IRequest $request |
|
110
|
|
|
* @param IGroupManager $groupManager |
|
111
|
|
|
* @param IUserManager $userManager |
|
112
|
|
|
* @param IManager $contactsManager |
|
113
|
|
|
* @param IConfig $config |
|
114
|
|
|
* @param IUserSession $userSession |
|
115
|
|
|
* @param IURLGenerator $urlGenerator |
|
116
|
|
|
* @param ILogger $logger |
|
117
|
|
|
* @param \OCP\Share\IManager $shareManager |
|
118
|
|
|
* @param IClientService $clientService |
|
119
|
|
|
* @param ICloudIdManager $cloudIdManager |
|
120
|
|
|
*/ |
|
121
|
|
View Code Duplication |
public function __construct($appName, |
|
|
|
|
|
|
122
|
|
|
IRequest $request, |
|
123
|
|
|
IGroupManager $groupManager, |
|
124
|
|
|
IUserManager $userManager, |
|
125
|
|
|
IManager $contactsManager, |
|
126
|
|
|
IConfig $config, |
|
127
|
|
|
IUserSession $userSession, |
|
128
|
|
|
IURLGenerator $urlGenerator, |
|
129
|
|
|
ILogger $logger, |
|
130
|
|
|
\OCP\Share\IManager $shareManager, |
|
131
|
|
|
IClientService $clientService, |
|
132
|
|
|
ICloudIdManager $cloudIdManager |
|
133
|
|
|
) { |
|
134
|
|
|
parent::__construct($appName, $request); |
|
135
|
|
|
|
|
136
|
|
|
$this->groupManager = $groupManager; |
|
137
|
|
|
$this->userManager = $userManager; |
|
138
|
|
|
$this->contactsManager = $contactsManager; |
|
139
|
|
|
$this->config = $config; |
|
140
|
|
|
$this->userSession = $userSession; |
|
141
|
|
|
$this->urlGenerator = $urlGenerator; |
|
142
|
|
|
$this->logger = $logger; |
|
143
|
|
|
$this->shareManager = $shareManager; |
|
144
|
|
|
$this->clientService = $clientService; |
|
145
|
|
|
$this->cloudIdManager = $cloudIdManager; |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* @param string $search |
|
150
|
|
|
*/ |
|
151
|
|
|
protected function getUsers($search) { |
|
152
|
|
|
$this->result['users'] = $this->result['exact']['users'] = $users = []; |
|
153
|
|
|
|
|
154
|
|
|
$userGroups = []; |
|
155
|
|
|
if ($this->shareWithGroupOnly) { |
|
156
|
|
|
// Search in all the groups this user is part of |
|
157
|
|
|
$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
|
|
|
|
|
158
|
|
|
foreach ($userGroups as $userGroup) { |
|
159
|
|
|
$usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset); |
|
160
|
|
|
foreach ($usersTmp as $uid => $userDisplayName) { |
|
161
|
|
|
$users[$uid] = $userDisplayName; |
|
162
|
|
|
} |
|
163
|
|
|
} |
|
164
|
|
|
} else { |
|
165
|
|
|
// Search in all users |
|
166
|
|
|
$usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset); |
|
167
|
|
|
|
|
168
|
|
|
foreach ($usersTmp as $user) { |
|
169
|
|
|
$users[$user->getUID()] = $user->getDisplayName(); |
|
170
|
|
|
} |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
View Code Duplication |
if (!$this->shareeEnumeration || sizeof($users) < $this->limit) { |
|
174
|
|
|
$this->reachedEndFor[] = 'users'; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
$foundUserById = false; |
|
178
|
|
|
$lowerSearch = strtolower($search); |
|
179
|
|
|
foreach ($users as $uid => $userDisplayName) { |
|
180
|
|
|
if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { |
|
181
|
|
|
if (strtolower($uid) === $lowerSearch) { |
|
182
|
|
|
$foundUserById = true; |
|
183
|
|
|
} |
|
184
|
|
|
$this->result['exact']['users'][] = [ |
|
185
|
|
|
'label' => $userDisplayName, |
|
186
|
|
|
'value' => [ |
|
187
|
|
|
'shareType' => Share::SHARE_TYPE_USER, |
|
188
|
|
|
'shareWith' => $uid, |
|
189
|
|
|
], |
|
190
|
|
|
]; |
|
191
|
|
View Code Duplication |
} else { |
|
192
|
|
|
$this->result['users'][] = [ |
|
193
|
|
|
'label' => $userDisplayName, |
|
194
|
|
|
'value' => [ |
|
195
|
|
|
'shareType' => Share::SHARE_TYPE_USER, |
|
196
|
|
|
'shareWith' => $uid, |
|
197
|
|
|
], |
|
198
|
|
|
]; |
|
199
|
|
|
} |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
if ($this->offset === 0 && !$foundUserById) { |
|
203
|
|
|
// On page one we try if the search result has a direct hit on the |
|
204
|
|
|
// user id and if so, we add that to the exact match list |
|
205
|
|
|
$user = $this->userManager->get($search); |
|
206
|
|
|
if ($user instanceof IUser) { |
|
207
|
|
|
$addUser = true; |
|
208
|
|
|
|
|
209
|
|
|
if ($this->shareWithGroupOnly) { |
|
210
|
|
|
// Only add, if we have a common group |
|
211
|
|
|
$commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user)); |
|
212
|
|
|
$addUser = !empty($commonGroups); |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
if ($addUser) { |
|
216
|
|
|
array_push($this->result['exact']['users'], [ |
|
217
|
|
|
'label' => $user->getDisplayName(), |
|
218
|
|
|
'value' => [ |
|
219
|
|
|
'shareType' => Share::SHARE_TYPE_USER, |
|
220
|
|
|
'shareWith' => $user->getUID(), |
|
221
|
|
|
], |
|
222
|
|
|
]); |
|
223
|
|
|
} |
|
224
|
|
|
} |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
if (!$this->shareeEnumeration) { |
|
228
|
|
|
$this->result['users'] = []; |
|
229
|
|
|
} |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* @param string $search |
|
234
|
|
|
*/ |
|
235
|
|
|
protected function getGroups($search) { |
|
236
|
|
|
$this->result['groups'] = $this->result['exact']['groups'] = []; |
|
237
|
|
|
|
|
238
|
|
|
$groups = $this->groupManager->search($search, $this->limit, $this->offset); |
|
239
|
|
|
$groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); |
|
240
|
|
|
|
|
241
|
|
View Code Duplication |
if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) { |
|
242
|
|
|
$this->reachedEndFor[] = 'groups'; |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
$userGroups = []; |
|
246
|
|
|
if (!empty($groups) && $this->shareWithGroupOnly) { |
|
247
|
|
|
// Intersect all the groups that match with the groups this user is a member of |
|
248
|
|
|
$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser()); |
|
249
|
|
|
$userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups); |
|
250
|
|
|
$groupIds = array_intersect($groupIds, $userGroups); |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
$lowerSearch = strtolower($search); |
|
254
|
|
|
foreach ($groups as $group) { |
|
255
|
|
|
// FIXME: use a more efficient approach |
|
256
|
|
|
$gid = $group->getGID(); |
|
257
|
|
|
if (!in_array($gid, $groupIds)) { |
|
258
|
|
|
continue; |
|
259
|
|
|
} |
|
260
|
|
|
if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) { |
|
261
|
|
|
$this->result['exact']['groups'][] = [ |
|
262
|
|
|
'label' => $group->getDisplayName(), |
|
263
|
|
|
'value' => [ |
|
264
|
|
|
'shareType' => Share::SHARE_TYPE_GROUP, |
|
265
|
|
|
'shareWith' => $gid, |
|
266
|
|
|
], |
|
267
|
|
|
]; |
|
268
|
|
View Code Duplication |
} else { |
|
269
|
|
|
$this->result['groups'][] = [ |
|
270
|
|
|
'label' => $group->getDisplayName(), |
|
271
|
|
|
'value' => [ |
|
272
|
|
|
'shareType' => Share::SHARE_TYPE_GROUP, |
|
273
|
|
|
'shareWith' => $gid, |
|
274
|
|
|
], |
|
275
|
|
|
]; |
|
276
|
|
|
} |
|
277
|
|
|
} |
|
278
|
|
|
|
|
279
|
|
|
if ($this->offset === 0 && empty($this->result['exact']['groups'])) { |
|
280
|
|
|
// On page one we try if the search result has a direct hit on the |
|
281
|
|
|
// user id and if so, we add that to the exact match list |
|
282
|
|
|
$group = $this->groupManager->get($search); |
|
283
|
|
|
if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) { |
|
284
|
|
|
array_push($this->result['exact']['groups'], [ |
|
285
|
|
|
'label' => $group->getDisplayName(), |
|
286
|
|
|
'value' => [ |
|
287
|
|
|
'shareType' => Share::SHARE_TYPE_GROUP, |
|
288
|
|
|
'shareWith' => $group->getGID(), |
|
289
|
|
|
], |
|
290
|
|
|
]); |
|
291
|
|
|
} |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
if (!$this->shareeEnumeration) { |
|
295
|
|
|
$this->result['groups'] = []; |
|
296
|
|
|
} |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
|
|
300
|
|
|
/** |
|
301
|
|
|
* @param string $search |
|
302
|
|
|
*/ |
|
303
|
|
|
protected function getCircles($search) { |
|
304
|
|
|
$this->result['circles'] = $this->result['exact']['circles'] = []; |
|
305
|
|
|
|
|
306
|
|
|
$result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset); |
|
307
|
|
|
if (array_key_exists('circles', $result['exact'])) { |
|
308
|
|
|
$this->result['exact']['circles'] = $result['exact']['circles']; |
|
309
|
|
|
} |
|
310
|
|
|
if (array_key_exists('circles', $result)) { |
|
311
|
|
|
$this->result['circles'] = $result['circles']; |
|
312
|
|
|
} |
|
313
|
|
|
} |
|
314
|
|
|
|
|
315
|
|
|
|
|
316
|
|
|
/** |
|
317
|
|
|
* @param string $search |
|
318
|
|
|
* @return array |
|
319
|
|
|
*/ |
|
320
|
|
|
protected function getRemote($search) { |
|
321
|
|
|
$result = ['results' => [], 'exact' => []]; |
|
322
|
|
|
|
|
323
|
|
|
// Search in contacts |
|
324
|
|
|
//@todo Pagination missing |
|
325
|
|
|
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); |
|
326
|
|
|
$result['exactIdMatch'] = false; |
|
327
|
|
|
foreach ($addressBookContacts as $contact) { |
|
328
|
|
|
if (isset($contact['isLocalSystemBook'])) { |
|
329
|
|
|
continue; |
|
330
|
|
|
} |
|
331
|
|
|
if (isset($contact['CLOUD'])) { |
|
332
|
|
|
$cloudIds = $contact['CLOUD']; |
|
333
|
|
|
if (!is_array($cloudIds)) { |
|
334
|
|
|
$cloudIds = [$cloudIds]; |
|
335
|
|
|
} |
|
336
|
|
|
$lowerSearch = strtolower($search); |
|
337
|
|
|
foreach ($cloudIds as $cloudId) { |
|
338
|
|
|
list(, $serverUrl) = $this->splitUserRemote($cloudId); |
|
339
|
|
|
if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) { |
|
340
|
|
|
if (strtolower($cloudId) === $lowerSearch) { |
|
341
|
|
|
$result['exactIdMatch'] = true; |
|
342
|
|
|
} |
|
343
|
|
|
$result['exact'][] = [ |
|
344
|
|
|
'label' => $contact['FN'] . " ($cloudId)", |
|
345
|
|
|
'value' => [ |
|
346
|
|
|
'shareType' => Share::SHARE_TYPE_REMOTE, |
|
347
|
|
|
'shareWith' => $cloudId, |
|
348
|
|
|
'server' => $serverUrl, |
|
349
|
|
|
], |
|
350
|
|
|
]; |
|
351
|
|
View Code Duplication |
} else { |
|
352
|
|
|
$result['results'][] = [ |
|
353
|
|
|
'label' => $contact['FN'] . " ($cloudId)", |
|
354
|
|
|
'value' => [ |
|
355
|
|
|
'shareType' => Share::SHARE_TYPE_REMOTE, |
|
356
|
|
|
'shareWith' => $cloudId, |
|
357
|
|
|
'server' => $serverUrl, |
|
358
|
|
|
], |
|
359
|
|
|
]; |
|
360
|
|
|
} |
|
361
|
|
|
} |
|
362
|
|
|
} |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
if (!$this->shareeEnumeration) { |
|
366
|
|
|
$result['results'] = []; |
|
367
|
|
|
} |
|
368
|
|
|
|
|
369
|
|
|
if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) { |
|
370
|
|
|
$result['exact'][] = [ |
|
371
|
|
|
'label' => $search, |
|
372
|
|
|
'value' => [ |
|
373
|
|
|
'shareType' => Share::SHARE_TYPE_REMOTE, |
|
374
|
|
|
'shareWith' => $search, |
|
375
|
|
|
], |
|
376
|
|
|
]; |
|
377
|
|
|
} |
|
378
|
|
|
|
|
379
|
|
|
$this->reachedEndFor[] = 'remotes'; |
|
380
|
|
|
|
|
381
|
|
|
return $result; |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
|
|
/** |
|
385
|
|
|
* split user and remote from federated cloud id |
|
386
|
|
|
* |
|
387
|
|
|
* @param string $address federated share address |
|
388
|
|
|
* @return array [user, remoteURL] |
|
389
|
|
|
* @throws \Exception |
|
390
|
|
|
*/ |
|
391
|
|
View Code Duplication |
public function splitUserRemote($address) { |
|
|
|
|
|
|
392
|
|
|
try { |
|
393
|
|
|
$cloudId = $this->cloudIdManager->resolveCloudId($address); |
|
394
|
|
|
return [$cloudId->getUser(), $cloudId->getRemote()]; |
|
395
|
|
|
} catch (\InvalidArgumentException $e) { |
|
396
|
|
|
throw new \Exception('Invalid Federated Cloud ID', 0, $e); |
|
397
|
|
|
} |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
/** |
|
401
|
|
|
* Strips away a potential file names and trailing slashes: |
|
402
|
|
|
* - http://localhost |
|
403
|
|
|
* - http://localhost/ |
|
404
|
|
|
* - http://localhost/index.php |
|
405
|
|
|
* - http://localhost/index.php/s/{shareToken} |
|
406
|
|
|
* |
|
407
|
|
|
* all return: http://localhost |
|
408
|
|
|
* |
|
409
|
|
|
* @param string $remote |
|
410
|
|
|
* @return string |
|
411
|
|
|
*/ |
|
412
|
|
View Code Duplication |
protected function fixRemoteURL($remote) { |
|
|
|
|
|
|
413
|
|
|
$remote = str_replace('\\', '/', $remote); |
|
414
|
|
|
if ($fileNamePosition = strpos($remote, '/index.php')) { |
|
415
|
|
|
$remote = substr($remote, 0, $fileNamePosition); |
|
416
|
|
|
} |
|
417
|
|
|
$remote = rtrim($remote, '/'); |
|
418
|
|
|
|
|
419
|
|
|
return $remote; |
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
/** |
|
423
|
|
|
* @NoAdminRequired |
|
424
|
|
|
* |
|
425
|
|
|
* @param string $search |
|
426
|
|
|
* @param string $itemType |
|
427
|
|
|
* @param int $page |
|
428
|
|
|
* @param int $perPage |
|
429
|
|
|
* @param int|int[] $shareType |
|
430
|
|
|
* @param bool $lookup |
|
431
|
|
|
* @return DataResponse |
|
432
|
|
|
* @throws OCSBadRequestException |
|
433
|
|
|
*/ |
|
434
|
|
|
public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) { |
|
435
|
|
|
|
|
436
|
|
|
// only search for string larger than a given threshold |
|
437
|
|
|
$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0); |
|
438
|
|
|
if (strlen($search) < $threshold) { |
|
439
|
|
|
return new DataResponse($this->result); |
|
440
|
|
|
} |
|
441
|
|
|
|
|
442
|
|
|
// never return more than the max. number of results configured in the config.php |
|
443
|
|
|
$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0); |
|
444
|
|
|
if ($maxResults > 0) { |
|
445
|
|
|
$perPage = min($perPage, $maxResults); |
|
446
|
|
|
} |
|
447
|
|
|
if ($perPage <= 0) { |
|
448
|
|
|
throw new OCSBadRequestException('Invalid perPage argument'); |
|
449
|
|
|
} |
|
450
|
|
|
if ($page <= 0) { |
|
451
|
|
|
throw new OCSBadRequestException('Invalid page'); |
|
452
|
|
|
} |
|
453
|
|
|
|
|
454
|
|
|
$shareTypes = [ |
|
455
|
|
|
Share::SHARE_TYPE_USER, |
|
456
|
|
|
]; |
|
457
|
|
|
|
|
458
|
|
|
if ($itemType === 'file' || $itemType === 'folder') { |
|
459
|
|
|
if ($this->shareManager->allowGroupSharing()) { |
|
460
|
|
|
$shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
461
|
|
|
} |
|
462
|
|
|
|
|
463
|
|
|
if ($this->isRemoteSharingAllowed($itemType)) { |
|
464
|
|
|
$shareTypes[] = Share::SHARE_TYPE_REMOTE; |
|
465
|
|
|
} |
|
466
|
|
|
|
|
467
|
|
|
if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) { |
|
468
|
|
|
$shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
469
|
|
|
} |
|
470
|
|
|
} else { |
|
471
|
|
|
$shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
472
|
|
|
$shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
473
|
|
|
} |
|
474
|
|
|
|
|
475
|
|
|
if (\OCP\App::isEnabled('circles')) { |
|
476
|
|
|
$shareTypes[] = Share::SHARE_TYPE_CIRCLE; |
|
477
|
|
|
} |
|
478
|
|
|
|
|
479
|
|
|
if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { |
|
480
|
|
|
$shareTypes = array_intersect($shareTypes, $_GET['shareType']); |
|
481
|
|
|
sort($shareTypes); |
|
482
|
|
|
} else if (is_numeric($shareType)) { |
|
483
|
|
|
$shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
484
|
|
|
sort($shareTypes); |
|
485
|
|
|
} |
|
486
|
|
|
|
|
487
|
|
|
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
488
|
|
|
$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; |
|
489
|
|
|
$this->limit = (int) $perPage; |
|
490
|
|
|
$this->offset = $perPage * ($page - 1); |
|
491
|
|
|
|
|
492
|
|
|
return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup); |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/** |
|
496
|
|
|
* Method to get out the static call for better testing |
|
497
|
|
|
* |
|
498
|
|
|
* @param string $itemType |
|
499
|
|
|
* @return bool |
|
500
|
|
|
*/ |
|
501
|
|
|
protected function isRemoteSharingAllowed($itemType) { |
|
502
|
|
|
try { |
|
503
|
|
|
$backend = Share::getBackend($itemType); |
|
504
|
|
|
return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE); |
|
505
|
|
|
} catch (\Exception $e) { |
|
506
|
|
|
return false; |
|
507
|
|
|
} |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
/** |
|
511
|
|
|
* Testable search function that does not need globals |
|
512
|
|
|
* |
|
513
|
|
|
* @param string $search |
|
514
|
|
|
* @param string $itemType |
|
515
|
|
|
* @param array $shareTypes |
|
516
|
|
|
* @param int $page |
|
517
|
|
|
* @param int $perPage |
|
518
|
|
|
* @param bool $lookup |
|
519
|
|
|
* @return DataResponse |
|
520
|
|
|
* @throws OCSBadRequestException |
|
521
|
|
|
*/ |
|
522
|
|
|
protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) { |
|
523
|
|
|
// Verify arguments |
|
524
|
|
|
if ($itemType === null) { |
|
525
|
|
|
throw new OCSBadRequestException('Missing itemType'); |
|
526
|
|
|
} |
|
527
|
|
|
|
|
528
|
|
|
// Get users |
|
529
|
|
|
if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) { |
|
530
|
|
|
$this->getUsers($search); |
|
531
|
|
|
} |
|
532
|
|
|
|
|
533
|
|
|
// Get groups |
|
534
|
|
|
if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) { |
|
535
|
|
|
$this->getGroups($search); |
|
536
|
|
|
} |
|
537
|
|
|
|
|
538
|
|
|
// Get circles |
|
539
|
|
|
if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) { |
|
540
|
|
|
$this->getCircles($search); |
|
541
|
|
|
} |
|
542
|
|
|
|
|
543
|
|
|
|
|
544
|
|
|
// Get remote |
|
545
|
|
|
$remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
546
|
|
|
if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) { |
|
547
|
|
|
$remoteResults = $this->getRemote($search); |
|
548
|
|
|
} |
|
549
|
|
|
|
|
550
|
|
|
// Get emails |
|
551
|
|
|
$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
552
|
|
|
if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) { |
|
553
|
|
|
$mailResults = $this->getEmail($search); |
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
// Get from lookup server |
|
557
|
|
|
if ($lookup) { |
|
558
|
|
|
$this->getLookup($search); |
|
559
|
|
|
} |
|
560
|
|
|
|
|
561
|
|
|
// if we have a exact match, either for the federated cloud id or for the |
|
562
|
|
|
// email address we only return the exact match. It is highly unlikely |
|
563
|
|
|
// that the exact same email address and federated cloud id exists |
|
564
|
|
|
if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) { |
|
565
|
|
|
$this->result['emails'] = $mailResults['results']; |
|
566
|
|
|
$this->result['exact']['emails'] = $mailResults['exact']; |
|
567
|
|
|
} else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) { |
|
568
|
|
|
$this->result['remotes'] = $remoteResults['results']; |
|
569
|
|
|
$this->result['exact']['remotes'] = $remoteResults['exact']; |
|
570
|
|
|
} else { |
|
571
|
|
|
$this->result['remotes'] = $remoteResults['results']; |
|
572
|
|
|
$this->result['exact']['remotes'] = $remoteResults['exact']; |
|
573
|
|
|
$this->result['emails'] = $mailResults['results']; |
|
574
|
|
|
$this->result['exact']['emails'] = $mailResults['exact']; |
|
575
|
|
|
} |
|
576
|
|
|
|
|
577
|
|
|
$response = new DataResponse($this->result); |
|
578
|
|
|
|
|
579
|
|
|
if (sizeof($this->reachedEndFor) < 3) { |
|
580
|
|
|
$response->addHeader('Link', $this->getPaginationLink($page, [ |
|
581
|
|
|
'search' => $search, |
|
582
|
|
|
'itemType' => $itemType, |
|
583
|
|
|
'shareType' => $shareTypes, |
|
584
|
|
|
'perPage' => $perPage, |
|
585
|
|
|
])); |
|
586
|
|
|
} |
|
587
|
|
|
|
|
588
|
|
|
return $response; |
|
589
|
|
|
} |
|
590
|
|
|
|
|
591
|
|
|
/** |
|
592
|
|
|
* @param string $search |
|
593
|
|
|
* @return array |
|
594
|
|
|
*/ |
|
595
|
|
|
protected function getEmail($search) { |
|
596
|
|
|
$result = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
597
|
|
|
|
|
598
|
|
|
// Search in contacts |
|
599
|
|
|
//@todo Pagination missing |
|
600
|
|
|
$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); |
|
601
|
|
|
$lowerSearch = strtolower($search); |
|
602
|
|
|
foreach ($addressBookContacts as $contact) { |
|
603
|
|
|
if (isset($contact['EMAIL'])) { |
|
604
|
|
|
$emailAddresses = $contact['EMAIL']; |
|
605
|
|
|
if (!is_array($emailAddresses)) { |
|
606
|
|
|
$emailAddresses = [$emailAddresses]; |
|
607
|
|
|
} |
|
608
|
|
|
foreach ($emailAddresses as $emailAddress) { |
|
609
|
|
|
$exactEmailMatch = strtolower($emailAddress) === $lowerSearch; |
|
610
|
|
|
|
|
611
|
|
|
if (isset($contact['isLocalSystemBook'])) { |
|
612
|
|
|
if ($exactEmailMatch) { |
|
613
|
|
|
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
614
|
|
View Code Duplication |
if (!$this->hasUserInResult($cloud->getUser())) { |
|
615
|
|
|
$this->result['exact']['users'][] = [ |
|
616
|
|
|
'label' => $contact['FN'] . " ($emailAddress)", |
|
617
|
|
|
'value' => [ |
|
618
|
|
|
'shareType' => Share::SHARE_TYPE_USER, |
|
619
|
|
|
'shareWith' => $cloud->getUser(), |
|
620
|
|
|
], |
|
621
|
|
|
]; |
|
622
|
|
|
} |
|
623
|
|
|
return ['results' => [], 'exact' => [], 'exactIdMatch' => true]; |
|
624
|
|
|
} |
|
625
|
|
|
if ($this->shareeEnumeration) { |
|
626
|
|
|
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
627
|
|
View Code Duplication |
if (!$this->hasUserInResult($cloud->getUser())) { |
|
628
|
|
|
$this->result['users'][] = [ |
|
629
|
|
|
'label' => $contact['FN'] . " ($emailAddress)", |
|
630
|
|
|
'value' => [ |
|
631
|
|
|
'shareType' => Share::SHARE_TYPE_USER, |
|
632
|
|
|
'shareWith' => $cloud->getUser(), |
|
633
|
|
|
], |
|
634
|
|
|
]; |
|
635
|
|
|
} |
|
636
|
|
|
} |
|
637
|
|
|
continue; |
|
638
|
|
|
} |
|
639
|
|
|
|
|
640
|
|
|
if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) { |
|
641
|
|
|
if ($exactEmailMatch) { |
|
642
|
|
|
$result['exactIdMatch'] = true; |
|
643
|
|
|
} |
|
644
|
|
|
$result['exact'][] = [ |
|
645
|
|
|
'label' => $contact['FN'] . " ($emailAddress)", |
|
646
|
|
|
'value' => [ |
|
647
|
|
|
'shareType' => Share::SHARE_TYPE_EMAIL, |
|
648
|
|
|
'shareWith' => $emailAddress, |
|
649
|
|
|
], |
|
650
|
|
|
]; |
|
651
|
|
View Code Duplication |
} else { |
|
652
|
|
|
$result['results'][] = [ |
|
653
|
|
|
'label' => $contact['FN'] . " ($emailAddress)", |
|
654
|
|
|
'value' => [ |
|
655
|
|
|
'shareType' => Share::SHARE_TYPE_EMAIL, |
|
656
|
|
|
'shareWith' => $emailAddress, |
|
657
|
|
|
], |
|
658
|
|
|
]; |
|
659
|
|
|
} |
|
660
|
|
|
} |
|
661
|
|
|
} |
|
662
|
|
|
} |
|
663
|
|
|
|
|
664
|
|
|
if (!$this->shareeEnumeration) { |
|
665
|
|
|
$result['results'] = []; |
|
666
|
|
|
} |
|
667
|
|
|
|
|
668
|
|
|
if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) { |
|
669
|
|
|
$result['exact'][] = [ |
|
670
|
|
|
'label' => $search, |
|
671
|
|
|
'value' => [ |
|
672
|
|
|
'shareType' => Share::SHARE_TYPE_EMAIL, |
|
673
|
|
|
'shareWith' => $search, |
|
674
|
|
|
], |
|
675
|
|
|
]; |
|
676
|
|
|
} |
|
677
|
|
|
|
|
678
|
|
|
$this->reachedEndFor[] = 'emails'; |
|
679
|
|
|
|
|
680
|
|
|
return $result; |
|
681
|
|
|
} |
|
682
|
|
|
|
|
683
|
|
|
protected function getLookup($search) { |
|
684
|
|
|
$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
685
|
|
|
$lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); |
|
686
|
|
|
$lookupServerUrl = rtrim($lookupServerUrl, '/'); |
|
687
|
|
|
$result = []; |
|
688
|
|
|
|
|
689
|
|
|
if($isEnabled === 'yes') { |
|
690
|
|
|
try { |
|
691
|
|
|
$client = $this->clientService->newClient(); |
|
692
|
|
|
$response = $client->get( |
|
693
|
|
|
$lookupServerUrl . '/users?search=' . urlencode($search), |
|
694
|
|
|
[ |
|
695
|
|
|
'timeout' => 10, |
|
696
|
|
|
'connect_timeout' => 3, |
|
697
|
|
|
] |
|
698
|
|
|
); |
|
699
|
|
|
|
|
700
|
|
|
$body = json_decode($response->getBody(), true); |
|
701
|
|
|
|
|
702
|
|
|
$result = []; |
|
703
|
|
|
foreach ($body as $lookup) { |
|
704
|
|
|
$result[] = [ |
|
705
|
|
|
'label' => $lookup['federationId'], |
|
706
|
|
|
'value' => [ |
|
707
|
|
|
'shareType' => Share::SHARE_TYPE_REMOTE, |
|
708
|
|
|
'shareWith' => $lookup['federationId'], |
|
709
|
|
|
], |
|
710
|
|
|
'extra' => $lookup, |
|
711
|
|
|
]; |
|
712
|
|
|
} |
|
713
|
|
|
} catch (\Exception $e) {} |
|
|
|
|
|
|
714
|
|
|
} |
|
715
|
|
|
|
|
716
|
|
|
$this->result['lookup'] = $result; |
|
717
|
|
|
} |
|
718
|
|
|
|
|
719
|
|
|
/** |
|
720
|
|
|
* Check if a given user is already part of the result |
|
721
|
|
|
* |
|
722
|
|
|
* @param string $userId |
|
723
|
|
|
* @return bool |
|
724
|
|
|
*/ |
|
725
|
|
|
protected function hasUserInResult($userId) { |
|
726
|
|
View Code Duplication |
foreach ($this->result['exact']['users'] as $result) { |
|
727
|
|
|
if ($result['value']['shareWith'] === $userId) { |
|
728
|
|
|
return true; |
|
729
|
|
|
} |
|
730
|
|
|
} |
|
731
|
|
|
|
|
732
|
|
View Code Duplication |
foreach ($this->result['users'] as $result) { |
|
733
|
|
|
if ($result['value']['shareWith'] === $userId) { |
|
734
|
|
|
return true; |
|
735
|
|
|
} |
|
736
|
|
|
} |
|
737
|
|
|
|
|
738
|
|
|
return false; |
|
739
|
|
|
} |
|
740
|
|
|
|
|
741
|
|
|
/** |
|
742
|
|
|
* Generates a bunch of pagination links for the current page |
|
743
|
|
|
* |
|
744
|
|
|
* @param int $page Current page |
|
745
|
|
|
* @param array $params Parameters for the URL |
|
746
|
|
|
* @return string |
|
747
|
|
|
*/ |
|
748
|
|
|
protected function getPaginationLink($page, array $params) { |
|
749
|
|
|
if ($this->isV2()) { |
|
750
|
|
|
$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
751
|
|
|
} else { |
|
752
|
|
|
$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
753
|
|
|
} |
|
754
|
|
|
$params['page'] = $page + 1; |
|
755
|
|
|
$link = '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
756
|
|
|
|
|
757
|
|
|
return $link; |
|
758
|
|
|
} |
|
759
|
|
|
|
|
760
|
|
|
/** |
|
761
|
|
|
* @return bool |
|
762
|
|
|
*/ |
|
763
|
|
|
protected function isV2() { |
|
764
|
|
|
return $this->request->getScriptName() === '/ocs/v2.php'; |
|
765
|
|
|
} |
|
766
|
|
|
} |
|
767
|
|
|
|
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.