1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Björn Schießle <[email protected]> |
4
|
|
|
* @author Joas Schilling <[email protected]> |
5
|
|
|
* @author phisch <[email protected]> |
6
|
|
|
* @author Roeland Jago Douma <[email protected]> |
7
|
|
|
* @author Vincent Petry <[email protected]> |
8
|
|
|
* @author Piotr Mrowczynski <[email protected]> |
9
|
|
|
* |
10
|
|
|
* @copyright Copyright (c) 2018, ownCloud GmbH |
11
|
|
|
* @license AGPL-3.0 |
12
|
|
|
* |
13
|
|
|
* This code is free software: you can redistribute it and/or modify |
14
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
15
|
|
|
* as published by the Free Software Foundation. |
16
|
|
|
* |
17
|
|
|
* This program is distributed in the hope that it will be useful, |
18
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
19
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20
|
|
|
* GNU Affero General Public License for more details. |
21
|
|
|
* |
22
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
23
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
24
|
|
|
* |
25
|
|
|
*/ |
26
|
|
|
namespace OC\Share20; |
27
|
|
|
|
28
|
|
|
use OCP\Files\File; |
29
|
|
|
use OCP\Share\IShare; |
30
|
|
|
use OCP\Share\IShareProvider; |
31
|
|
|
use OC\Share20\Exception\InvalidShare; |
32
|
|
|
use OC\Share20\Exception\ProviderException; |
33
|
|
|
use OCP\Share\Exceptions\ShareNotFound; |
34
|
|
|
use OC\Share20\Exception\BackendError; |
35
|
|
|
use OCP\DB\QueryBuilder\IQueryBuilder; |
36
|
|
|
use OCP\IGroup; |
37
|
|
|
use OCP\IGroupManager; |
38
|
|
|
use OCP\IUserManager; |
39
|
|
|
use OCP\Files\IRootFolder; |
40
|
|
|
use OCP\IDBConnection; |
41
|
|
|
use OCP\Files\Node; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Class DefaultShareProvider |
45
|
|
|
* |
46
|
|
|
* @package OC\Share20 |
47
|
|
|
*/ |
48
|
|
|
class DefaultShareProvider implements IShareProvider { |
49
|
|
|
|
50
|
|
|
// Special share type for user modified group shares |
51
|
|
|
const SHARE_TYPE_USERGROUP = 2; |
52
|
|
|
|
53
|
|
|
/** @var IDBConnection */ |
54
|
|
|
private $dbConn; |
55
|
|
|
|
56
|
|
|
/** @var IUserManager */ |
57
|
|
|
private $userManager; |
58
|
|
|
|
59
|
|
|
/** @var IGroupManager */ |
60
|
|
|
private $groupManager; |
61
|
|
|
|
62
|
|
|
/** @var IRootFolder */ |
63
|
|
|
private $rootFolder; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* DefaultShareProvider constructor. |
67
|
|
|
* |
68
|
|
|
* @param IDBConnection $connection |
69
|
|
|
* @param IUserManager $userManager |
70
|
|
|
* @param IGroupManager $groupManager |
71
|
|
|
* @param IRootFolder $rootFolder |
72
|
|
|
*/ |
73
|
|
|
public function __construct( |
74
|
|
|
IDBConnection $connection, |
75
|
|
|
IUserManager $userManager, |
76
|
|
|
IGroupManager $groupManager, |
77
|
|
|
IRootFolder $rootFolder) { |
78
|
|
|
$this->dbConn = $connection; |
79
|
|
|
$this->userManager = $userManager; |
80
|
|
|
$this->groupManager = $groupManager; |
81
|
|
|
$this->rootFolder = $rootFolder; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Return the identifier of this provider. |
86
|
|
|
* |
87
|
|
|
* @return string Containing only [a-zA-Z0-9] |
88
|
|
|
*/ |
89
|
|
|
public function identifier() { |
90
|
|
|
return 'ocinternal'; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Share a path |
95
|
|
|
* |
96
|
|
|
* @param \OCP\Share\IShare $share |
97
|
|
|
* @return \OCP\Share\IShare The share object |
98
|
|
|
* @throws ShareNotFound |
99
|
|
|
* @throws InvalidArgumentException if the share validation failed |
100
|
|
|
* @throws \Exception |
101
|
|
|
*/ |
102
|
|
|
public function create(\OCP\Share\IShare $share) { |
103
|
|
|
$this->validate($share); |
104
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
105
|
|
|
|
106
|
|
|
$qb->insert('share'); |
107
|
|
|
$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType())); |
108
|
|
|
|
109
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
110
|
|
|
//Set the UID of the user we share with |
111
|
|
|
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); |
112
|
|
|
$qb->setValue('accepted', $share->getState()); |
113
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
114
|
|
|
//Set the GID of the group we share with |
115
|
|
|
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); |
116
|
|
|
$qb->setValue('accepted', $share->getState()); |
117
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
118
|
|
|
//Set the token of the share |
119
|
|
|
$qb->setValue('token', $qb->createNamedParameter($share->getToken())); |
120
|
|
|
|
121
|
|
|
//If a password is set store it |
122
|
|
|
if ($share->getPassword() !== null) { |
123
|
|
|
$qb->setValue('share_with', $qb->createNamedParameter($share->getPassword())); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
//If an expiration date is set store it |
127
|
|
|
if ($share->getExpirationDate() !== null) { |
128
|
|
|
$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (\method_exists($share, 'getParent')) { |
132
|
|
|
$qb->setValue('parent', $qb->createNamedParameter($share->getParent())); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// Set user-defined name |
136
|
|
|
$qb->setValue('share_name', $qb->createNamedParameter($share->getName())); |
137
|
|
|
} else { |
138
|
|
|
throw new \Exception('invalid share type!'); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
// Set what is shares |
142
|
|
|
$qb->setValue('item_type', $qb->createParameter('itemType')); |
143
|
|
|
if ($share->getNode() instanceof \OCP\Files\File) { |
144
|
|
|
$qb->setParameter('itemType', 'file'); |
145
|
|
|
} else { |
146
|
|
|
$qb->setParameter('itemType', 'folder'); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
// Set the file id |
150
|
|
|
$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId())); |
151
|
|
|
$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId())); |
152
|
|
|
|
153
|
|
|
// set the permissions |
154
|
|
|
$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions())); |
155
|
|
|
|
156
|
|
|
// Set who created this share |
157
|
|
|
$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy())); |
158
|
|
|
|
159
|
|
|
// Set who is the owner of this file/folder (and this the owner of the share) |
160
|
|
|
$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner())); |
161
|
|
|
|
162
|
|
|
// Set the file target |
163
|
|
|
$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget())); |
164
|
|
|
|
165
|
|
|
// Set the time this share was created |
166
|
|
|
$qb->setValue('stime', $qb->createNamedParameter(\time())); |
167
|
|
|
|
168
|
|
|
// insert the data and fetch the id of the share |
169
|
|
|
$this->dbConn->beginTransaction(); |
170
|
|
|
$qb->execute(); |
171
|
|
|
$id = $this->dbConn->lastInsertId('*PREFIX*share'); |
172
|
|
|
|
173
|
|
|
// Now fetch the inserted share and create a complete share object |
174
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
175
|
|
|
$qb->select('*') |
176
|
|
|
->from('share') |
177
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
178
|
|
|
|
179
|
|
|
$cursor = $qb->execute(); |
180
|
|
|
$data = $cursor->fetch(); |
181
|
|
|
$this->dbConn->commit(); |
182
|
|
|
$cursor->closeCursor(); |
183
|
|
|
|
184
|
|
|
if ($data === false) { |
185
|
|
|
throw new ShareNotFound(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
$share = $this->createShare($data); |
189
|
|
|
return $share; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Update a share |
194
|
|
|
* |
195
|
|
|
* @param \OCP\Share\IShare $share |
196
|
|
|
* @return \OCP\Share\IShare The share object |
197
|
|
|
* @throws InvalidArgumentException if the share validation failed |
198
|
|
|
*/ |
199
|
|
|
public function update(\OCP\Share\IShare $share) { |
200
|
|
|
$this->validate($share); |
201
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
202
|
|
|
/* |
203
|
|
|
* We allow updating the recipient on user shares. |
204
|
|
|
*/ |
205
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
206
|
|
|
$qb->update('share') |
207
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
208
|
|
|
->set('share_with', $qb->createNamedParameter($share->getSharedWith())) |
209
|
|
|
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
210
|
|
|
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
211
|
|
|
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
212
|
|
|
->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) |
213
|
|
|
->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) |
214
|
|
|
->set('accepted', $qb->createNamedParameter($share->getState())) |
215
|
|
|
->set('mail_send', $qb->createNamedParameter((int) $share->getMailSend())) |
216
|
|
|
->execute(); |
217
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
218
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
219
|
|
|
$qb->update('share') |
220
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
221
|
|
|
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
222
|
|
|
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
223
|
|
|
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
224
|
|
|
->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) |
225
|
|
|
->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) |
226
|
|
|
->set('accepted', $qb->createNamedParameter($share->getState())) |
227
|
|
|
->set('mail_send', $qb->createNamedParameter((int) $share->getMailSend())) |
228
|
|
|
->execute(); |
229
|
|
|
|
230
|
|
|
/* |
231
|
|
|
* Update all user defined group shares |
232
|
|
|
*/ |
233
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
234
|
|
|
$qb->update('share') |
235
|
|
|
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) |
236
|
|
|
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
237
|
|
|
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
238
|
|
|
->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) |
239
|
|
|
->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) |
240
|
|
|
->execute(); |
241
|
|
|
|
242
|
|
|
/* |
243
|
|
|
* Now update the permissions for all children |
244
|
|
|
*/ |
245
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
246
|
|
|
$qb->update('share') |
247
|
|
|
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) |
248
|
|
|
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
249
|
|
|
->execute(); |
250
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
251
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
252
|
|
|
$qb->update('share') |
253
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
254
|
|
|
->set('share_with', $qb->createNamedParameter($share->getPassword())) |
255
|
|
|
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
256
|
|
|
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
257
|
|
|
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
258
|
|
|
->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) |
259
|
|
|
->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) |
260
|
|
|
->set('token', $qb->createNamedParameter($share->getToken())) |
261
|
|
|
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
262
|
|
|
->set('share_name', $qb->createNamedParameter($share->getName())) |
263
|
|
|
->execute(); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
return $share; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Get all children of this share |
271
|
|
|
* FIXME: remove once https://github.com/owncloud/core/pull/21660 is in |
272
|
|
|
* |
273
|
|
|
* @param \OCP\Share\IShare $parent |
274
|
|
|
* @return \OCP\Share\IShare[] |
275
|
|
|
*/ |
276
|
|
|
public function getChildren(\OCP\Share\IShare $parent) { |
277
|
|
|
$children = []; |
278
|
|
|
|
279
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
280
|
|
|
$qb->select('*') |
281
|
|
|
->from('share') |
282
|
|
|
->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
283
|
|
|
->andWhere( |
284
|
|
|
$qb->expr()->in( |
285
|
|
|
'share_type', |
286
|
|
|
$qb->createNamedParameter([ |
287
|
|
|
\OCP\Share::SHARE_TYPE_USER, |
288
|
|
|
\OCP\Share::SHARE_TYPE_GROUP, |
289
|
|
|
\OCP\Share::SHARE_TYPE_LINK, |
290
|
|
|
], IQueryBuilder::PARAM_INT_ARRAY) |
291
|
|
|
) |
292
|
|
|
) |
293
|
|
|
->andWhere($qb->expr()->orX( |
294
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
295
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
296
|
|
|
)) |
297
|
|
|
->orderBy('id'); |
298
|
|
|
|
299
|
|
|
$cursor = $qb->execute(); |
300
|
|
|
while ($data = $cursor->fetch()) { |
301
|
|
|
$children[] = $this->createShare($data); |
302
|
|
|
} |
303
|
|
|
$cursor->closeCursor(); |
304
|
|
|
|
305
|
|
|
return $children; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Delete a share |
310
|
|
|
* |
311
|
|
|
* @param \OCP\Share\IShare $share |
312
|
|
|
*/ |
313
|
|
|
public function delete(\OCP\Share\IShare $share) { |
314
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
315
|
|
|
$qb->delete('share') |
316
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))); |
317
|
|
|
|
318
|
|
|
/* |
319
|
|
|
* If the share is a group share delete all possible |
320
|
|
|
* user defined groups shares. |
321
|
|
|
*/ |
322
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
323
|
|
|
$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
$qb->execute(); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Unshare a share from the recipient. If this is a group share |
331
|
|
|
* this means we need a special entry in the share db. |
332
|
|
|
* |
333
|
|
|
* @param \OCP\Share\IShare $share |
334
|
|
|
* @param string $recipient UserId of recipient |
335
|
|
|
* @throws BackendError |
336
|
|
|
* @throws ProviderException |
337
|
|
|
*/ |
338
|
|
|
public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { |
339
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP || $share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
340
|
|
|
$share->setState(\OCP\Share::STATE_REJECTED); |
341
|
|
|
$this->updateForRecipient($share, $recipient); |
342
|
|
|
} else { |
343
|
|
|
throw new ProviderException('Invalid share type ' . $share->getShareType()); |
344
|
|
|
} |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @inheritdoc |
349
|
|
|
*/ |
350
|
|
|
public function move(\OCP\Share\IShare $share, $recipient) { |
351
|
|
|
return $this->updateForRecipient($share, $recipient); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* @inheritdoc |
356
|
|
|
*/ |
357
|
|
|
public function updateForRecipient(\OCP\Share\IShare $share, $recipient) { |
358
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
359
|
|
|
if ($share->getSharedWith() !== $recipient) { |
360
|
|
|
throw new ProviderException('Recipient does not match'); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
// Just update the target |
364
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
365
|
|
|
$qb->update('share') |
366
|
|
|
->set('accepted', $qb->createNamedParameter($share->getState())) |
367
|
|
|
->set('file_target', $qb->createNamedParameter($share->getTarget())) |
368
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
369
|
|
|
->execute(); |
370
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
371
|
|
|
$group = $this->groupManager->get($share->getSharedWith()); |
372
|
|
|
$user = $this->userManager->get($recipient); |
373
|
|
|
|
374
|
|
|
if ($group === null) { |
375
|
|
|
throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist'); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
if (!$group->inGroup($user)) { |
|
|
|
|
379
|
|
|
throw new ProviderException('Recipient not in receiving group'); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
// Check if there is a usergroup share |
383
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
384
|
|
|
$stmt = $qb->select('id') |
385
|
|
|
->from('share') |
386
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) |
387
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) |
388
|
|
|
->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) |
389
|
|
|
->andWhere($qb->expr()->orX( |
390
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
391
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
392
|
|
|
)) |
393
|
|
|
->setMaxResults(1) |
394
|
|
|
->execute(); |
395
|
|
|
|
396
|
|
|
$data = $stmt->fetch(); |
397
|
|
|
$stmt->closeCursor(); |
398
|
|
|
|
399
|
|
|
if ($data === false) { |
400
|
|
|
// No usergroup share yet. Create one. |
401
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
402
|
|
|
$qb->insert('share') |
403
|
|
|
->values([ |
404
|
|
|
'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), |
405
|
|
|
'share_with' => $qb->createNamedParameter($recipient), |
406
|
|
|
'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), |
407
|
|
|
'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), |
408
|
|
|
'parent' => $qb->createNamedParameter($share->getId()), |
409
|
|
|
'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'), |
410
|
|
|
'item_source' => $qb->createNamedParameter($share->getNode()->getId()), |
411
|
|
|
'file_source' => $qb->createNamedParameter($share->getNode()->getId()), |
412
|
|
|
'file_target' => $qb->createNamedParameter($share->getTarget()), |
413
|
|
|
'permissions' => $qb->createNamedParameter($share->getPermissions()), |
414
|
|
|
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), |
415
|
|
|
'accepted' => $qb->createNamedParameter($share->getState()), |
416
|
|
|
])->execute(); |
417
|
|
|
} else { |
418
|
|
|
// Already a usergroup share. Update it. |
419
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
420
|
|
|
$qb->update('share') |
421
|
|
|
->set('accepted', $qb->createNamedParameter($share->getState())) |
422
|
|
|
->set('file_target', $qb->createNamedParameter($share->getTarget())) |
423
|
|
|
// make sure to reset the permissions to the one of the parent share, |
424
|
|
|
// as legacy entries with zero permissions might still exist |
425
|
|
|
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
426
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) |
427
|
|
|
->execute(); |
428
|
|
|
} |
429
|
|
|
} else { |
430
|
|
|
throw new ProviderException('Can\'t update share of recipient for share type ' . $share->getShareType()); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
return $share; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @inheritdoc |
438
|
|
|
*/ |
439
|
|
|
public function getAllSharesBy($userId, $shareTypes, $nodeIDs, $reshares) { |
440
|
|
|
$shares = []; |
441
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
442
|
|
|
|
443
|
|
|
$nodeIdsChunks = \array_chunk($nodeIDs, 100); |
444
|
|
|
foreach ($nodeIdsChunks as $nodeIdsChunk) { |
445
|
|
|
$qb->select('*') |
446
|
|
|
->from('share') |
447
|
|
|
->andWhere($qb->expr()->orX( |
448
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
449
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
450
|
|
|
)); |
451
|
|
|
|
452
|
|
|
$orX = $qb->expr()->orX(); |
453
|
|
|
|
454
|
|
|
foreach ($shareTypes as $shareType) { |
455
|
|
|
$orX->add($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
$qb->andWhere($orX); |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Reshares for this user are shares where they are the owner. |
462
|
|
|
*/ |
463
|
|
|
if ($reshares === false) { |
464
|
|
|
$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
465
|
|
|
} else { |
466
|
|
|
$qb->andWhere( |
467
|
|
|
$qb->expr()->orX( |
468
|
|
|
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
469
|
|
|
$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
|
|
|
470
|
|
|
) |
471
|
|
|
); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
$qb->andWhere($qb->expr()->in('file_source', $qb->createParameter('file_source_ids'))); |
475
|
|
|
$qb->setParameter('file_source_ids', $nodeIdsChunk, IQueryBuilder::PARAM_INT_ARRAY); |
476
|
|
|
|
477
|
|
|
$qb->orderBy('id'); |
478
|
|
|
|
479
|
|
|
$cursor = $qb->execute(); |
480
|
|
|
while ($data = $cursor->fetch()) { |
481
|
|
|
$shares[] = $this->createShare($data); |
482
|
|
|
} |
483
|
|
|
$cursor->closeCursor(); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
return $shares; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* @inheritdoc |
491
|
|
|
*/ |
492
|
|
|
public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
493
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
494
|
|
|
$qb->select('*') |
495
|
|
|
->from('share') |
496
|
|
|
->andWhere($qb->expr()->orX( |
497
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
498
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
499
|
|
|
)); |
500
|
|
|
|
501
|
|
|
$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* Reshares for this user are shares where they are the owner. |
505
|
|
|
*/ |
506
|
|
|
if ($reshares === false) { |
507
|
|
|
$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
508
|
|
|
} else { |
509
|
|
|
$qb->andWhere( |
510
|
|
|
$qb->expr()->orX( |
511
|
|
|
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
512
|
|
|
$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
|
|
|
513
|
|
|
) |
514
|
|
|
); |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
if ($node !== null) { |
518
|
|
|
$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
if ($limit !== -1) { |
522
|
|
|
$qb->setMaxResults($limit); |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
$qb->setFirstResult($offset); |
526
|
|
|
$qb->orderBy('id'); |
527
|
|
|
|
528
|
|
|
$cursor = $qb->execute(); |
529
|
|
|
$shares = []; |
530
|
|
|
while ($data = $cursor->fetch()) { |
531
|
|
|
$shares[] = $this->createShare($data); |
532
|
|
|
} |
533
|
|
|
$cursor->closeCursor(); |
534
|
|
|
|
535
|
|
|
return $shares; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* @inheritdoc |
540
|
|
|
*/ |
541
|
|
|
public function getShareById($id, $recipientId = null) { |
542
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
543
|
|
|
|
544
|
|
|
$qb->select('*') |
545
|
|
|
->from('share') |
546
|
|
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
547
|
|
|
->andWhere( |
548
|
|
|
$qb->expr()->in( |
549
|
|
|
'share_type', |
550
|
|
|
$qb->createNamedParameter([ |
551
|
|
|
\OCP\Share::SHARE_TYPE_USER, |
552
|
|
|
\OCP\Share::SHARE_TYPE_GROUP, |
553
|
|
|
\OCP\Share::SHARE_TYPE_LINK, |
554
|
|
|
], IQueryBuilder::PARAM_INT_ARRAY) |
555
|
|
|
) |
556
|
|
|
) |
557
|
|
|
->andWhere($qb->expr()->orX( |
558
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
559
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
560
|
|
|
)); |
561
|
|
|
|
562
|
|
|
$cursor = $qb->execute(); |
563
|
|
|
$data = $cursor->fetch(); |
564
|
|
|
$cursor->closeCursor(); |
565
|
|
|
|
566
|
|
|
if ($data === false) { |
567
|
|
|
throw new ShareNotFound(); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
try { |
571
|
|
|
$share = $this->createShare($data); |
572
|
|
|
} catch (InvalidShare $e) { |
573
|
|
|
throw new ShareNotFound(); |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
// If the recipient is set for a group share resolve to that user |
577
|
|
|
if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
578
|
|
|
$resolvedShares = $this->resolveGroupShares([$share], $recipientId); |
579
|
|
|
if (\count($resolvedShares) === 1) { |
580
|
|
|
// If we pass to resolveGroupShares() an with one element, |
581
|
|
|
// we expect to receive exactly one element, otherwise it is error |
582
|
|
|
$share = $resolvedShares[0]; |
583
|
|
|
} else { |
584
|
|
|
throw new ProviderException("ResolveGroupShares() returned wrong result"); |
585
|
|
|
} |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
return $share; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
/** |
592
|
|
|
* Get shares for a given path |
593
|
|
|
* |
594
|
|
|
* @param \OCP\Files\Node $path |
595
|
|
|
* @return \OCP\Share\IShare[] |
596
|
|
|
*/ |
597
|
|
|
public function getSharesByPath(Node $path) { |
598
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
599
|
|
|
|
600
|
|
|
$cursor = $qb->select('*') |
601
|
|
|
->from('share') |
602
|
|
|
->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
603
|
|
|
->andWhere( |
604
|
|
|
$qb->expr()->orX( |
605
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), |
606
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)) |
|
|
|
|
607
|
|
|
) |
608
|
|
|
) |
609
|
|
|
->andWhere($qb->expr()->orX( |
610
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
611
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
612
|
|
|
)) |
613
|
|
|
->execute(); |
614
|
|
|
|
615
|
|
|
$shares = []; |
616
|
|
|
while ($data = $cursor->fetch()) { |
617
|
|
|
$shares[] = $this->createShare($data); |
618
|
|
|
} |
619
|
|
|
$cursor->closeCursor(); |
620
|
|
|
|
621
|
|
|
return $shares; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Returns whether the given database result can be interpreted as |
626
|
|
|
* a share with accessible file (not trashed, not deleted) |
627
|
|
|
*/ |
628
|
|
|
private function isAccessibleResult($data) { |
629
|
|
|
// exclude shares leading to deleted file entries |
630
|
|
|
if ($data['fileid'] === null) { |
631
|
|
|
return false; |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
// exclude shares leading to trashbin on home storages |
635
|
|
|
$pathSections = \explode('/', $data['path'], 2); |
636
|
|
|
// FIXME: would not detect rare md5'd home storage case properly |
637
|
|
|
$storagePrefix = \explode(':', $data['storage_string_id'], 2)[0]; |
638
|
|
|
if ($pathSections[0] !== 'files' && \in_array($storagePrefix, ['home', 'object'], true)) { |
639
|
|
|
return false; |
640
|
|
|
} |
641
|
|
|
return true; |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
/* |
645
|
|
|
* Get shared with user shares for the given userId and node |
646
|
|
|
* |
647
|
|
|
* @param string $userId |
648
|
|
|
* @param Node|null $node |
649
|
|
|
* @return DB\QueryBuilder\IQueryBuilder $qb |
650
|
|
|
*/ |
651
|
|
|
public function getSharedWithUserQuery($userId, $node) { |
652
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
653
|
|
|
$qb->select('s.*', 'f.fileid', 'f.path') |
|
|
|
|
654
|
|
|
->selectAlias('st.id', 'storage_string_id') |
655
|
|
|
->from('share', 's') |
656
|
|
|
->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) |
657
|
|
|
->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')); |
658
|
|
|
|
659
|
|
|
// Order by id |
660
|
|
|
$qb->orderBy('s.id'); |
661
|
|
|
|
662
|
|
|
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))) |
663
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) |
664
|
|
|
->andWhere($qb->expr()->orX( |
665
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
666
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
667
|
|
|
)); |
668
|
|
|
|
669
|
|
|
// Filter by node if provided |
670
|
|
|
if ($node !== null) { |
671
|
|
|
$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
672
|
|
|
} |
673
|
|
|
|
674
|
|
|
return $qb; |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
/* |
678
|
|
|
* Get shared with group shares for the given groups and node |
679
|
|
|
* |
680
|
|
|
* @param IGroup[] $groups |
681
|
|
|
* @param Node|null $node |
682
|
|
|
* @return DB\QueryBuilder\IQueryBuilder $qb |
683
|
|
|
*/ |
684
|
|
|
private function getSharedWithGroupQuery($groups, $node) { |
685
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
686
|
|
|
$qb->select('s.*', 'f.fileid', 'f.path') |
|
|
|
|
687
|
|
|
->selectAlias('st.id', 'storage_string_id') |
688
|
|
|
->from('share', 's') |
689
|
|
|
->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) |
690
|
|
|
->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) |
691
|
|
|
->orderBy('s.id'); |
692
|
|
|
|
693
|
|
|
// Filter by node if provided |
694
|
|
|
if ($node !== null) { |
695
|
|
|
$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
$groups = \array_map(function (IGroup $group) { |
699
|
|
|
return $group->getGID(); |
700
|
|
|
}, $groups); |
701
|
|
|
|
702
|
|
|
$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) |
703
|
|
|
->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter( |
704
|
|
|
$groups, |
705
|
|
|
IQueryBuilder::PARAM_STR_ARRAY |
706
|
|
|
))) |
707
|
|
|
->andWhere($qb->expr()->orX( |
708
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
709
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
710
|
|
|
)); |
711
|
|
|
|
712
|
|
|
return $qb; |
713
|
|
|
} |
714
|
|
|
|
715
|
|
|
/* |
716
|
|
|
* Get shared with group and shared with user shares for the given groups, userId and node |
717
|
|
|
* |
718
|
|
|
* @param IGroup[] $groups |
719
|
|
|
* @param string $userId |
720
|
|
|
* @param Node|null $node |
721
|
|
|
* @return DB\QueryBuilder\IQueryBuilder $qb |
722
|
|
|
*/ |
723
|
|
|
private function getSharedWithUserGroupQuery($groups, $userId, $node) { |
724
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
725
|
|
|
$qb->select('s.*', 'f.fileid', 'f.path') |
|
|
|
|
726
|
|
|
->selectAlias('st.id', 'storage_string_id') |
727
|
|
|
->from('share', 's') |
728
|
|
|
->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) |
729
|
|
|
->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) |
730
|
|
|
->orderBy('s.id'); |
731
|
|
|
|
732
|
|
|
// Filter by node if provided |
733
|
|
|
if ($node !== null) { |
734
|
|
|
$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
$groups = \array_map(function (IGroup $group) { |
738
|
|
|
return $group->getGID(); |
739
|
|
|
}, $groups); |
740
|
|
|
|
741
|
|
|
$qb->andWhere($qb->expr()->orX( |
742
|
|
|
$qb->expr()->andX( |
743
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), |
744
|
|
|
$qb->expr()->in('share_with', $qb->createNamedParameter( |
|
|
|
|
745
|
|
|
$groups, |
746
|
|
|
IQueryBuilder::PARAM_STR_ARRAY |
747
|
|
|
)) |
748
|
|
|
), |
749
|
|
|
$qb->expr()->andX( |
|
|
|
|
750
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), |
751
|
|
|
$qb->expr()->eq('share_with', $qb->createNamedParameter($userId)) |
|
|
|
|
752
|
|
|
) |
753
|
|
|
)); |
754
|
|
|
|
755
|
|
|
return $qb; |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
/** |
759
|
|
|
* @inheritdoc |
760
|
|
|
*/ |
761
|
|
|
public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
762
|
|
|
/** @var Share[] $shares */ |
763
|
|
|
$shares = []; |
764
|
|
|
|
765
|
|
|
if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
766
|
|
|
// Create SharedWithUser query |
767
|
|
|
$qb = $this->getSharedWithUserQuery($userId, $node); |
768
|
|
|
|
769
|
|
|
// Set limit and offset |
770
|
|
|
if ($limit !== -1) { |
771
|
|
|
$qb->setMaxResults($limit); |
772
|
|
|
} |
773
|
|
|
$qb->setFirstResult($offset); |
774
|
|
|
|
775
|
|
|
$cursor = $qb->execute(); |
776
|
|
|
|
777
|
|
|
while ($data = $cursor->fetch()) { |
778
|
|
|
if ($this->isAccessibleResult($data)) { |
779
|
|
|
$shares[] = $this->createShare($data); |
780
|
|
|
} |
781
|
|
|
} |
782
|
|
|
$cursor->closeCursor(); |
783
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
784
|
|
|
$user = $this->userManager->get($userId); |
785
|
|
|
$allGroups = $this->groupManager->getUserGroups($user, 'sharing'); |
786
|
|
|
|
787
|
|
|
/** @var Share[] $shares2 */ |
788
|
|
|
$shares2 = []; |
789
|
|
|
|
790
|
|
|
$start = 0; |
791
|
|
|
while (true) { |
792
|
|
|
$groups = \array_slice($allGroups, $start, 100); |
793
|
|
|
$start += 100; |
794
|
|
|
|
795
|
|
|
if ($groups === []) { |
796
|
|
|
break; |
797
|
|
|
} |
798
|
|
|
|
799
|
|
|
// Create SharedWithGroups query |
800
|
|
|
$qb = $this->getSharedWithGroupQuery($groups, $node); |
801
|
|
|
$qb->setFirstResult(0); |
802
|
|
|
|
803
|
|
|
if ($limit !== -1) { |
804
|
|
|
$qb->setMaxResults($limit - \count($shares)); |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
$cursor = $qb->execute(); |
808
|
|
|
while ($data = $cursor->fetch()) { |
809
|
|
|
if ($offset > 0) { |
810
|
|
|
$offset--; |
811
|
|
|
continue; |
812
|
|
|
} |
813
|
|
|
|
814
|
|
|
if ($this->isAccessibleResult($data)) { |
815
|
|
|
$shares2[] = $this->createShare($data); |
816
|
|
|
} |
817
|
|
|
} |
818
|
|
|
$cursor->closeCursor(); |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
//Resolve all group shares to user specific shares |
822
|
|
|
if (!empty($shares2)) { |
823
|
|
|
$resolvedGroupShares = $this->resolveGroupShares($shares2, $userId); |
824
|
|
|
$shares = \array_merge($shares, $resolvedGroupShares); |
825
|
|
|
} |
826
|
|
|
} else { |
827
|
|
|
throw new BackendError('Invalid backend'); |
828
|
|
|
} |
829
|
|
|
|
830
|
|
|
return $shares; |
831
|
|
|
} |
832
|
|
|
|
833
|
|
|
/** |
834
|
|
|
* @inheritdoc |
835
|
|
|
*/ |
836
|
|
|
public function getAllSharedWith($userId, $node) { |
837
|
|
|
// Create array of sharedWith objects (target user -> $userId or group of which user is a member |
838
|
|
|
$user = $this->userManager->get($userId); |
839
|
|
|
|
840
|
|
|
// Check if user is member of some groups and chunk them |
841
|
|
|
$allGroups = $this->groupManager->getUserGroups($user, 'sharing'); |
842
|
|
|
|
843
|
|
|
// Make chunks |
844
|
|
|
$sharedWithGroupChunks = \array_chunk($allGroups, 100); |
845
|
|
|
|
846
|
|
|
// Check how many group chunks do we need |
847
|
|
|
$sharedWithGroupChunksNo = \count($sharedWithGroupChunks); |
848
|
|
|
|
849
|
|
|
// If there are not groups, query only user, if there are groups, query both |
850
|
|
|
$chunkedResults = []; |
851
|
|
|
if ($sharedWithGroupChunksNo === 0) { |
852
|
|
|
// There are no groups, query only for user |
853
|
|
|
$qb = $this->getSharedWithUserQuery($userId, $node); |
854
|
|
|
$cursor = $qb->execute(); |
855
|
|
|
$chunkedResults[] = $cursor->fetchAll(); |
856
|
|
|
$cursor->closeCursor(); |
857
|
|
|
} else { |
858
|
|
|
// There are groups, query both for user and for groups |
859
|
|
|
$userSharesRetrieved = false; |
860
|
|
|
for ($chunkNo = 0; $chunkNo < $sharedWithGroupChunksNo; $chunkNo++) { |
861
|
|
|
// Get respective group chunk |
862
|
|
|
$groups = $sharedWithGroupChunks[$chunkNo]; |
863
|
|
|
|
864
|
|
|
// Check if user shares were already retrieved |
865
|
|
|
// One cannot retrieve user shares multiple times, since it will result in duplicated |
866
|
|
|
// user shares with each query |
867
|
|
|
if ($userSharesRetrieved === false) { |
868
|
|
|
$qb = $this->getSharedWithUserGroupQuery($groups, $userId, $node); |
869
|
|
|
$userSharesRetrieved = true; |
870
|
|
|
} else { |
871
|
|
|
$qb = $this->getSharedWithGroupQuery($groups, $node); |
872
|
|
|
} |
873
|
|
|
$cursor = $qb->execute(); |
874
|
|
|
$chunkedResults[] = $cursor->fetchAll(); |
875
|
|
|
$cursor->closeCursor(); |
876
|
|
|
} |
877
|
|
|
} |
878
|
|
|
|
879
|
|
|
$resolvedShares = []; |
880
|
|
|
$groupShares = []; |
881
|
|
|
foreach ($chunkedResults as $resultBatch) { |
882
|
|
|
foreach ($resultBatch as $data) { |
883
|
|
|
if ($this->isAccessibleResult($data)) { |
884
|
|
|
$share = $this->createShare($data); |
885
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
886
|
|
|
$groupShares[] = $share; |
887
|
|
|
} else { |
888
|
|
|
$resolvedShares[] = $share; |
889
|
|
|
} |
890
|
|
|
} |
891
|
|
|
} |
892
|
|
|
} |
893
|
|
|
|
894
|
|
|
//Resolve all group shares to user specific shares |
895
|
|
|
if (!empty($groupShares)) { |
896
|
|
|
$resolvedGroupShares = $this->resolveGroupShares($groupShares, $userId); |
897
|
|
|
$resolvedShares = \array_merge($resolvedShares, $resolvedGroupShares); |
898
|
|
|
} |
899
|
|
|
|
900
|
|
|
return $resolvedShares; |
901
|
|
|
} |
902
|
|
|
|
903
|
|
|
/** |
904
|
|
|
* Get a share by token |
905
|
|
|
* |
906
|
|
|
* @param string $token |
907
|
|
|
* @return \OCP\Share\IShare |
908
|
|
|
* @throws ShareNotFound |
909
|
|
|
*/ |
910
|
|
|
public function getShareByToken($token) { |
911
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
912
|
|
|
|
913
|
|
|
$cursor = $qb->select('*') |
914
|
|
|
->from('share') |
915
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))) |
916
|
|
|
->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
917
|
|
|
->andWhere($qb->expr()->orX( |
918
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
919
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
920
|
|
|
)) |
921
|
|
|
->execute(); |
922
|
|
|
|
923
|
|
|
$data = $cursor->fetch(); |
924
|
|
|
|
925
|
|
|
if ($data === false) { |
926
|
|
|
throw new ShareNotFound(); |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
try { |
930
|
|
|
$share = $this->createShare($data); |
931
|
|
|
} catch (InvalidShare $e) { |
932
|
|
|
throw new ShareNotFound(); |
933
|
|
|
} |
934
|
|
|
|
935
|
|
|
return $share; |
936
|
|
|
} |
937
|
|
|
|
938
|
|
|
/** |
939
|
|
|
* Create a share object from an database row |
940
|
|
|
* |
941
|
|
|
* @param mixed[] $data |
942
|
|
|
* @return \OCP\Share\IShare |
943
|
|
|
* @throws InvalidShare |
944
|
|
|
*/ |
945
|
|
|
private function createShare($data) { |
946
|
|
|
$share = new Share($this->rootFolder, $this->userManager); |
947
|
|
|
$share->setId((int)$data['id']) |
948
|
|
|
->setShareType((int)$data['share_type']) |
949
|
|
|
->setPermissions((int)$data['permissions']) |
950
|
|
|
->setTarget($data['file_target']) |
951
|
|
|
->setMailSend((bool)$data['mail_send']); |
952
|
|
|
|
953
|
|
|
$shareTime = new \DateTime(); |
954
|
|
|
$shareTime->setTimestamp((int)$data['stime']); |
955
|
|
|
$share->setShareTime($shareTime); |
956
|
|
|
|
957
|
|
|
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
958
|
|
|
$share->setSharedWith($data['share_with']); |
959
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
960
|
|
|
$share->setSharedWith($data['share_with']); |
961
|
|
|
} elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
962
|
|
|
$share->setPassword($data['share_with']); |
963
|
|
|
$share->setToken($data['token']); |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
$share->setSharedBy($data['uid_initiator']); |
967
|
|
|
$share->setShareOwner($data['uid_owner']); |
968
|
|
|
|
969
|
|
|
$share->setNodeId((int)$data['file_source']); |
970
|
|
|
$share->setNodeType($data['item_type']); |
971
|
|
|
$share->setName($data['share_name']); |
972
|
|
|
$share->setState((int)$data['accepted']); |
973
|
|
|
|
974
|
|
|
if ($data['expiration'] !== null) { |
975
|
|
|
$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
976
|
|
|
$share->setExpirationDate($expiration); |
|
|
|
|
977
|
|
|
} |
978
|
|
|
|
979
|
|
|
$share->setProviderId($this->identifier()); |
980
|
|
|
|
981
|
|
|
return $share; |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
/** |
985
|
|
|
* Will return two maps: |
986
|
|
|
* - $chunkedShareIds responsible to split shareIds into chunks containing 100 elements |
987
|
|
|
* e.g. $chunkedShareIds { { "4", "52", "54",... }[100], { .. }[2] }[2] |
988
|
|
|
* |
989
|
|
|
* - $shareIdToShareMap responsible to split shareIds into chunks containing 100 elements |
990
|
|
|
* e.g. $shareIdToShareMap { "4" => IShare, "52" => IShare, "54" => IShare, ... }[102] |
991
|
|
|
* |
992
|
|
|
* @param \OCP\Share\IShare[] $shares |
993
|
|
|
* @return array $chunkedSharesToMaps e.g { $chunkedShareIds, $shareIdToShareMap }[2] |
994
|
|
|
*/ |
995
|
|
|
private function chunkSharesToMaps($shares) { |
996
|
|
|
$chunkedShareIds = []; |
997
|
|
|
$shareIdToShareMap = []; |
998
|
|
|
$chunkId = 0; |
999
|
|
|
$shareNo = 0; |
1000
|
|
|
foreach ($shares as $share) { |
1001
|
|
|
// Map unique shareIds to IShare |
1002
|
|
|
$shareId = $share->getId(); |
1003
|
|
|
$shareIdToShareMap[$shareId] = $share; |
1004
|
|
|
|
1005
|
|
|
// Chunk shareId array |
1006
|
|
|
if ($shareNo >= 100) { |
1007
|
|
|
// If we have over 100 shares in the array, start next chunk |
1008
|
|
|
$shareNo = 0; |
1009
|
|
|
$chunkId++; |
1010
|
|
|
} else { |
1011
|
|
|
// Increase number of shares in current array |
1012
|
|
|
$shareNo++; |
1013
|
|
|
} |
1014
|
|
|
$chunkedShareIds[$chunkId][] = $shareId; |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
$chunkedSharesToMaps = [$chunkedShareIds, $shareIdToShareMap]; |
1018
|
|
|
return $chunkedSharesToMaps; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
/** |
1022
|
|
|
* Resolve a group shares to a user specific share. |
1023
|
|
|
* Thus if the user moved their group share make sure this is properly reflected here, |
1024
|
|
|
* If $shares array contains exactly 2 elements, where |
1025
|
|
|
* only 1 will be changed(resolved), it returns exactly 2 elements, containing the resolved one. |
1026
|
|
|
* |
1027
|
|
|
* @param \OCP\Share\IShare[] $shares e.g. { IShare, IShare }[2] |
1028
|
|
|
* @param string $userId |
1029
|
|
|
* @return \OCP\Share\IShare[] $resolvedShares |
1030
|
|
|
* @throws ProviderException |
1031
|
|
|
*/ |
1032
|
|
|
private function resolveGroupShares($shares, $userId) { |
1033
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
1034
|
|
|
|
1035
|
|
|
list($chunkedShareIds, $shareIdToShareMap) = $this->chunkSharesToMaps($shares); |
1036
|
|
|
foreach ($chunkedShareIds as $shareIdsChunk) { |
1037
|
|
|
$qb->select('*') |
1038
|
|
|
->from('share') |
1039
|
|
|
->where($qb->expr()->in('parent', $qb->createNamedParameter( |
1040
|
|
|
$shareIdsChunk, |
1041
|
|
|
IQueryBuilder::PARAM_STR_ARRAY |
1042
|
|
|
))) |
1043
|
|
|
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) |
1044
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) |
1045
|
|
|
->andWhere($qb->expr()->orX( |
1046
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
1047
|
|
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
|
|
|
1048
|
|
|
)); |
1049
|
|
|
|
1050
|
|
|
$stmt = $qb->execute(); |
1051
|
|
|
|
1052
|
|
|
// Resolve $shareIdToShareMap array containing group shares |
1053
|
|
|
$shareParents = []; |
1054
|
|
|
while ($data = $stmt->fetch()) { |
1055
|
|
|
// Get share parent |
1056
|
|
|
$shareParent = $data['parent']; |
1057
|
|
|
|
1058
|
|
|
// Ensure uniquenes of parents |
1059
|
|
|
if (!isset($shareParents[$shareParent])) { |
1060
|
|
|
$shareParents[$shareParent] = true; |
1061
|
|
|
} else { |
1062
|
|
|
throw new ProviderException('Parent of share should be unique'); |
1063
|
|
|
} |
1064
|
|
|
|
1065
|
|
|
// Resolve only shares contained in the map. |
1066
|
|
|
// This will ensure that we return the same amount of shares in the input as in the output |
1067
|
|
|
// If $shareParent is contained in $shareIdToShareMap, it means that needs resolving |
1068
|
|
|
if (isset($shareIdToShareMap[$shareParent])) { |
1069
|
|
|
$share = $shareIdToShareMap[$shareParent]; |
1070
|
|
|
$share->setState(\intval($data['accepted'])); |
1071
|
|
|
$share->setTarget($data['file_target']); |
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
$stmt->closeCursor(); |
1075
|
|
|
} |
1076
|
|
|
|
1077
|
|
|
$resolvedShares = \array_values($shareIdToShareMap); |
1078
|
|
|
return $resolvedShares; |
1079
|
|
|
} |
1080
|
|
|
|
1081
|
|
|
/** |
1082
|
|
|
* A user is deleted from the system |
1083
|
|
|
* So clean up the relevant shares. |
1084
|
|
|
* |
1085
|
|
|
* @param string $uid |
1086
|
|
|
* @param int $shareType |
1087
|
|
|
*/ |
1088
|
|
|
public function userDeleted($uid, $shareType) { |
1089
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
1090
|
|
|
|
1091
|
|
|
$qb->delete('share'); |
1092
|
|
|
|
1093
|
|
|
if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
1094
|
|
|
/* |
1095
|
|
|
* Delete all user shares that are owned by this user |
1096
|
|
|
* or that are received by this user |
1097
|
|
|
*/ |
1098
|
|
|
|
1099
|
|
|
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))); |
1100
|
|
|
|
1101
|
|
|
$qb->andWhere( |
1102
|
|
|
$qb->expr()->orX( |
1103
|
|
|
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)), |
1104
|
|
|
$qb->expr()->eq('share_with', $qb->createNamedParameter($uid)) |
|
|
|
|
1105
|
|
|
) |
1106
|
|
|
); |
1107
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
1108
|
|
|
/* |
1109
|
|
|
* Delete all group shares that are owned by this user |
1110
|
|
|
* Or special user group shares that are received by this user |
1111
|
|
|
*/ |
1112
|
|
|
$qb->where( |
1113
|
|
|
$qb->expr()->andX( |
1114
|
|
|
$qb->expr()->orX( |
1115
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), |
1116
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)) |
|
|
|
|
1117
|
|
|
), |
1118
|
|
|
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)) |
|
|
|
|
1119
|
|
|
) |
1120
|
|
|
); |
1121
|
|
|
|
1122
|
|
|
$qb->orWhere( |
1123
|
|
|
$qb->expr()->andX( |
1124
|
|
|
$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)), |
1125
|
|
|
$qb->expr()->eq('share_with', $qb->createNamedParameter($uid)) |
|
|
|
|
1126
|
|
|
) |
1127
|
|
|
); |
1128
|
|
|
} elseif ($shareType === \OCP\Share::SHARE_TYPE_LINK) { |
1129
|
|
|
/* |
1130
|
|
|
* Delete all link shares owned by this user. |
1131
|
|
|
* And all link shares initiated by this user (until #22327 is in) |
1132
|
|
|
*/ |
1133
|
|
|
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))); |
1134
|
|
|
|
1135
|
|
|
$qb->andWhere( |
1136
|
|
|
$qb->expr()->orX( |
1137
|
|
|
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)), |
1138
|
|
|
$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid)) |
|
|
|
|
1139
|
|
|
) |
1140
|
|
|
); |
1141
|
|
|
} |
1142
|
|
|
|
1143
|
|
|
$qb->execute(); |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
/** |
1147
|
|
|
* Delete all shares received by this group. As well as any custom group |
1148
|
|
|
* shares for group members. |
1149
|
|
|
* |
1150
|
|
|
* @param string $gid |
1151
|
|
|
*/ |
1152
|
|
|
public function groupDeleted($gid) { |
1153
|
|
|
/* |
1154
|
|
|
* First delete all custom group shares for group members |
1155
|
|
|
*/ |
1156
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
1157
|
|
|
$qb->select('id') |
1158
|
|
|
->from('share') |
1159
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) |
1160
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); |
1161
|
|
|
|
1162
|
|
|
$cursor = $qb->execute(); |
1163
|
|
|
$ids = []; |
1164
|
|
|
while ($row = $cursor->fetch()) { |
1165
|
|
|
$ids[] = (int)$row['id']; |
1166
|
|
|
} |
1167
|
|
|
$cursor->closeCursor(); |
1168
|
|
|
|
1169
|
|
|
if (!empty($ids)) { |
1170
|
|
|
$chunks = \array_chunk($ids, 100); |
1171
|
|
|
foreach ($chunks as $chunk) { |
1172
|
|
|
$qb->delete('share') |
1173
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) |
1174
|
|
|
->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); |
1175
|
|
|
$qb->execute(); |
1176
|
|
|
} |
1177
|
|
|
} |
1178
|
|
|
|
1179
|
|
|
/* |
1180
|
|
|
* Now delete all the group shares |
1181
|
|
|
*/ |
1182
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
1183
|
|
|
$qb->delete('share') |
1184
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) |
1185
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); |
1186
|
|
|
$qb->execute(); |
1187
|
|
|
} |
1188
|
|
|
|
1189
|
|
|
/** |
1190
|
|
|
* Delete custom group shares to this group for this user |
1191
|
|
|
* |
1192
|
|
|
* @param string $uid |
1193
|
|
|
* @param string $gid |
1194
|
|
|
*/ |
1195
|
|
|
public function userDeletedFromGroup($uid, $gid) { |
1196
|
|
|
/* |
1197
|
|
|
* Get all group shares |
1198
|
|
|
*/ |
1199
|
|
|
$qb = $this->dbConn->getQueryBuilder(); |
1200
|
|
|
$qb->select('id') |
1201
|
|
|
->from('share') |
1202
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) |
1203
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); |
1204
|
|
|
|
1205
|
|
|
$cursor = $qb->execute(); |
1206
|
|
|
$ids = []; |
1207
|
|
|
while ($row = $cursor->fetch()) { |
1208
|
|
|
$ids[] = (int)$row['id']; |
1209
|
|
|
} |
1210
|
|
|
$cursor->closeCursor(); |
1211
|
|
|
|
1212
|
|
View Code Duplication |
if (!empty($ids)) { |
|
|
|
|
1213
|
|
|
$chunks = \array_chunk($ids, 100); |
1214
|
|
|
foreach ($chunks as $chunk) { |
1215
|
|
|
/* |
1216
|
|
|
* Delete all special shares wit this users for the found group shares |
1217
|
|
|
*/ |
1218
|
|
|
$qb->delete('share') |
1219
|
|
|
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) |
1220
|
|
|
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid))) |
1221
|
|
|
->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); |
1222
|
|
|
$qb->execute(); |
1223
|
|
|
} |
1224
|
|
|
} |
1225
|
|
|
} |
1226
|
|
|
|
1227
|
|
|
/** |
1228
|
|
|
* Check whether the share object fits the expectations of this provider |
1229
|
|
|
* |
1230
|
|
|
* @param IShare $share share |
1231
|
|
|
* |
1232
|
|
|
* @throws InvalidArgumentException if the share validation failed |
1233
|
|
|
*/ |
1234
|
|
|
private function validate($share) { |
1235
|
|
|
if ($share->getName() !== null && \strlen($share->getName()) > 64) { |
1236
|
|
|
throw new \InvalidArgumentException('Share name cannot be more than 64 characters'); |
1237
|
|
|
} |
1238
|
|
|
|
1239
|
|
|
// TODO: add more early validation for fields instead of relying on the DB |
1240
|
|
|
} |
1241
|
|
|
} |
1242
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.