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

DefaultShareProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 8
dl 0
loc 17
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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