Passed
Push — master ( 4ebb20...72b7c9 )
by Roeland
12:19
created

DefaultShareProvider::delete()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 14
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Andrius <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Jan-Philipp Litza <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author phisch <[email protected]>
11
 * @author Robin Appelman <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
namespace OC\Share20;
31
32
use OC\Files\Cache\Cache;
33
use OCP\Defaults;
34
use OCP\Files\Folder;
35
use OCP\IL10N;
36
use OCP\IURLGenerator;
37
use OCP\IUser;
38
use OCP\Mail\IMailer;
39
use OCP\Share\IShare;
40
use OCP\Share\IShareHelper;
41
use OCP\Share\IShareProvider;
42
use OC\Share20\Exception\InvalidShare;
43
use OC\Share20\Exception\ProviderException;
44
use OCP\Share\Exceptions\ShareNotFound;
45
use OC\Share20\Exception\BackendError;
46
use OCP\DB\QueryBuilder\IQueryBuilder;
47
use OCP\IGroup;
48
use OCP\IGroupManager;
49
use OCP\IUserManager;
50
use OCP\Files\IRootFolder;
51
use OCP\IDBConnection;
52
use OCP\Files\Node;
53
54
/**
55
 * Class DefaultShareProvider
56
 *
57
 * @package OC\Share20
58
 */
59
class DefaultShareProvider implements IShareProvider {
60
61
	// Special share type for user modified group shares
62
	const SHARE_TYPE_USERGROUP = 2;
63
64
	/** @var IDBConnection */
65
	private $dbConn;
66
67
	/** @var IUserManager */
68
	private $userManager;
69
70
	/** @var IGroupManager */
71
	private $groupManager;
72
73
	/** @var IRootFolder */
74
	private $rootFolder;
75
76
	/** @var IMailer */
77
	private $mailer;
78
79
	/** @var Defaults */
80
	private $defaults;
81
82
	/** @var IL10N */
83
	private $l;
84
85
	/** @var IURLGenerator */
86
	private $urlGenerator;
87
88
	/**
89
	 * DefaultShareProvider constructor.
90
	 *
91
	 * @param IDBConnection $connection
92
	 * @param IUserManager $userManager
93
	 * @param IGroupManager $groupManager
94
	 * @param IRootFolder $rootFolder
95
	 * @param IMailer $mailer ;
96
	 * @param Defaults $defaults
97
	 * @param IL10N $l
98
	 * @param IURLGenerator $urlGenerator
99
	 */
100
	public function __construct(
101
			IDBConnection $connection,
102
			IUserManager $userManager,
103
			IGroupManager $groupManager,
104
			IRootFolder $rootFolder,
105
			IMailer $mailer,
106
			Defaults $defaults,
107
			IL10N $l,
108
			IURLGenerator $urlGenerator) {
109
		$this->dbConn = $connection;
110
		$this->userManager = $userManager;
111
		$this->groupManager = $groupManager;
112
		$this->rootFolder = $rootFolder;
113
		$this->mailer = $mailer;
114
		$this->defaults = $defaults;
115
		$this->l = $l;
116
		$this->urlGenerator = $urlGenerator;
117
	}
118
119
	/**
120
	 * Return the identifier of this provider.
121
	 *
122
	 * @return string Containing only [a-zA-Z0-9]
123
	 */
124
	public function identifier() {
125
		return 'ocinternal';
126
	}
127
128
	/**
129
	 * Share a path
130
	 *
131
	 * @param \OCP\Share\IShare $share
132
	 * @return \OCP\Share\IShare The share object
133
	 * @throws ShareNotFound
134
	 * @throws \Exception
135
	 */
136
	public function create(\OCP\Share\IShare $share) {
137
		$qb = $this->dbConn->getQueryBuilder();
138
139
		$qb->insert('share');
140
		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
141
142
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
143
			//Set the UID of the user we share with
144
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
145
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
146
			//Set the GID of the group we share with
147
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
148
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
149
			//set label for public link
150
			$qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
151
			//Set the token of the share
152
			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
153
154
			//If a password is set store it
155
			if ($share->getPassword() !== null) {
0 ignored issues
show
introduced by
The condition $share->getPassword() !== null is always true.
Loading history...
156
				$qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
157
			}
158
159
			$qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
160
161
			//If an expiration date is set store it
162
			if ($share->getExpirationDate() !== null) {
163
				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
164
			}
165
166
			if (method_exists($share, 'getParent')) {
167
				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
168
			}
169
		} else {
170
			throw new \Exception('invalid share type!');
171
		}
172
173
		// Set what is shares
174
		$qb->setValue('item_type', $qb->createParameter('itemType'));
175
		if ($share->getNode() instanceof \OCP\Files\File) {
176
			$qb->setParameter('itemType', 'file');
177
		} else {
178
			$qb->setParameter('itemType', 'folder');
179
		}
180
181
		// Set the file id
182
		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
183
		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
184
185
		// set the permissions
186
		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
187
188
		// Set who created this share
189
		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
190
191
		// Set who is the owner of this file/folder (and this the owner of the share)
192
		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
193
194
		// Set the file target
195
		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
196
197
		// Set the time this share was created
198
		$qb->setValue('stime', $qb->createNamedParameter(time()));
199
200
		// insert the data and fetch the id of the share
201
		$this->dbConn->beginTransaction();
202
		$qb->execute();
203
		$id = $this->dbConn->lastInsertId('*PREFIX*share');
204
205
		// Now fetch the inserted share and create a complete share object
206
		$qb = $this->dbConn->getQueryBuilder();
207
		$qb->select('*')
208
			->from('share')
209
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
210
211
		$cursor = $qb->execute();
212
		$data = $cursor->fetch();
213
		$this->dbConn->commit();
214
		$cursor->closeCursor();
215
216
		if ($data === false) {
217
			throw new ShareNotFound();
218
		}
219
220
		$mailSendValue = $share->getMailSend();
221
		$data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
0 ignored issues
show
introduced by
The condition $mailSendValue === null is always false.
Loading history...
222
223
		$share = $this->createShare($data);
224
		return $share;
225
	}
226
227
	/**
228
	 * Update a share
229
	 *
230
	 * @param \OCP\Share\IShare $share
231
	 * @return \OCP\Share\IShare The share object
232
	 * @throws ShareNotFound
233
	 * @throws \OCP\Files\InvalidPathException
234
	 * @throws \OCP\Files\NotFoundException
235
	 */
236
	public function update(\OCP\Share\IShare $share) {
237
238
		$originalShare = $this->getShareById($share->getId());
239
240
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
241
			/*
242
			 * We allow updating the recipient on user shares.
243
			 */
244
			$qb = $this->dbConn->getQueryBuilder();
245
			$qb->update('share')
246
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
247
				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
248
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
249
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
250
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
251
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
252
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
253
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
254
				->set('note', $qb->createNamedParameter($share->getNote()))
255
				->execute();
256
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
257
			$qb = $this->dbConn->getQueryBuilder();
258
			$qb->update('share')
259
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
260
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
261
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
262
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
263
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
264
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
265
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
266
				->set('note', $qb->createNamedParameter($share->getNote()))
267
				->execute();
268
269
			/*
270
			 * Update all user defined group shares
271
			 */
272
			$qb = $this->dbConn->getQueryBuilder();
273
			$qb->update('share')
274
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
275
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
276
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
277
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
278
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
279
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
280
				->set('note', $qb->createNamedParameter($share->getNote()))
281
				->execute();
282
283
			/*
284
			 * Now update the permissions for all children that have not set it to 0
285
			 */
286
			$qb = $this->dbConn->getQueryBuilder();
287
			$qb->update('share')
288
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
289
				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
290
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
291
				->execute();
292
293
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
294
			$qb = $this->dbConn->getQueryBuilder();
295
			$qb->update('share')
296
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
297
				->set('password', $qb->createNamedParameter($share->getPassword()))
298
				->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
299
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
300
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
301
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
302
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
303
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
304
				->set('token', $qb->createNamedParameter($share->getToken()))
305
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
306
				->set('note', $qb->createNamedParameter($share->getNote()))
307
				->set('label', $qb->createNamedParameter($share->getLabel()))
308
				->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
0 ignored issues
show
Unused Code introduced by
The call to OCP\DB\QueryBuilder\IQueryBuilder::set() has too many arguments starting with OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT. ( Ignorable by Annotation )

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

308
				->/** @scrutinizer ignore-call */ set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)

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. Please note the @ignore annotation hint above.

Loading history...
309
				->execute();
310
		}
311
312
		if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
313
			$this->propagateNote($share);
314
		}
315
316
317
		return $share;
318
	}
319
320
	/**
321
	 * Get all children of this share
322
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
323
	 *
324
	 * @param \OCP\Share\IShare $parent
325
	 * @return \OCP\Share\IShare[]
326
	 */
327
	public function getChildren(\OCP\Share\IShare $parent) {
328
		$children = [];
329
330
		$qb = $this->dbConn->getQueryBuilder();
331
		$qb->select('*')
332
			->from('share')
333
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
334
			->andWhere(
335
				$qb->expr()->in(
336
					'share_type',
337
					$qb->createNamedParameter([
338
						\OCP\Share::SHARE_TYPE_USER,
339
						\OCP\Share::SHARE_TYPE_GROUP,
340
						\OCP\Share::SHARE_TYPE_LINK,
341
					], IQueryBuilder::PARAM_INT_ARRAY)
342
				)
343
			)
344
			->andWhere($qb->expr()->orX(
345
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
346
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
347
			))
348
			->orderBy('id');
349
350
		$cursor = $qb->execute();
351
		while($data = $cursor->fetch()) {
352
			$children[] = $this->createShare($data);
353
		}
354
		$cursor->closeCursor();
355
356
		return $children;
357
	}
358
359
	/**
360
	 * Delete a share
361
	 *
362
	 * @param \OCP\Share\IShare $share
363
	 */
364
	public function delete(\OCP\Share\IShare $share) {
365
		$qb = $this->dbConn->getQueryBuilder();
366
		$qb->delete('share')
367
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
368
369
		/*
370
		 * If the share is a group share delete all possible
371
		 * user defined groups shares.
372
		 */
373
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
374
			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
375
		}
376
377
		$qb->execute();
378
	}
379
380
	/**
381
	 * Unshare a share from the recipient. If this is a group share
382
	 * this means we need a special entry in the share db.
383
	 *
384
	 * @param \OCP\Share\IShare $share
385
	 * @param string $recipient UserId of recipient
386
	 * @throws BackendError
387
	 * @throws ProviderException
388
	 */
389
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
390
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
391
392
			$group = $this->groupManager->get($share->getSharedWith());
393
			$user = $this->userManager->get($recipient);
394
395
			if (is_null($group)) {
396
				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
397
			}
398
399
			if (!$group->inGroup($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user can also be of type null; however, parameter $user of OCP\IGroup::inGroup() does only seem to accept OCP\IUser, maybe add an additional type check? ( Ignorable by Annotation )

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

399
			if (!$group->inGroup(/** @scrutinizer ignore-type */ $user)) {
Loading history...
400
				throw new ProviderException('Recipient not in receiving group');
401
			}
402
403
			// Try to fetch user specific share
404
			$qb = $this->dbConn->getQueryBuilder();
405
			$stmt = $qb->select('*')
406
				->from('share')
407
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
408
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
409
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
410
				->andWhere($qb->expr()->orX(
411
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
412
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
413
				))
414
				->execute();
415
416
			$data = $stmt->fetch();
417
418
			/*
419
			 * Check if there already is a user specific group share.
420
			 * If there is update it (if required).
421
			 */
422
			if ($data === false) {
423
				$qb = $this->dbConn->getQueryBuilder();
424
425
				$type = $share->getNodeType();
426
427
				//Insert new share
428
				$qb->insert('share')
429
					->values([
430
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
431
						'share_with' => $qb->createNamedParameter($recipient),
432
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
433
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
434
						'parent' => $qb->createNamedParameter($share->getId()),
435
						'item_type' => $qb->createNamedParameter($type),
436
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
437
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
438
						'file_target' => $qb->createNamedParameter($share->getTarget()),
439
						'permissions' => $qb->createNamedParameter(0),
440
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
441
					])->execute();
442
443
			} else if ($data['permissions'] !== 0) {
444
445
				// Update existing usergroup share
446
				$qb = $this->dbConn->getQueryBuilder();
447
				$qb->update('share')
448
					->set('permissions', $qb->createNamedParameter(0))
449
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
450
					->execute();
451
			}
452
453
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
454
455
			if ($share->getSharedWith() !== $recipient) {
456
				throw new ProviderException('Recipient does not match');
457
			}
458
459
			// We can just delete user and link shares
460
			$this->delete($share);
461
		} else {
462
			throw new ProviderException('Invalid shareType');
463
		}
464
	}
465
466
	/**
467
	 * @inheritdoc
468
	 *
469
	 * For now this only works for group shares
470
	 * If this gets implemented for normal shares we have to extend it
471
	 */
472
	public function restore(IShare $share, string $recipient): IShare {
473
		$qb = $this->dbConn->getQueryBuilder();
474
		$qb->select('permissions')
475
			->from('share')
476
			->where(
477
				$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
478
			);
479
		$cursor = $qb->execute();
480
		$data = $cursor->fetch();
481
		$cursor->closeCursor();
482
483
		$originalPermission = $data['permissions'];
484
485
		$qb = $this->dbConn->getQueryBuilder();
486
		$qb->update('share')
487
			->set('permissions', $qb->createNamedParameter($originalPermission))
488
			->where(
489
				$qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
0 ignored issues
show
Bug introduced by
The method getParent() does not exist on OCP\Share\IShare. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\Share\IShare. ( Ignorable by Annotation )

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

489
				$qb->expr()->eq('parent', $qb->createNamedParameter($share->/** @scrutinizer ignore-call */ getParent()))
Loading history...
490
			)->andWhere(
491
				$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
492
			)->andWhere(
493
				$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
494
			);
495
496
		$qb->execute();
497
498
		return $this->getShareById($share->getId(), $recipient);
499
	}
500
501
	/**
502
	 * @inheritdoc
503
	 */
504
	public function move(\OCP\Share\IShare $share, $recipient) {
505
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
506
			// Just update the target
507
			$qb = $this->dbConn->getQueryBuilder();
508
			$qb->update('share')
509
				->set('file_target', $qb->createNamedParameter($share->getTarget()))
510
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
511
				->execute();
512
513
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
514
515
			// Check if there is a usergroup share
516
			$qb = $this->dbConn->getQueryBuilder();
517
			$stmt = $qb->select('id')
518
				->from('share')
519
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
520
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
521
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
522
				->andWhere($qb->expr()->orX(
523
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
524
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
525
				))
526
				->setMaxResults(1)
527
				->execute();
528
529
			$data = $stmt->fetch();
530
			$stmt->closeCursor();
531
532
			if ($data === false) {
533
				// No usergroup share yet. Create one.
534
				$qb = $this->dbConn->getQueryBuilder();
535
				$qb->insert('share')
536
					->values([
537
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
538
						'share_with' => $qb->createNamedParameter($recipient),
539
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
540
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
541
						'parent' => $qb->createNamedParameter($share->getId()),
542
						'item_type' => $qb->createNamedParameter($share->getNodeType()),
543
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
544
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
545
						'file_target' => $qb->createNamedParameter($share->getTarget()),
546
						'permissions' => $qb->createNamedParameter($share->getPermissions()),
547
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
548
					])->execute();
549
			} else {
550
				// Already a usergroup share. Update it.
551
				$qb = $this->dbConn->getQueryBuilder();
552
				$qb->update('share')
553
					->set('file_target', $qb->createNamedParameter($share->getTarget()))
554
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
555
					->execute();
556
			}
557
		}
558
559
		return $share;
560
	}
561
562
	public function getSharesInFolder($userId, Folder $node, $reshares) {
563
		$qb = $this->dbConn->getQueryBuilder();
564
		$qb->select('*')
565
			->from('share', 's')
566
			->andWhere($qb->expr()->orX(
567
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
568
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
569
			));
570
571
		$qb->andWhere($qb->expr()->orX(
572
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
573
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
574
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
575
		));
576
577
		/**
578
		 * Reshares for this user are shares where they are the owner.
579
		 */
580
		if ($reshares === false) {
581
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
582
		} else {
583
			$qb->andWhere(
584
				$qb->expr()->orX(
585
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
586
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
587
				)
588
			);
589
		}
590
591
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
592
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
593
594
		$qb->orderBy('id');
595
596
		$cursor = $qb->execute();
597
		$shares = [];
598
		while ($data = $cursor->fetch()) {
599
			$shares[$data['fileid']][] = $this->createShare($data);
600
		}
601
		$cursor->closeCursor();
602
603
		return $shares;
604
	}
605
606
	/**
607
	 * @inheritdoc
608
	 */
609
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
610
		$qb = $this->dbConn->getQueryBuilder();
611
		$qb->select('*')
612
			->from('share')
613
			->andWhere($qb->expr()->orX(
614
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
615
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
616
			));
617
618
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
619
620
		/**
621
		 * Reshares for this user are shares where they are the owner.
622
		 */
623
		if ($reshares === false) {
624
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
625
		} else {
626
			if ($node === null) {
627
				$qb->andWhere(
628
					$qb->expr()->orX(
629
						$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
630
						$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
631
					)
632
				);
633
			}
634
		}
635
636
		if ($node !== null) {
637
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
638
		}
639
640
		if ($limit !== -1) {
641
			$qb->setMaxResults($limit);
642
		}
643
644
		$qb->setFirstResult($offset);
645
		$qb->orderBy('id');
646
647
		$cursor = $qb->execute();
648
		$shares = [];
649
		while($data = $cursor->fetch()) {
650
			$shares[] = $this->createShare($data);
651
		}
652
		$cursor->closeCursor();
653
654
		return $shares;
655
	}
656
657
	/**
658
	 * @inheritdoc
659
	 */
660
	public function getShareById($id, $recipientId = null) {
661
		$qb = $this->dbConn->getQueryBuilder();
662
663
		$qb->select('*')
664
			->from('share')
665
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
666
			->andWhere(
667
				$qb->expr()->in(
668
					'share_type',
669
					$qb->createNamedParameter([
670
						\OCP\Share::SHARE_TYPE_USER,
671
						\OCP\Share::SHARE_TYPE_GROUP,
672
						\OCP\Share::SHARE_TYPE_LINK,
673
					], IQueryBuilder::PARAM_INT_ARRAY)
674
				)
675
			)
676
			->andWhere($qb->expr()->orX(
677
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
678
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
679
			));
680
681
		$cursor = $qb->execute();
682
		$data = $cursor->fetch();
683
		$cursor->closeCursor();
684
685
		if ($data === false) {
686
			throw new ShareNotFound();
687
		}
688
689
		try {
690
			$share = $this->createShare($data);
691
		} catch (InvalidShare $e) {
692
			throw new ShareNotFound();
693
		}
694
695
		// If the recipient is set for a group share resolve to that user
696
		if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
697
			$share = $this->resolveGroupShares([$share], $recipientId)[0];
698
		}
699
700
		return $share;
701
	}
702
703
	/**
704
	 * Get shares for a given path
705
	 *
706
	 * @param \OCP\Files\Node $path
707
	 * @return \OCP\Share\IShare[]
708
	 */
709
	public function getSharesByPath(Node $path) {
710
		$qb = $this->dbConn->getQueryBuilder();
711
712
		$cursor = $qb->select('*')
713
			->from('share')
714
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
715
			->andWhere(
716
				$qb->expr()->orX(
717
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
718
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
719
				)
720
			)
721
			->andWhere($qb->expr()->orX(
722
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
723
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
724
			))
725
			->execute();
726
727
		$shares = [];
728
		while($data = $cursor->fetch()) {
729
			$shares[] = $this->createShare($data);
730
		}
731
		$cursor->closeCursor();
732
733
		return $shares;
734
	}
735
736
	/**
737
	 * Returns whether the given database result can be interpreted as
738
	 * a share with accessible file (not trashed, not deleted)
739
	 */
740
	private function isAccessibleResult($data) {
741
		// exclude shares leading to deleted file entries
742
		if ($data['fileid'] === null) {
743
			return false;
744
		}
745
746
		// exclude shares leading to trashbin on home storages
747
		$pathSections = explode('/', $data['path'], 2);
748
		// FIXME: would not detect rare md5'd home storage case properly
749
		if ($pathSections[0] !== 'files'
750
		    	&& in_array(explode(':', $data['storage_string_id'], 2)[0], array('home', 'object'))) {
751
			return false;
752
		}
753
		return true;
754
	}
755
756
	/**
757
	 * @inheritdoc
758
	 */
759
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
760
		/** @var Share[] $shares */
761
		$shares = [];
762
763
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
764
			//Get shares directly with this user
765
			$qb = $this->dbConn->getQueryBuilder();
766
			$qb->select('s.*',
767
				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
768
				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
769
				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
770
			)
771
				->selectAlias('st.id', 'storage_string_id')
772
				->from('share', 's')
773
				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
774
				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
775
776
			// Order by id
777
			$qb->orderBy('s.id');
778
779
			// Set limit and offset
780
			if ($limit !== -1) {
781
				$qb->setMaxResults($limit);
782
			}
783
			$qb->setFirstResult($offset);
784
785
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
786
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
787
				->andWhere($qb->expr()->orX(
788
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
789
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
790
				));
791
792
			// Filter by node if provided
793
			if ($node !== null) {
794
				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
795
			}
796
797
			$cursor = $qb->execute();
798
799
			while($data = $cursor->fetch()) {
800
				if ($this->isAccessibleResult($data)) {
801
					$shares[] = $this->createShare($data);
802
				}
803
			}
804
			$cursor->closeCursor();
805
806
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
807
			$user = $this->userManager->get($userId);
808
			$allGroups = $this->groupManager->getUserGroups($user);
809
810
			/** @var Share[] $shares2 */
811
			$shares2 = [];
812
813
			$start = 0;
814
			while(true) {
815
				$groups = array_slice($allGroups, $start, 100);
816
				$start += 100;
817
818
				if ($groups === []) {
819
					break;
820
				}
821
822
				$qb = $this->dbConn->getQueryBuilder();
823
				$qb->select('s.*',
824
					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
825
					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
826
					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
827
				)
828
					->selectAlias('st.id', 'storage_string_id')
829
					->from('share', 's')
830
					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
831
					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
832
					->orderBy('s.id')
833
					->setFirstResult(0);
834
835
				if ($limit !== -1) {
836
					$qb->setMaxResults($limit - count($shares));
837
				}
838
839
				// Filter by node if provided
840
				if ($node !== null) {
841
					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
842
				}
843
844
845
				$groups = array_filter($groups, function($group) { return $group instanceof IGroup; });
846
				$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
847
848
				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
849
					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
850
						$groups,
851
						IQueryBuilder::PARAM_STR_ARRAY
852
					)))
853
					->andWhere($qb->expr()->orX(
854
						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
855
						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
856
					));
857
858
				$cursor = $qb->execute();
859
				while($data = $cursor->fetch()) {
860
					if ($offset > 0) {
861
						$offset--;
862
						continue;
863
					}
864
865
					if ($this->isAccessibleResult($data)) {
866
						$shares2[] = $this->createShare($data);
867
					}
868
				}
869
				$cursor->closeCursor();
870
			}
871
872
			/*
873
 			 * Resolve all group shares to user specific shares
874
 			 */
875
			$shares = $this->resolveGroupShares($shares2, $userId);
876
		} else {
877
			throw new BackendError('Invalid backend');
878
		}
879
880
881
		return $shares;
882
	}
883
884
	/**
885
	 * Get a share by token
886
	 *
887
	 * @param string $token
888
	 * @return \OCP\Share\IShare
889
	 * @throws ShareNotFound
890
	 */
891
	public function getShareByToken($token) {
892
		$qb = $this->dbConn->getQueryBuilder();
893
894
		$cursor = $qb->select('*')
895
			->from('share')
896
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
897
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
898
			->andWhere($qb->expr()->orX(
899
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
900
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
901
			))
902
			->execute();
903
904
		$data = $cursor->fetch();
905
906
		if ($data === false) {
907
			throw new ShareNotFound();
908
		}
909
910
		try {
911
			$share = $this->createShare($data);
912
		} catch (InvalidShare $e) {
913
			throw new ShareNotFound();
914
		}
915
916
		return $share;
917
	}
918
919
	/**
920
	 * Create a share object from an database row
921
	 *
922
	 * @param mixed[] $data
923
	 * @return \OCP\Share\IShare
924
	 * @throws InvalidShare
925
	 */
926
	private function createShare($data) {
927
		$share = new Share($this->rootFolder, $this->userManager);
928
		$share->setId((int)$data['id'])
929
			->setShareType((int)$data['share_type'])
930
			->setPermissions((int)$data['permissions'])
931
			->setTarget($data['file_target'])
932
			->setNote($data['note'])
933
			->setMailSend((bool)$data['mail_send'])
934
			->setLabel($data['label']);
935
936
		$shareTime = new \DateTime();
937
		$shareTime->setTimestamp((int)$data['stime']);
938
		$share->setShareTime($shareTime);
939
940
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
941
			$share->setSharedWith($data['share_with']);
942
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
943
			$share->setSharedWith($data['share_with']);
944
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
945
			$share->setPassword($data['password']);
946
			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
947
			$share->setToken($data['token']);
948
		}
949
950
		$share->setSharedBy($data['uid_initiator']);
951
		$share->setShareOwner($data['uid_owner']);
952
953
		$share->setNodeId((int)$data['file_source']);
954
		$share->setNodeType($data['item_type']);
955
956
		if ($data['expiration'] !== null) {
957
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
958
			$share->setExpirationDate($expiration);
959
		}
960
961
		if (isset($data['f_permissions'])) {
962
			$entryData = $data;
963
			$entryData['permissions'] = $entryData['f_permissions'];
964
			$entryData['parent'] = $entryData['f_parent'];
965
			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
966
				\OC::$server->getMimeTypeLoader()));
967
		}
968
969
		$share->setProviderId($this->identifier());
970
		$share->setHideDownload((int)$data['hide_download'] === 1);
971
972
		return $share;
973
	}
974
975
	/**
976
	 * @param Share[] $shares
977
	 * @param $userId
978
	 * @return Share[] The updates shares if no update is found for a share return the original
979
	 */
980
	private function resolveGroupShares($shares, $userId) {
981
		$result = [];
982
983
		$start = 0;
984
		while(true) {
985
			/** @var Share[] $shareSlice */
986
			$shareSlice = array_slice($shares, $start, 100);
987
			$start += 100;
988
989
			if ($shareSlice === []) {
990
				break;
991
			}
992
993
			/** @var int[] $ids */
994
			$ids = [];
995
			/** @var Share[] $shareMap */
996
			$shareMap = [];
997
998
			foreach ($shareSlice as $share) {
999
				$ids[] = (int)$share->getId();
1000
				$shareMap[$share->getId()] = $share;
1001
			}
1002
1003
			$qb = $this->dbConn->getQueryBuilder();
1004
1005
			$query = $qb->select('*')
1006
				->from('share')
1007
				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1008
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1009
				->andWhere($qb->expr()->orX(
1010
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1011
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1012
				));
1013
1014
			$stmt = $query->execute();
1015
1016
			while($data = $stmt->fetch()) {
1017
				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1018
				$shareMap[$data['parent']]->setTarget($data['file_target']);
1019
				$shareMap[$data['parent']]->setParent($data['parent']);
1020
			}
1021
1022
			$stmt->closeCursor();
1023
1024
			foreach ($shareMap as $share) {
1025
				$result[] = $share;
1026
			}
1027
		}
1028
1029
		return $result;
1030
	}
1031
1032
	/**
1033
	 * A user is deleted from the system
1034
	 * So clean up the relevant shares.
1035
	 *
1036
	 * @param string $uid
1037
	 * @param int $shareType
1038
	 */
1039
	public function userDeleted($uid, $shareType) {
1040
		$qb = $this->dbConn->getQueryBuilder();
1041
1042
		$qb->delete('share');
1043
1044
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
1045
			/*
1046
			 * Delete all user shares that are owned by this user
1047
			 * or that are received by this user
1048
			 */
1049
1050
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
1051
1052
			$qb->andWhere(
1053
				$qb->expr()->orX(
1054
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1055
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1056
				)
1057
			);
1058
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
1059
			/*
1060
			 * Delete all group shares that are owned by this user
1061
			 * Or special user group shares that are received by this user
1062
			 */
1063
			$qb->where(
1064
				$qb->expr()->andX(
1065
					$qb->expr()->orX(
1066
						$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
1067
						$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
1068
					),
1069
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1070
				)
1071
			);
1072
1073
			$qb->orWhere(
1074
				$qb->expr()->andX(
1075
					$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
1076
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1077
				)
1078
			);
1079
		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
1080
			/*
1081
			 * Delete all link shares owned by this user.
1082
			 * And all link shares initiated by this user (until #22327 is in)
1083
			 */
1084
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
1085
1086
			$qb->andWhere(
1087
				$qb->expr()->orX(
1088
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1089
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1090
				)
1091
			);
1092
		}
1093
1094
		$qb->execute();
1095
	}
1096
1097
	/**
1098
	 * Delete all shares received by this group. As well as any custom group
1099
	 * shares for group members.
1100
	 *
1101
	 * @param string $gid
1102
	 */
1103
	public function groupDeleted($gid) {
1104
		/*
1105
		 * First delete all custom group shares for group members
1106
		 */
1107
		$qb = $this->dbConn->getQueryBuilder();
1108
		$qb->select('id')
1109
			->from('share')
1110
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1111
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1112
1113
		$cursor = $qb->execute();
1114
		$ids = [];
1115
		while($row = $cursor->fetch()) {
1116
			$ids[] = (int)$row['id'];
1117
		}
1118
		$cursor->closeCursor();
1119
1120
		if (!empty($ids)) {
1121
			$chunks = array_chunk($ids, 100);
1122
			foreach ($chunks as $chunk) {
1123
				$qb->delete('share')
1124
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1125
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1126
				$qb->execute();
1127
			}
1128
		}
1129
1130
		/*
1131
		 * Now delete all the group shares
1132
		 */
1133
		$qb = $this->dbConn->getQueryBuilder();
1134
		$qb->delete('share')
1135
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1136
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1137
		$qb->execute();
1138
	}
1139
1140
	/**
1141
	 * Delete custom group shares to this group for this user
1142
	 *
1143
	 * @param string $uid
1144
	 * @param string $gid
1145
	 */
1146
	public function userDeletedFromGroup($uid, $gid) {
1147
		/*
1148
		 * Get all group shares
1149
		 */
1150
		$qb = $this->dbConn->getQueryBuilder();
1151
		$qb->select('id')
1152
			->from('share')
1153
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1154
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1155
1156
		$cursor = $qb->execute();
1157
		$ids = [];
1158
		while($row = $cursor->fetch()) {
1159
			$ids[] = (int)$row['id'];
1160
		}
1161
		$cursor->closeCursor();
1162
1163
		if (!empty($ids)) {
1164
			$chunks = array_chunk($ids, 100);
1165
			foreach ($chunks as $chunk) {
1166
				/*
1167
				 * Delete all special shares wit this users for the found group shares
1168
				 */
1169
				$qb->delete('share')
1170
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1171
					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1172
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1173
				$qb->execute();
1174
			}
1175
		}
1176
	}
1177
1178
	/**
1179
	 * @inheritdoc
1180
	 */
1181
	public function getAccessList($nodes, $currentAccess) {
1182
		$ids = [];
1183
		foreach ($nodes as $node) {
1184
			$ids[] = $node->getId();
1185
		}
1186
1187
		$qb = $this->dbConn->getQueryBuilder();
1188
1189
		$or = $qb->expr()->orX(
1190
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
1191
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
1192
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
1193
		);
1194
1195
		if ($currentAccess) {
1196
			$or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)));
1197
		}
1198
1199
		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1200
			->from('share')
1201
			->where(
1202
				$or
1203
			)
1204
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1205
			->andWhere($qb->expr()->orX(
1206
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1207
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1208
			));
1209
		$cursor = $qb->execute();
1210
1211
		$users = [];
1212
		$link = false;
1213
		while($row = $cursor->fetch()) {
1214
			$type = (int)$row['share_type'];
1215
			if ($type === \OCP\Share::SHARE_TYPE_USER) {
1216
				$uid = $row['share_with'];
1217
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1218
				$users[$uid][$row['id']] = $row;
1219
			} else if ($type === \OCP\Share::SHARE_TYPE_GROUP) {
1220
				$gid = $row['share_with'];
1221
				$group = $this->groupManager->get($gid);
1222
1223
				if ($group === null) {
1224
					continue;
1225
				}
1226
1227
				$userList = $group->getUsers();
1228
				foreach ($userList as $user) {
1229
					$uid = $user->getUID();
1230
					$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1231
					$users[$uid][$row['id']] = $row;
1232
				}
1233
			} else if ($type === \OCP\Share::SHARE_TYPE_LINK) {
1234
				$link = true;
1235
			} else if ($type === self::SHARE_TYPE_USERGROUP && $currentAccess === true) {
1236
				$uid = $row['share_with'];
1237
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1238
				$users[$uid][$row['id']] = $row;
1239
			}
1240
		}
1241
		$cursor->closeCursor();
1242
1243
		if ($currentAccess === true) {
1244
			$users = array_map([$this, 'filterSharesOfUser'], $users);
1245
			$users = array_filter($users);
1246
		} else {
1247
			$users = array_keys($users);
1248
		}
1249
1250
		return ['users' => $users, 'public' => $link];
1251
	}
1252
1253
	/**
1254
	 * For each user the path with the fewest slashes is returned
1255
	 * @param array $shares
1256
	 * @return array
1257
	 */
1258
	protected function filterSharesOfUser(array $shares) {
1259
		// Group shares when the user has a share exception
1260
		foreach ($shares as $id => $share) {
1261
			$type = (int) $share['share_type'];
1262
			$permissions = (int) $share['permissions'];
1263
1264
			if ($type === self::SHARE_TYPE_USERGROUP) {
1265
				unset($shares[$share['parent']]);
1266
1267
				if ($permissions === 0) {
1268
					unset($shares[$id]);
1269
				}
1270
			}
1271
		}
1272
1273
		$best = [];
1274
		$bestDepth = 0;
1275
		foreach ($shares as $id => $share) {
1276
			$depth = substr_count($share['file_target'], '/');
1277
			if (empty($best) || $depth < $bestDepth) {
1278
				$bestDepth = $depth;
1279
				$best = [
1280
					'node_id' => $share['file_source'],
1281
					'node_path' => $share['file_target'],
1282
				];
1283
			}
1284
		}
1285
1286
		return $best;
1287
	}
1288
1289
	/**
1290
	 * propagate notes to the recipients
1291
	 *
1292
	 * @param IShare $share
1293
	 * @throws \OCP\Files\NotFoundException
1294
	 */
1295
	private function propagateNote(IShare $share) {
1296
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
1297
			$user = $this->userManager->get($share->getSharedWith());
1298
			$this->sendNote([$user], $share);
1299
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
1300
			$group = $this->groupManager->get($share->getSharedWith());
1301
			$groupMembers = $group->getUsers();
1302
			$this->sendNote($groupMembers, $share);
1303
		}
1304
	}
1305
1306
	/**
1307
	 * send note by mail
1308
	 *
1309
	 * @param array $recipients
1310
	 * @param IShare $share
1311
	 * @throws \OCP\Files\NotFoundException
1312
	 */
1313
	private function sendNote(array $recipients, IShare $share) {
1314
1315
		$toList = [];
1316
1317
		foreach ($recipients as $recipient) {
1318
			/** @var IUser $recipient */
1319
			$email = $recipient->getEMailAddress();
1320
			if ($email) {
1321
				$toList[$email] = $recipient->getDisplayName();
1322
			}
1323
		}
1324
1325
		if (!empty($toList)) {
1326
1327
			$filename = $share->getNode()->getName();
1328
			$initiator = $share->getSharedBy();
1329
			$note = $share->getNote();
1330
1331
			$initiatorUser = $this->userManager->get($initiator);
1332
			$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1333
			$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1334
			$plainHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
1335
			$htmlHeading = $this->l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
1336
			$message = $this->mailer->createMessage();
1337
1338
			$emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1339
1340
			$emailTemplate->setSubject($this->l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
1341
			$emailTemplate->addHeader();
1342
			$emailTemplate->addHeading($htmlHeading, $plainHeading);
1343
			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
1344
1345
			$link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1346
			$emailTemplate->addBodyButton(
1347
				$this->l->t('Open »%s«', [$filename]),
1348
				$link
1349
			);
1350
1351
1352
			// The "From" contains the sharers name
1353
			$instanceName = $this->defaults->getName();
1354
			$senderName = $this->l->t(
1355
				'%1$s via %2$s',
1356
				[
1357
					$initiatorDisplayName,
1358
					$instanceName
1359
				]
1360
			);
1361
			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1362
			if ($initiatorEmailAddress !== null) {
1363
				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1364
				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1365
			} else {
1366
				$emailTemplate->addFooter();
1367
			}
1368
1369
			if (count($toList) === 1) {
1370
				$message->setTo($toList);
1371
			} else {
1372
				$message->setTo([]);
1373
				$message->setBcc($toList);
1374
			}
1375
			$message->useTemplate($emailTemplate);
1376
			$this->mailer->send($message);
1377
		}
1378
1379
	}
1380
}
1381