1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Viktar Dubiniuk <[email protected]> |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright (c) 2018, ownCloud GmbH |
6
|
|
|
* @license AGPL-3.0 |
7
|
|
|
* |
8
|
|
|
* This code is free software: you can redistribute it and/or modify |
9
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
10
|
|
|
* as published by the Free Software Foundation. |
11
|
|
|
* |
12
|
|
|
* This program is distributed in the hope that it will be useful, |
13
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15
|
|
|
* GNU Affero General Public License for more details. |
16
|
|
|
* |
17
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
18
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
19
|
|
|
* |
20
|
|
|
*/ |
21
|
|
|
|
22
|
|
|
namespace OCA\Files_Sharing\Controller; |
23
|
|
|
|
24
|
|
|
use OCP\AppFramework\OCSController; |
25
|
|
|
use OCP\Files\IRootFolder; |
26
|
|
|
use OCP\Files\NotFoundException; |
27
|
|
|
use OCP\IConfig; |
28
|
|
|
use OCP\IGroupManager; |
29
|
|
|
use OCP\IL10N; |
30
|
|
|
use OCP\IRequest; |
31
|
|
|
use OCP\IURLGenerator; |
32
|
|
|
use OCP\IUser; |
33
|
|
|
use OCP\IUserManager; |
34
|
|
|
use OCP\Lock\ILockingProvider; |
35
|
|
|
use OCP\Lock\LockedException; |
36
|
|
|
use OCP\Share\Exceptions\GenericShareException; |
37
|
|
|
use OCP\Share\Exceptions\ShareNotFound; |
38
|
|
|
use OCP\Share\IManager; |
39
|
|
|
use OCP\Share\IShare; |
40
|
|
|
use OCA\Files_Sharing\Service\NotificationPublisher; |
41
|
|
|
use OCA\Files_Sharing\Helper; |
42
|
|
|
use OCA\Files_Sharing\SharingBlacklist; |
43
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcher; |
44
|
|
|
use Symfony\Component\EventDispatcher\GenericEvent; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Class Share20OcsController |
48
|
|
|
* |
49
|
|
|
* @package OCA\Files_Sharing\Controller |
50
|
|
|
*/ |
51
|
|
|
class Share20OcsController extends OCSController { |
52
|
|
|
/** @var IManager */ |
53
|
|
|
private $shareManager; |
54
|
|
|
/** @var IGroupManager */ |
55
|
|
|
private $groupManager; |
56
|
|
|
/** @var IUserManager */ |
57
|
|
|
private $userManager; |
58
|
|
|
/** @var IRootFolder */ |
59
|
|
|
private $rootFolder; |
60
|
|
|
/** @var IURLGenerator */ |
61
|
|
|
private $urlGenerator; |
62
|
|
|
/** @var IUser */ |
63
|
|
|
private $currentUser; |
64
|
|
|
/** @var IL10N */ |
65
|
|
|
private $l; |
66
|
|
|
/** @var IConfig */ |
67
|
|
|
private $config; |
68
|
|
|
/** @var NotificationPublisher */ |
69
|
|
|
private $notificationPublisher; |
70
|
|
|
/** @var EventDispatcher */ |
71
|
|
|
private $eventDispatcher; |
72
|
|
|
/** @var SharingBlacklist */ |
73
|
|
|
private $sharingBlacklist; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @var string |
77
|
|
|
*/ |
78
|
|
|
private $additionalInfoField; |
79
|
|
|
|
80
|
|
|
public function __construct( |
81
|
|
|
$appName, |
82
|
|
|
IRequest $request, |
83
|
|
|
IManager $shareManager, |
84
|
|
|
IGroupManager $groupManager, |
85
|
|
|
IUserManager $userManager, |
86
|
|
|
IRootFolder $rootFolder, |
87
|
|
|
IURLGenerator $urlGenerator, |
88
|
|
|
IUser $currentUser, |
89
|
|
|
IL10N $l10n, |
90
|
|
|
IConfig $config, |
91
|
|
|
NotificationPublisher $notificationPublisher, |
92
|
|
|
EventDispatcher $eventDispatcher, |
93
|
|
|
SharingBlacklist $sharingBlacklist |
94
|
|
|
) { |
95
|
|
|
parent::__construct($appName, $request); |
96
|
|
|
$this->request = $request; |
97
|
|
|
$this->shareManager = $shareManager; |
98
|
|
|
$this->groupManager = $groupManager; |
99
|
|
|
$this->userManager = $userManager; |
100
|
|
|
$this->rootFolder = $rootFolder; |
101
|
|
|
$this->urlGenerator = $urlGenerator; |
102
|
|
|
$this->currentUser = $currentUser; |
103
|
|
|
$this->l = $l10n; |
104
|
|
|
$this->config = $config; |
105
|
|
|
$this->notificationPublisher = $notificationPublisher; |
106
|
|
|
$this->eventDispatcher = $eventDispatcher; |
107
|
|
|
$this->sharingBlacklist = $sharingBlacklist; |
108
|
|
|
$this->additionalInfoField = $this->config->getAppValue('core', 'user_additional_info_field', ''); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Returns the additional info to display behind the display name as configured. |
113
|
|
|
* |
114
|
|
|
* @param IUser $user user for which to retrieve the additional info |
115
|
|
|
* @return string|null additional info or null if none to be displayed |
116
|
|
|
*/ |
117
|
|
View Code Duplication |
private function getAdditionalUserInfo(IUser $user) { |
118
|
|
|
if ($this->additionalInfoField === 'email') { |
119
|
|
|
return $user->getEMailAddress(); |
120
|
|
|
} elseif ($this->additionalInfoField === 'id') { |
121
|
|
|
return $user->getUID(); |
122
|
|
|
} |
123
|
|
|
return null; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Convert an IShare to an array for OCS output |
128
|
|
|
* |
129
|
|
|
* @param \OCP\Share\IShare $share |
130
|
|
|
* @param bool $received whether it's formatting received shares |
131
|
|
|
* @return array |
132
|
|
|
* @throws NotFoundException In case the node can't be resolved. |
133
|
|
|
*/ |
134
|
|
|
protected function formatShare(\OCP\Share\IShare $share, $received = false) { |
135
|
|
|
$sharedBy = $this->userManager->get($share->getSharedBy()); |
136
|
|
|
$shareOwner = $this->userManager->get($share->getShareOwner()); |
137
|
|
|
|
138
|
|
|
$result = [ |
139
|
|
|
'id' => $share->getId(), |
140
|
|
|
'share_type' => $share->getShareType(), |
141
|
|
|
'uid_owner' => $share->getSharedBy(), |
142
|
|
|
'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(), |
143
|
|
|
'permissions' => $share->getPermissions(), |
144
|
|
|
'stime' => $share->getShareTime() ? $share->getShareTime()->getTimestamp() : null, |
145
|
|
|
'parent' => null, |
146
|
|
|
'expiration' => null, |
147
|
|
|
'token' => null, |
148
|
|
|
'uid_file_owner' => $share->getShareOwner(), |
149
|
|
|
'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner() |
150
|
|
|
]; |
151
|
|
|
|
152
|
|
|
if ($received) { |
153
|
|
|
// also add state |
154
|
|
|
$result['state'] = $share->getState(); |
155
|
|
|
|
156
|
|
|
// can only fetch path info if mounted already or if owner |
157
|
|
|
if ($share->getState() === \OCP\Share::STATE_ACCEPTED || $share->getShareOwner() === $this->currentUser->getUID()) { |
158
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); |
159
|
|
|
} else { |
160
|
|
|
// need to go through owner user for pending shares |
161
|
|
|
$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
162
|
|
|
} |
163
|
|
|
} else { |
164
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$nodes = $userFolder->getById($share->getNodeId()); |
168
|
|
|
|
169
|
|
|
if (empty($nodes)) { |
170
|
|
|
throw new NotFoundException(); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
$node = $nodes[0]; |
174
|
|
|
|
175
|
|
|
$result['path'] = $userFolder->getRelativePath($node->getPath()); |
176
|
|
|
if ($node instanceof \OCP\Files\Folder) { |
177
|
|
|
$result['item_type'] = 'folder'; |
178
|
|
|
} else { |
179
|
|
|
$result['item_type'] = 'file'; |
180
|
|
|
} |
181
|
|
|
$result['mimetype'] = $node->getMimeType(); |
182
|
|
|
$result['storage_id'] = $node->getStorage()->getId(); |
183
|
|
|
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId(); |
184
|
|
|
$result['item_source'] = \strval($node->getId()); |
185
|
|
|
$result['file_source'] = \strval($node->getId()); |
186
|
|
|
$result['file_parent'] = \strval($node->getParent()->getId()); |
187
|
|
|
$result['file_target'] = $share->getTarget(); |
188
|
|
|
|
189
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
190
|
|
|
$sharedWith = $this->userManager->get($share->getSharedWith()); |
191
|
|
|
$result['share_with'] = $share->getSharedWith(); |
192
|
|
|
$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith(); |
193
|
|
|
if ($sharedWith !== null) { |
194
|
|
|
$result['share_with_additional_info'] = $this->getAdditionalUserInfo($sharedWith); |
195
|
|
|
} |
196
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
197
|
|
|
$group = $this->groupManager->get($share->getSharedWith()); |
198
|
|
|
$result['share_with'] = $share->getSharedWith(); |
199
|
|
|
$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); |
200
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
201
|
|
|
$result['share_with'] = $share->getPassword(); |
202
|
|
|
$result['share_with_displayname'] = $share->getPassword(); |
203
|
|
|
$result['name'] = $share->getName(); |
204
|
|
|
|
205
|
|
|
$result['token'] = $share->getToken(); |
206
|
|
|
if ($share->getToken() !== null) { |
207
|
|
|
$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
$expiration = $share->getExpirationDate(); |
211
|
|
|
if ($expiration !== null) { |
212
|
|
|
$result['expiration'] = $expiration->format('Y-m-d 00:00:00'); |
213
|
|
|
} |
214
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { |
215
|
|
|
$result['share_with'] = $share->getSharedWith(); |
216
|
|
|
$result['share_with_displayname'] = $share->getSharedWith(); |
217
|
|
|
$result['token'] = $share->getToken(); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$result['mail_send'] = $share->getMailSend() ? 1 : 0; |
221
|
|
|
|
222
|
|
|
return $result; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Get a specific share by id |
227
|
|
|
* |
228
|
|
|
* @NoCSRFRequired |
229
|
|
|
* @NoAdminRequired |
230
|
|
|
* |
231
|
|
|
* @param string $id |
232
|
|
|
* @return \OC\OCS\Result |
233
|
|
|
*/ |
234
|
|
|
public function getShare($id) { |
235
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
236
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled')); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
try { |
240
|
|
|
$share = $this->getShareById($id); |
241
|
|
|
} catch (ShareNotFound $e) { |
242
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
if ($this->canAccessShare($share)) { |
246
|
|
|
try { |
247
|
|
|
$share = $this->formatShare($share); |
248
|
|
|
return new \OC\OCS\Result([$share]); |
249
|
|
|
} catch (NotFoundException $e) { |
250
|
|
|
//Fall trough |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Delete a share |
259
|
|
|
* |
260
|
|
|
* @NoCSRFRequired |
261
|
|
|
* @NoAdminRequired |
262
|
|
|
* |
263
|
|
|
* @param string $id |
264
|
|
|
* @return \OC\OCS\Result |
265
|
|
|
*/ |
266
|
|
|
public function deleteShare($id) { |
267
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
268
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled')); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
try { |
272
|
|
|
$share = $this->getShareById($id); |
273
|
|
|
} catch (ShareNotFound $e) { |
274
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
try { |
278
|
|
|
$share->getNode()->lock(ILockingProvider::LOCK_SHARED); |
279
|
|
|
} catch (LockedException $e) { |
280
|
|
|
return new \OC\OCS\Result(null, 404, 'could not delete share'); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
View Code Duplication |
if (!$this->canAccessShare($share)) { |
|
|
|
|
284
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
285
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Could not delete share')); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$this->shareManager->deleteShare($share); |
289
|
|
|
|
290
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
291
|
|
|
|
292
|
|
|
return new \OC\OCS\Result(); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* @NoCSRFRequired |
297
|
|
|
* @NoAdminRequired |
298
|
|
|
* |
299
|
|
|
* @return \OC\OCS\Result |
300
|
|
|
*/ |
301
|
|
|
public function createShare() { |
302
|
|
|
$share = $this->shareManager->newShare(); |
303
|
|
|
|
304
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
305
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled')); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
$name = $this->request->getParam('name', null); |
309
|
|
|
|
310
|
|
|
// Verify path |
311
|
|
|
$path = $this->request->getParam('path', null); |
312
|
|
|
if ($path === null) { |
313
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a file or folder path')); |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); |
317
|
|
|
|
318
|
|
|
try { |
319
|
|
|
$path = $userFolder->get($path); |
320
|
|
|
} catch (NotFoundException $e) { |
321
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist')); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
$share->setNode($path); |
325
|
|
|
|
326
|
|
|
try { |
327
|
|
|
$share->getNode()->lock(ILockingProvider::LOCK_SHARED); |
328
|
|
|
} catch (LockedException $e) { |
329
|
|
|
return new \OC\OCS\Result(null, 404, 'Could not create share'); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
$shareType = (int)$this->request->getParam('shareType', '-1'); |
333
|
|
|
|
334
|
|
|
// Parse permissions (if available) |
335
|
|
|
$permissions = $this->request->getParam('permissions', null); |
336
|
|
|
if ($permissions === null) { |
337
|
|
|
if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) { |
338
|
|
|
$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL); |
339
|
|
|
$permissions |= \OCP\Constants::PERMISSION_READ; |
340
|
|
|
} else { |
341
|
|
|
$permissions = \OCP\Constants::PERMISSION_ALL; |
342
|
|
|
} |
343
|
|
|
} else { |
344
|
|
|
$permissions = (int)$permissions; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) { |
348
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
349
|
|
|
return new \OC\OCS\Result(null, 404, 'invalid permissions'); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
if ($permissions === 0) { |
353
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions')); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
// link shares can have create-only without read (anonymous upload) |
357
|
|
|
if ($shareType !== \OCP\Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) { |
358
|
|
|
// Shares always require read permissions |
359
|
|
|
$permissions |= \OCP\Constants::PERMISSION_READ; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
if ($path instanceof \OCP\Files\File) { |
363
|
|
|
// Single file shares should never have delete or create permissions |
364
|
|
|
$permissions &= ~\OCP\Constants::PERMISSION_DELETE; |
365
|
|
|
$permissions &= ~\OCP\Constants::PERMISSION_CREATE; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/* |
369
|
|
|
* Hack for https://github.com/owncloud/core/issues/22587 |
370
|
|
|
* We check the permissions via webdav. But the permissions of the mount point |
371
|
|
|
* do not equal the share permissions. Here we fix that for federated mounts. |
372
|
|
|
*/ |
373
|
|
|
if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
374
|
|
|
$permissions &= ~($permissions & ~$path->getPermissions()); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
$shareWith = $this->request->getParam('shareWith', null); |
378
|
|
|
|
379
|
|
|
$autoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes'; |
380
|
|
|
if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
381
|
|
|
// Valid user is required to share |
382
|
|
|
if ($shareWith === null || !$this->userManager->userExists($shareWith)) { |
383
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
384
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid user')); |
385
|
|
|
} |
386
|
|
|
$share->setSharedWith($shareWith); |
387
|
|
|
$share->setPermissions($permissions); |
388
|
|
|
if ($autoAccept) { |
389
|
|
|
$share->setState(\OCP\Share::STATE_ACCEPTED); |
390
|
|
|
} else { |
391
|
|
|
$share->setState(\OCP\Share::STATE_PENDING); |
392
|
|
|
} |
393
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
394
|
|
|
if (!$this->shareManager->allowGroupSharing()) { |
395
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
396
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Group sharing is disabled by the administrator')); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
// Valid group is required to share |
400
|
|
|
if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) { |
401
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
402
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Please specify a valid group')); |
403
|
|
|
} |
404
|
|
|
if ($this->sharingBlacklist->isGroupBlacklisted($this->groupManager->get($shareWith))) { |
405
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('The group is blacklisted for sharing')); |
406
|
|
|
} |
407
|
|
|
$share->setSharedWith($shareWith); |
408
|
|
|
$share->setPermissions($permissions); |
409
|
|
|
if ($autoAccept) { |
410
|
|
|
$share->setState(\OCP\Share::STATE_ACCEPTED); |
411
|
|
|
} else { |
412
|
|
|
$share->setState(\OCP\Share::STATE_PENDING); |
413
|
|
|
} |
414
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_LINK) { |
415
|
|
|
//Can we even share links? |
416
|
|
|
if (!$this->shareManager->shareApiAllowLinks()) { |
417
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
418
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator')); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
// legacy way, expecting that this won't be used together with "create-only" shares |
422
|
|
|
$publicUpload = $this->request->getParam('publicUpload', null); |
423
|
|
|
// a few permission checks |
424
|
|
View Code Duplication |
if ($publicUpload === 'true' || $permissions === \OCP\Constants::PERMISSION_CREATE) { |
|
|
|
|
425
|
|
|
// Check if public upload is allowed |
426
|
|
|
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
427
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
428
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator')); |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
// Public upload can only be set for folders |
432
|
|
|
if ($path instanceof \OCP\Files\File) { |
433
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
434
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders')); |
435
|
|
|
} |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
// convert to permissions |
439
|
|
|
if ($publicUpload === 'true') { |
440
|
|
|
$share->setPermissions( |
441
|
|
|
\OCP\Constants::PERMISSION_READ | |
442
|
|
|
\OCP\Constants::PERMISSION_CREATE | |
443
|
|
|
\OCP\Constants::PERMISSION_UPDATE | |
444
|
|
|
\OCP\Constants::PERMISSION_DELETE |
445
|
|
|
); |
446
|
|
|
} elseif ($permissions === \OCP\Constants::PERMISSION_CREATE || |
447
|
|
|
$permissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)) { |
448
|
|
|
$share->setPermissions($permissions); |
449
|
|
|
} else { |
450
|
|
|
// because when "publicUpload" is passed usually no permissions are set, |
451
|
|
|
// which defaults to ALL. But in the case of link shares we default to READ... |
452
|
|
|
$share->setPermissions(\OCP\Constants::PERMISSION_READ); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
// set name only if passed as parameter, empty string is allowed |
456
|
|
|
if ($name !== null) { |
457
|
|
|
$share->setName($name); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
// Set password |
461
|
|
|
$password = $this->request->getParam('password', ''); |
462
|
|
|
|
463
|
|
|
if ($password !== '') { |
464
|
|
|
$share->setPassword($password); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
//Expire date |
468
|
|
|
$expireDate = $this->request->getParam('expireDate', ''); |
469
|
|
|
|
470
|
|
|
if ($expireDate !== '') { |
471
|
|
|
try { |
472
|
|
|
$expireDate = $this->parseDate($expireDate); |
473
|
|
|
$share->setExpirationDate($expireDate); |
474
|
|
|
} catch (\Exception $e) { |
475
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
476
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD')); |
477
|
|
|
} |
478
|
|
|
} |
479
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { |
480
|
|
|
if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
481
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
482
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType])); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
$share->setSharedWith($shareWith); |
486
|
|
|
$share->setPermissions($permissions); |
487
|
|
View Code Duplication |
} else { |
|
|
|
|
488
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
489
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Unknown share type')); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
$share->setShareType($shareType); |
493
|
|
|
$share->setSharedBy($this->currentUser->getUID()); |
494
|
|
|
|
495
|
|
|
try { |
496
|
|
|
$share = $this->shareManager->createShare($share); |
497
|
|
|
} catch (GenericShareException $e) { |
498
|
|
|
$code = $e->getCode() === 0 ? 403 : $e->getCode(); |
499
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
500
|
|
|
return new \OC\OCS\Result(null, $code, $e->getHint()); |
501
|
|
|
} catch (\Exception $e) { |
502
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
503
|
|
|
return new \OC\OCS\Result(null, 403, $e->getMessage()); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
507
|
|
|
|
508
|
|
|
$formattedShareAfterCreate = $this->formatShare($share); |
509
|
|
|
|
510
|
|
|
return new \OC\OCS\Result($formattedShareAfterCreate); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* @param \OCP\Files\File|\OCP\Files\Folder $node |
515
|
|
|
* @param boolean $includeTags include tags in response |
516
|
|
|
* @param int|null $stateFilter state filter or empty for all, defaults to 0 (accepted) |
517
|
|
|
* @return \OC\OCS\Result |
518
|
|
|
*/ |
519
|
|
|
private function getSharedWithMe($node = null, $includeTags, $stateFilter = 0) { |
520
|
|
|
$userShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0); |
521
|
|
|
$groupShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0); |
522
|
|
|
|
523
|
|
|
$shares = \array_merge($userShares, $groupShares); |
524
|
|
|
|
525
|
|
|
$shares = \array_filter($shares, function (IShare $share) { |
526
|
|
|
return $share->getShareOwner() !== $this->currentUser->getUID(); |
527
|
|
|
}); |
528
|
|
|
|
529
|
|
|
$formatted = []; |
530
|
|
|
foreach ($shares as $share) { |
531
|
|
|
if (($stateFilter === null || $share->getState() === $stateFilter) && |
532
|
|
|
$this->canAccessShare($share)) { |
533
|
|
|
try { |
534
|
|
|
$formatted[] = $this->formatShare($share, true); |
535
|
|
|
} catch (NotFoundException $e) { |
536
|
|
|
// Ignore this share |
537
|
|
|
} |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
if ($includeTags) { |
542
|
|
|
$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source'); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
return new \OC\OCS\Result($formatted); |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* @param \OCP\Files\Folder $folder |
550
|
|
|
* @return \OC\OCS\Result |
551
|
|
|
*/ |
552
|
|
|
private function getSharesInDir($folder) { |
553
|
|
|
if (!($folder instanceof \OCP\Files\Folder)) { |
554
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Not a directory')); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
$nodes = $folder->getDirectoryListing(); |
558
|
|
|
/** @var \OCP\Share\IShare[] $shares */ |
559
|
|
|
$shares = []; |
560
|
|
|
foreach ($nodes as $node) { |
561
|
|
|
$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); |
562
|
|
|
$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0)); |
563
|
|
|
$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); |
564
|
|
|
if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
565
|
|
|
$shares = \array_merge($shares, $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0)); |
566
|
|
|
} |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
$formatted = []; |
570
|
|
|
foreach ($shares as $share) { |
571
|
|
|
try { |
572
|
|
|
$formatted[] = $this->formatShare($share); |
573
|
|
|
} catch (NotFoundException $e) { |
574
|
|
|
//Ignore this share |
575
|
|
|
} |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
return new \OC\OCS\Result($formatted); |
579
|
|
|
} |
580
|
|
|
|
581
|
|
|
/** |
582
|
|
|
* The getShares function. |
583
|
|
|
* |
584
|
|
|
* @NoCSRFRequired |
585
|
|
|
* @NoAdminRequired |
586
|
|
|
* |
587
|
|
|
* - Get shares by the current user |
588
|
|
|
* - Get shares by the current user and reshares (?reshares=true) |
589
|
|
|
* - Get shares with the current user (?shared_with_me=true) |
590
|
|
|
* - Get shares for a specific path (?path=...) |
591
|
|
|
* - Get all shares in a folder (?subfiles=true&path=..) |
592
|
|
|
* |
593
|
|
|
* @return \OC\OCS\Result |
594
|
|
|
*/ |
595
|
|
|
public function getShares() { |
596
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
597
|
|
|
return new \OC\OCS\Result(); |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
$sharedWithMe = $this->request->getParam('shared_with_me', null); |
601
|
|
|
$reshares = $this->request->getParam('reshares', null); |
602
|
|
|
$subfiles = $this->request->getParam('subfiles'); |
603
|
|
|
$path = $this->request->getParam('path', null); |
604
|
|
|
|
605
|
|
|
$includeTags = $this->request->getParam('include_tags', false); |
606
|
|
|
|
607
|
|
|
if ($path !== null) { |
608
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); |
609
|
|
|
try { |
610
|
|
|
$path = $userFolder->get($path); |
611
|
|
|
$path->lock(ILockingProvider::LOCK_SHARED); |
612
|
|
|
} catch (\OCP\Files\NotFoundException $e) { |
613
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist')); |
614
|
|
|
} catch (LockedException $e) { |
615
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Could not lock path')); |
616
|
|
|
} |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
if ($sharedWithMe === 'true') { |
620
|
|
|
$stateFilter = $this->request->getParam('state', \OCP\Share::STATE_ACCEPTED); |
621
|
|
|
if ($stateFilter === '') { |
622
|
|
|
$stateFilter = \OCP\Share::STATE_ACCEPTED; |
623
|
|
|
} elseif ($stateFilter === 'all') { |
624
|
|
|
$stateFilter = null; // which means all |
625
|
|
|
} else { |
626
|
|
|
$stateFilter = (int)$stateFilter; |
627
|
|
|
} |
628
|
|
|
$result = $this->getSharedWithMe($path, $includeTags, $stateFilter); |
|
|
|
|
629
|
|
|
if ($path !== null) { |
630
|
|
|
$path->unlock(ILockingProvider::LOCK_SHARED); |
631
|
|
|
} |
632
|
|
|
return $result; |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
if ($subfiles === 'true') { |
636
|
|
|
$result = $this->getSharesInDir($path); |
|
|
|
|
637
|
|
|
if ($path !== null) { |
638
|
|
|
$path->unlock(ILockingProvider::LOCK_SHARED); |
639
|
|
|
} |
640
|
|
|
return $result; |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
if ($reshares === 'true') { |
644
|
|
|
$reshares = true; |
645
|
|
|
} else { |
646
|
|
|
$reshares = false; |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
// Get all shares |
650
|
|
|
$userShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0); |
651
|
|
|
$groupShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0); |
652
|
|
|
$linkShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0); |
653
|
|
|
$shares = \array_merge($userShares, $groupShares, $linkShares); |
654
|
|
|
|
655
|
|
|
if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
656
|
|
|
$federatedShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0); |
657
|
|
|
$shares = \array_merge($shares, $federatedShares); |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
$formatted = []; |
661
|
|
|
foreach ($shares as $share) { |
662
|
|
|
try { |
663
|
|
|
$formatted[] = $this->formatShare($share); |
664
|
|
|
} catch (NotFoundException $e) { |
665
|
|
|
//Ignore share |
666
|
|
|
} |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
if ($includeTags) { |
670
|
|
|
$formatted = \OCA\Files\Helper::populateTags($formatted, 'file_source'); |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
if ($path !== null) { |
674
|
|
|
$path->unlock(ILockingProvider::LOCK_SHARED); |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
return new \OC\OCS\Result($formatted); |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
/** |
681
|
|
|
* @NoCSRFRequired |
682
|
|
|
* @NoAdminRequired |
683
|
|
|
* |
684
|
|
|
* @param int $id |
685
|
|
|
* @return \OC\OCS\Result |
686
|
|
|
*/ |
687
|
|
|
public function updateShare($id) { |
688
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
689
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled')); |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
try { |
693
|
|
|
$share = $this->getShareById($id); |
694
|
|
|
} catch (ShareNotFound $e) { |
695
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
$share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
699
|
|
|
|
700
|
|
View Code Duplication |
if (!$this->canAccessShare($share)) { |
|
|
|
|
701
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
702
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
$permissions = $this->request->getParam('permissions', null); |
706
|
|
|
$password = $this->request->getParam('password', null); |
707
|
|
|
$publicUpload = $this->request->getParam('publicUpload', null); |
708
|
|
|
$expireDate = $this->request->getParam('expireDate', null); |
709
|
|
|
$name = $this->request->getParam('name', null); |
710
|
|
|
|
711
|
|
|
/* |
712
|
|
|
* expirationdate, password and publicUpload only make sense for link shares |
713
|
|
|
*/ |
714
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
715
|
|
|
if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null && $name === null) { |
716
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
717
|
|
|
return new \OC\OCS\Result(null, 400, 'Wrong or no update parameter given'); |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
$newPermissions = null; |
721
|
|
|
if ($publicUpload === 'true') { |
722
|
|
|
$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
723
|
|
|
} elseif ($publicUpload === 'false') { |
724
|
|
|
$newPermissions = \OCP\Constants::PERMISSION_READ; |
725
|
|
|
} |
726
|
|
|
|
727
|
|
|
if ($permissions !== null) { |
728
|
|
|
$newPermissions = (int)$permissions; |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
if ($newPermissions !== null && |
732
|
|
|
$newPermissions !== \OCP\Constants::PERMISSION_READ && |
733
|
|
|
$newPermissions !== \OCP\Constants::PERMISSION_CREATE && |
734
|
|
|
// legacy |
735
|
|
|
$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) && |
736
|
|
|
// correct |
737
|
|
|
$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE) |
738
|
|
|
) { |
739
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
740
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Can\'t change permissions for public share links')); |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
if ( |
744
|
|
|
// legacy |
745
|
|
|
$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) || |
746
|
|
|
// correct |
747
|
|
|
$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE) |
748
|
|
|
) { |
749
|
|
|
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
750
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
751
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator')); |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
if (!($share->getNode() instanceof \OCP\Files\Folder)) { |
755
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
756
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders')); |
757
|
|
|
} |
758
|
|
|
|
759
|
|
|
// normalize to correct public upload permissions |
760
|
|
|
$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
761
|
|
|
} |
762
|
|
|
|
763
|
|
|
// create-only (upload-only) |
764
|
|
View Code Duplication |
if ( |
|
|
|
|
765
|
|
|
$newPermissions === \OCP\Constants::PERMISSION_CREATE |
766
|
|
|
) { |
767
|
|
|
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
768
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
769
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('Public upload disabled by the administrator')); |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
if (!($share->getNode() instanceof \OCP\Files\Folder)) { |
773
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
774
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders')); |
775
|
|
|
} |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
// set name only if passed as parameter, empty string is allowed |
779
|
|
|
if ($name !== null) { |
780
|
|
|
$oldname = $share->getName(); |
781
|
|
|
$share->setName($name); |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
if ($newPermissions !== null) { |
785
|
|
|
$share->setPermissions($newPermissions); |
786
|
|
|
$permissions = $newPermissions; |
787
|
|
|
} |
788
|
|
|
|
789
|
|
|
if ($expireDate === '') { |
790
|
|
|
$share->setExpirationDate(null); |
|
|
|
|
791
|
|
|
} elseif ($expireDate !== null) { |
792
|
|
|
try { |
793
|
|
|
$expireDate = $this->parseDate($expireDate); |
794
|
|
|
} catch (\Exception $e) { |
795
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
796
|
|
|
return new \OC\OCS\Result(null, 400, $e->getMessage()); |
797
|
|
|
} |
798
|
|
|
$share->setExpirationDate($expireDate); |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
if ($password === '') { |
802
|
|
|
$share->setPassword(null); |
803
|
|
|
} elseif ($password !== null) { |
804
|
|
|
$share->setPassword($password); |
805
|
|
|
} |
806
|
|
|
} else { |
807
|
|
|
// For other shares only permissions is valid. |
808
|
|
|
if ($permissions === null) { |
809
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
810
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Wrong or no update parameter given')); |
811
|
|
|
} else { |
812
|
|
|
$permissions = (int)$permissions; |
813
|
|
|
$share->setPermissions($permissions); |
814
|
|
|
} |
815
|
|
|
} |
816
|
|
|
|
817
|
|
|
if ($permissions !== null && $share->getShareOwner() !== $this->currentUser->getUID()) { |
818
|
|
|
/* Check if this is an incoming share */ |
819
|
|
|
$incomingShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0); |
820
|
|
|
$incomingShares = \array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0)); |
821
|
|
|
|
822
|
|
|
if (!empty($incomingShares)) { |
823
|
|
|
$maxPermissions = 0; |
824
|
|
|
foreach ($incomingShares as $incomingShare) { |
825
|
|
|
$maxPermissions |= $incomingShare->getPermissions(); |
826
|
|
|
} |
827
|
|
|
|
828
|
|
|
if ($share->getPermissions() & ~$maxPermissions) { |
829
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
830
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Cannot increase permissions')); |
831
|
|
|
} |
832
|
|
|
} |
833
|
|
|
} |
834
|
|
|
|
835
|
|
|
if ($share->getPermissions() === 0) { |
836
|
|
|
return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions')); |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
try { |
840
|
|
|
$share = $this->shareManager->updateShare($share); |
841
|
|
|
} catch (\Exception $e) { |
842
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
843
|
|
|
return new \OC\OCS\Result(null, 400, $e->getMessage()); |
844
|
|
|
} |
845
|
|
|
|
846
|
|
|
$share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
847
|
|
|
|
848
|
|
|
return new \OC\OCS\Result($this->formatShare($share)); |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
/** |
852
|
|
|
* @NoCSRFRequired |
853
|
|
|
* @NoAdminRequired |
854
|
|
|
* |
855
|
|
|
* @param int $id |
856
|
|
|
* @return \OC\OCS\Result |
857
|
|
|
*/ |
858
|
|
|
public function acceptShare($id) { |
859
|
|
|
return $this->updateShareState($id, \OCP\Share::STATE_ACCEPTED); |
860
|
|
|
} |
861
|
|
|
|
862
|
|
|
/** |
863
|
|
|
* @NoCSRFRequired |
864
|
|
|
* @NoAdminRequired |
865
|
|
|
* |
866
|
|
|
* @param int $id |
867
|
|
|
* @return \OC\OCS\Result |
868
|
|
|
*/ |
869
|
|
|
public function declineShare($id) { |
870
|
|
|
return $this->updateShareState($id, \OCP\Share::STATE_REJECTED); |
871
|
|
|
} |
872
|
|
|
|
873
|
|
|
/** |
874
|
|
|
* Send a notification to share recipient(s) |
875
|
|
|
* |
876
|
|
|
* @NoCSRFRequired |
877
|
|
|
* @NoAdminRequired |
878
|
|
|
* |
879
|
|
|
* @param string $itemType |
880
|
|
|
* @param int $itemSource |
881
|
|
|
*/ |
882
|
|
|
public function notifyRecipients($itemType, $itemSource) { |
883
|
|
|
$l = \OC::$server->getL10N('core'); |
884
|
|
|
$shareType = (int) $this->request->getParam('shareType'); |
885
|
|
|
$recipient = (string)$this->request->getParam('recipient'); |
886
|
|
|
|
887
|
|
|
$recipientList = []; |
888
|
|
|
if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
889
|
|
|
$recipientList[] = $this->userManager->get($recipient); |
890
|
|
View Code Duplication |
} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
|
|
|
|
891
|
|
|
$group = \OC::$server->getGroupManager()->get($recipient); |
892
|
|
|
$recipientList = $group->searchUsers(''); |
893
|
|
|
} |
894
|
|
|
// don't send a mail to the user who shared the file |
895
|
|
|
$recipientList = \array_filter($recipientList, function ($user) { |
896
|
|
|
/** @var IUser $user */ |
897
|
|
|
return $user->getUID() !== $this->currentUser->getUID(); |
898
|
|
|
}); |
899
|
|
|
|
900
|
|
|
$defaults = new \OCP\Defaults(); |
901
|
|
|
$mailNotification = new \OC\Share\MailNotifications( |
902
|
|
|
$this->currentUser, |
903
|
|
|
\OC::$server->getL10N('lib'), |
904
|
|
|
\OC::$server->getMailer(), |
905
|
|
|
\OC::$server->getConfig(), |
906
|
|
|
\OC::$server->getLogger(), |
907
|
|
|
$defaults, |
908
|
|
|
\OC::$server->getURLGenerator(), |
909
|
|
|
\OC::$server->getEventDispatcher() |
|
|
|
|
910
|
|
|
); |
911
|
|
|
|
912
|
|
|
$result = $mailNotification->sendInternalShareMail($recipientList, $itemSource, $itemType); |
913
|
|
|
|
914
|
|
|
// if we were able to send to at least one recipient, mark as sent |
915
|
|
|
// allowing the user to resend would spam users who already got a notification |
916
|
|
|
if (\count($result) < \count($recipientList)) { |
917
|
|
|
\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, true); |
918
|
|
|
} |
919
|
|
|
|
920
|
|
|
if (empty($result)) { |
921
|
|
|
\OCP\JSON::success(); |
922
|
|
|
} else { |
923
|
|
|
\OCP\JSON::error([ |
924
|
|
|
'data' => [ |
925
|
|
|
'message' => $l->t("Couldn't send mail to following recipient(s): %s ", |
926
|
|
|
\implode(', ', $result) |
|
|
|
|
927
|
|
|
) |
928
|
|
|
] |
929
|
|
|
]); |
930
|
|
|
} |
931
|
|
|
exit(); |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
/** |
935
|
|
|
* Just mark a notification to share recipient(s) as sent |
936
|
|
|
* |
937
|
|
|
* @NoCSRFRequired |
938
|
|
|
* @NoAdminRequired |
939
|
|
|
* |
940
|
|
|
* @param string $itemType |
941
|
|
|
* @param int $itemSource |
942
|
|
|
*/ |
943
|
|
|
public function notifyRecipientsDisabled($itemType, $itemSource) { |
944
|
|
|
$shareType = (int) $this->request->getParam('shareType'); |
945
|
|
|
$recipient = (string)$this->request->getParam('recipient'); |
946
|
|
|
\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, false); |
947
|
|
|
\OCP\JSON::success(); |
948
|
|
|
exit(); |
949
|
|
|
} |
950
|
|
|
|
951
|
|
|
/** |
952
|
|
|
* @param $id |
953
|
|
|
* @param $state |
954
|
|
|
* @return \OC\OCS\Result |
955
|
|
|
*/ |
956
|
|
|
private function updateShareState($id, $state) { |
957
|
|
|
$eventName = ''; |
958
|
|
|
if ($state === \OCP\Share::STATE_ACCEPTED) { |
959
|
|
|
$eventName = 'accept'; |
960
|
|
|
} elseif ($state === \OCP\Share::STATE_REJECTED) { |
961
|
|
|
$eventName = 'reject'; |
962
|
|
|
} |
963
|
|
|
|
964
|
|
|
if (!$this->shareManager->shareApiEnabled()) { |
965
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Share API is disabled')); |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
try { |
969
|
|
|
$share = $this->getShareById($id, $this->currentUser->getUID()); |
970
|
|
|
$this->eventDispatcher->dispatch('share.before' . $eventName, new GenericEvent(null, ['share' => $share])); |
971
|
|
|
} catch (ShareNotFound $e) { |
972
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
$node = $share->getNode(); |
976
|
|
|
$node->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
977
|
|
|
|
978
|
|
|
// this checks that we are either the owner or recipient |
979
|
|
View Code Duplication |
if (!$this->canAccessShare($share)) { |
|
|
|
|
980
|
|
|
$node->unlock(ILockingProvider::LOCK_SHARED); |
981
|
|
|
return new \OC\OCS\Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
// only recipient can accept/reject share |
985
|
|
|
if ($share->getShareOwner() === $this->currentUser->getUID() || |
986
|
|
|
$share->getSharedBy() === $this->currentUser->getUID()) { |
987
|
|
|
$node->unlock(ILockingProvider::LOCK_SHARED); |
988
|
|
|
return new \OC\OCS\Result(null, 403, $this->l->t('Only recipient can change accepted state')); |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
if ($share->getState() === $state) { |
992
|
|
View Code Duplication |
if ($eventName !== '') { |
|
|
|
|
993
|
|
|
$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share])); |
994
|
|
|
} |
995
|
|
|
// if there are no changes in the state, just return the share as if the change was successful |
996
|
|
|
$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
997
|
|
|
return new \OC\OCS\Result([$this->formatShare($share, true)]); |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
// we actually want to update all shares related to the node in case there are multiple |
1001
|
|
|
// incoming shares for the same node (ex: receiving simultaneously through group share and user share) |
1002
|
|
|
$allShares = $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_USER, $node, -1, 0); |
1003
|
|
|
$allShares = \array_merge($allShares, $this->shareManager->getSharedWith($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0)); |
1004
|
|
|
|
1005
|
|
|
// resolve and deduplicate target if accepting |
1006
|
|
|
if ($state === \OCP\Share::STATE_ACCEPTED) { |
1007
|
|
|
$share = $this->deduplicateShareTarget($share); |
1008
|
|
|
} |
1009
|
|
|
|
1010
|
|
|
$share->setState($state); |
1011
|
|
|
|
1012
|
|
|
try { |
1013
|
|
|
foreach ($allShares as $aShare) { |
1014
|
|
|
$aShare->setState($share->getState()); |
1015
|
|
|
$aShare->setTarget($share->getTarget()); |
1016
|
|
|
$this->shareManager->updateShareForRecipient($aShare, $this->currentUser->getUID()); |
1017
|
|
|
} |
1018
|
|
|
} catch (\Exception $e) { |
1019
|
|
|
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); |
1020
|
|
|
return new \OC\OCS\Result(null, 400, $e->getMessage()); |
1021
|
|
|
} |
1022
|
|
|
|
1023
|
|
|
$node->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); |
1024
|
|
|
|
1025
|
|
|
// FIXME: needs public API! |
1026
|
|
|
\OC\Files\Filesystem::tearDown(); |
1027
|
|
|
// FIXME: trigger mount for user to make sure the new node is mounted already |
1028
|
|
|
// before formatShare resolves it |
1029
|
|
|
$this->rootFolder->getUserFolder($this->currentUser->getUID()); |
1030
|
|
|
|
1031
|
|
|
$this->notificationPublisher->discardNotificationForUser($share, $this->currentUser->getUID()); |
1032
|
|
|
|
1033
|
|
View Code Duplication |
if ($eventName !== '') { |
|
|
|
|
1034
|
|
|
$this->eventDispatcher->dispatch('share.after' . $eventName, new GenericEvent(null, ['share' => $share])); |
1035
|
|
|
} |
1036
|
|
|
return new \OC\OCS\Result([$this->formatShare($share, true)]); |
1037
|
|
|
} |
1038
|
|
|
|
1039
|
|
|
/** |
1040
|
|
|
* Deduplicate the share target in the current user home folder, |
1041
|
|
|
* based on configured share folder |
1042
|
|
|
* |
1043
|
|
|
* @param IShare $share share target to deduplicate |
1044
|
|
|
* @return IShare same share with target updated if necessary |
1045
|
|
|
*/ |
1046
|
|
|
private function deduplicateShareTarget(IShare $share) { |
1047
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); |
1048
|
|
|
$mountPoint = \basename($share->getTarget()); |
1049
|
|
|
$parentDir = \dirname($share->getTarget()); |
1050
|
|
|
if (!$userFolder->nodeExists($parentDir)) { |
1051
|
|
|
$parentDir = Helper::getShareFolder(); |
1052
|
|
|
$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $share->getTarget()); |
1053
|
|
|
} else { |
1054
|
|
|
$pathAttempt = \OC\Files\Filesystem::normalizePath($share->getTarget()); |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
|
|
$pathinfo = \pathinfo($pathAttempt); |
1058
|
|
|
$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; |
1059
|
|
|
$name = $pathinfo['filename']; |
1060
|
|
|
|
1061
|
|
|
$i = 2; |
1062
|
|
|
while ($userFolder->nodeExists($pathAttempt)) { |
1063
|
|
|
$pathAttempt = \OC\Files\Filesystem::normalizePath($parentDir . '/' . $name . ' ('.$i.')' . $ext); |
1064
|
|
|
$i++; |
1065
|
|
|
} |
1066
|
|
|
|
1067
|
|
|
$share->setTarget($pathAttempt); |
1068
|
|
|
|
1069
|
|
|
return $share; |
1070
|
|
|
} |
1071
|
|
|
|
1072
|
|
|
/** |
1073
|
|
|
* @param \OCP\Share\IShare $share |
1074
|
|
|
* @return bool |
1075
|
|
|
*/ |
1076
|
|
|
protected function canAccessShare(\OCP\Share\IShare $share) { |
1077
|
|
|
// A file with permissions 0 can't be accessed by us, |
1078
|
|
|
// unless it's a rejected sub-group share in which case we want it visible to let the user accept it again |
1079
|
|
|
if ($share->getPermissions() === 0 |
1080
|
|
|
&& !($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $share->getState() === \OCP\Share::STATE_REJECTED)) { |
1081
|
|
|
return false; |
1082
|
|
|
} |
1083
|
|
|
|
1084
|
|
|
// Owner of the file and the sharer of the file can always get share |
1085
|
|
|
if ($share->getShareOwner() === $this->currentUser->getUID() || |
1086
|
|
|
$share->getSharedBy() === $this->currentUser->getUID() |
1087
|
|
|
) { |
1088
|
|
|
return true; |
1089
|
|
|
} |
1090
|
|
|
|
1091
|
|
|
// If the share is shared with you (or a group you are a member of) |
1092
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
1093
|
|
|
$share->getSharedWith() === $this->currentUser->getUID()) { |
1094
|
|
|
return true; |
1095
|
|
|
} |
1096
|
|
|
|
1097
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
1098
|
|
|
$sharedWith = $this->groupManager->get($share->getSharedWith()); |
1099
|
|
|
if ($sharedWith !== null && $sharedWith->inGroup($this->currentUser)) { |
1100
|
|
|
return true; |
1101
|
|
|
} |
1102
|
|
|
} |
1103
|
|
|
|
1104
|
|
|
return false; |
1105
|
|
|
} |
1106
|
|
|
|
1107
|
|
|
/** |
1108
|
|
|
* Make sure that the passed date is valid ISO 8601 |
1109
|
|
|
* So YYYY-MM-DD |
1110
|
|
|
* If not throw an exception |
1111
|
|
|
* |
1112
|
|
|
* @param string $expireDate |
1113
|
|
|
* |
1114
|
|
|
* @throws \Exception |
1115
|
|
|
* @return \DateTime |
1116
|
|
|
*/ |
1117
|
|
|
private function parseDate($expireDate) { |
1118
|
|
|
try { |
1119
|
|
|
$date = new \DateTime($expireDate); |
1120
|
|
|
} catch (\Exception $e) { |
1121
|
|
|
throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
1122
|
|
|
} |
1123
|
|
|
|
1124
|
|
|
if ($date === false) { |
1125
|
|
|
throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
1126
|
|
|
} |
1127
|
|
|
|
1128
|
|
|
$date->setTime(0, 0, 0); |
1129
|
|
|
|
1130
|
|
|
return $date; |
1131
|
|
|
} |
1132
|
|
|
|
1133
|
|
|
/** |
1134
|
|
|
* Since we have multiple providers but the OCS Share API v1 does |
1135
|
|
|
* not support this we need to check all backends. |
1136
|
|
|
* |
1137
|
|
|
* @param string $id |
1138
|
|
|
* @return \OCP\Share\IShare |
1139
|
|
|
* @throws ShareNotFound |
1140
|
|
|
*/ |
1141
|
|
|
private function getShareById($id, $recipient = null) { |
1142
|
|
|
$share = null; |
1143
|
|
|
|
1144
|
|
|
// First check if it is an internal share. |
1145
|
|
|
try { |
1146
|
|
|
$share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient); |
1147
|
|
|
} catch (ShareNotFound $e) { |
1148
|
|
|
if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
1149
|
|
|
throw new ShareNotFound(); |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient); |
1153
|
|
|
} |
1154
|
|
|
|
1155
|
|
|
return $share; |
1156
|
|
|
} |
1157
|
|
|
} |
1158
|
|
|
|
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.