Completed
Push — master ( 86d952...cbfcfb )
by Morris
24:23
created

DefaultShareProvider::delete()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 15
loc 15
rs 9.7666
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\Files\Folder;
34
use OCP\Share\IShare;
35
use OCP\Share\IShareProvider;
36
use OC\Share20\Exception\InvalidShare;
37
use OC\Share20\Exception\ProviderException;
38
use OCP\Share\Exceptions\ShareNotFound;
39
use OC\Share20\Exception\BackendError;
40
use OCP\DB\QueryBuilder\IQueryBuilder;
41
use OCP\IGroup;
42
use OCP\IGroupManager;
43
use OCP\IUserManager;
44
use OCP\Files\IRootFolder;
45
use OCP\IDBConnection;
46
use OCP\Files\Node;
47
48
/**
49
 * Class DefaultShareProvider
50
 *
51
 * @package OC\Share20
52
 */
53
class DefaultShareProvider implements IShareProvider {
54
55
	// Special share type for user modified group shares
56
	const SHARE_TYPE_USERGROUP = 2;
57
58
	/** @var IDBConnection */
59
	private $dbConn;
60
61
	/** @var IUserManager */
62
	private $userManager;
63
64
	/** @var IGroupManager */
65
	private $groupManager;
66
67
	/** @var IRootFolder */
68
	private $rootFolder;
69
70
	/**
71
	 * DefaultShareProvider constructor.
72
	 *
73
	 * @param IDBConnection $connection
74
	 * @param IUserManager $userManager
75
	 * @param IGroupManager $groupManager
76
	 * @param IRootFolder $rootFolder
77
	 */
78
	public function __construct(
79
			IDBConnection $connection,
80
			IUserManager $userManager,
81
			IGroupManager $groupManager,
82
			IRootFolder $rootFolder) {
83
		$this->dbConn = $connection;
84
		$this->userManager = $userManager;
85
		$this->groupManager = $groupManager;
86
		$this->rootFolder = $rootFolder;
87
	}
88
89
	/**
90
	 * Return the identifier of this provider.
91
	 *
92
	 * @return string Containing only [a-zA-Z0-9]
93
	 */
94
	public function identifier() {
95
		return 'ocinternal';
96
	}
97
98
	/**
99
	 * Share a path
100
	 *
101
	 * @param \OCP\Share\IShare $share
102
	 * @return \OCP\Share\IShare The share object
103
	 * @throws ShareNotFound
104
	 * @throws \Exception
105
	 */
106
	public function create(\OCP\Share\IShare $share) {
107
		$qb = $this->dbConn->getQueryBuilder();
108
109
		$qb->insert('share');
110
		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
111
112
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
113
			//Set the UID of the user we share with
114
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
115
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
116
			//Set the GID of the group we share with
117
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
118
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
119
			//Set the token of the share
120
			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
121
122
			//If a password is set store it
123
			if ($share->getPassword() !== null) {
124
				$qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
125
			}
126
127
			//If an expiration date is set store it
128
			if ($share->getExpirationDate() !== null) {
129
				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
130
			}
131
132
			if (method_exists($share, 'getParent')) {
133
				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
134
			}
135
		} else {
136
			throw new \Exception('invalid share type!');
137
		}
138
139
		// Set what is shares
140
		$qb->setValue('item_type', $qb->createParameter('itemType'));
141
		if ($share->getNode() instanceof \OCP\Files\File) {
142
			$qb->setParameter('itemType', 'file');
143
		} else {
144
			$qb->setParameter('itemType', 'folder');
145
		}
146
147
		// Set the file id
148
		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
149
		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
150
151
		// set the permissions
152
		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
153
154
		// Set who created this share
155
		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
156
157
		// Set who is the owner of this file/folder (and this the owner of the share)
158
		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
159
160
		// Set the file target
161
		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
162
163
		// Set the time this share was created
164
		$qb->setValue('stime', $qb->createNamedParameter(time()));
165
166
		// insert the data and fetch the id of the share
167
		$this->dbConn->beginTransaction();
168
		$qb->execute();
169
		$id = $this->dbConn->lastInsertId('*PREFIX*share');
170
171
		// Now fetch the inserted share and create a complete share object
172
		$qb = $this->dbConn->getQueryBuilder();
173
		$qb->select('*')
174
			->from('share')
175
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
176
177
		$cursor = $qb->execute();
178
		$data = $cursor->fetch();
179
		$this->dbConn->commit();
180
		$cursor->closeCursor();
181
182
		if ($data === false) {
183
			throw new ShareNotFound();
184
		}
185
186
		$mailSendValue = $share->getMailSend();
187
		$data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
188
189
		$share = $this->createShare($data);
190
		return $share;
191
	}
192
193
	/**
194
	 * Update a share
195
	 *
196
	 * @param \OCP\Share\IShare $share
197
	 * @return \OCP\Share\IShare The share object
198
	 */
199
	public function update(\OCP\Share\IShare $share) {
200
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
201
			/*
202
			 * We allow updating the recipient on user shares.
203
			 */
204
			$qb = $this->dbConn->getQueryBuilder();
205
			$qb->update('share')
206
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
207
				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
208
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
209
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
210
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
211
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
212
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
213
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
214
				->execute();
215
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
216
			$qb = $this->dbConn->getQueryBuilder();
217
			$qb->update('share')
218
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
219
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
220
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
221
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
222
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
223
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
224
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
225
				->execute();
226
227
			/*
228
			 * Update all user defined group shares
229
			 */
230
			$qb = $this->dbConn->getQueryBuilder();
231
			$qb->update('share')
232
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
233
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
234
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
235
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
236
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
237
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
238
				->execute();
239
240
			/*
241
			 * Now update the permissions for all children that have not set it to 0
242
			 */
243
			$qb = $this->dbConn->getQueryBuilder();
244
			$qb->update('share')
245
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
246
				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
247
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
248
				->execute();
249
250
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
251
			$qb = $this->dbConn->getQueryBuilder();
252
			$qb->update('share')
253
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
254
				->set('password', $qb->createNamedParameter($share->getPassword()))
255
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
256
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
257
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
258
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
259
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
260
				->set('token', $qb->createNamedParameter($share->getToken()))
261
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
262
				->execute();
263
		}
264
265
		return $share;
266
	}
267
268
	/**
269
	 * Get all children of this share
270
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
271
	 *
272
	 * @param \OCP\Share\IShare $parent
273
	 * @return \OCP\Share\IShare[]
274
	 */
275
	public function getChildren(\OCP\Share\IShare $parent) {
276
		$children = [];
277
278
		$qb = $this->dbConn->getQueryBuilder();
279
		$qb->select('*')
280
			->from('share')
281
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
282
			->andWhere(
283
				$qb->expr()->in(
284
					'share_type',
285
					$qb->createNamedParameter([
286
						\OCP\Share::SHARE_TYPE_USER,
287
						\OCP\Share::SHARE_TYPE_GROUP,
288
						\OCP\Share::SHARE_TYPE_LINK,
289
					], IQueryBuilder::PARAM_INT_ARRAY)
290
				)
291
			)
292
			->andWhere($qb->expr()->orX(
293
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
294
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
295
			))
296
			->orderBy('id');
297
298
		$cursor = $qb->execute();
299
		while($data = $cursor->fetch()) {
300
			$children[] = $this->createShare($data);
301
		}
302
		$cursor->closeCursor();
303
304
		return $children;
305
	}
306
307
	/**
308
	 * Delete a share
309
	 *
310
	 * @param \OCP\Share\IShare $share
311
	 */
312 View Code Duplication
	public function delete(\OCP\Share\IShare $share) {
313
		$qb = $this->dbConn->getQueryBuilder();
314
		$qb->delete('share')
315
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
316
317
		/*
318
		 * If the share is a group share delete all possible
319
		 * user defined groups shares.
320
		 */
321
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
322
			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
323
		}
324
325
		$qb->execute();
326
	}
327
328
	/**
329
	 * Unshare a share from the recipient. If this is a group share
330
	 * this means we need a special entry in the share db.
331
	 *
332
	 * @param \OCP\Share\IShare $share
333
	 * @param string $recipient UserId of recipient
334
	 * @throws BackendError
335
	 * @throws ProviderException
336
	 */
337
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
338
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
339
340
			$group = $this->groupManager->get($share->getSharedWith());
341
			$user = $this->userManager->get($recipient);
342
343
			if (is_null($group)) {
344
				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
345
			}
346
347
			if (!$group->inGroup($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($recipient) on line 341 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
348
				throw new ProviderException('Recipient not in receiving group');
349
			}
350
351
			// Try to fetch user specific share
352
			$qb = $this->dbConn->getQueryBuilder();
353
			$stmt = $qb->select('*')
354
				->from('share')
355
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
356
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
357
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
358
				->andWhere($qb->expr()->orX(
359
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
360
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
361
				))
362
				->execute();
363
364
			$data = $stmt->fetch();
365
366
			/*
367
			 * Check if there already is a user specific group share.
368
			 * If there is update it (if required).
369
			 */
370
			if ($data === false) {
371
				$qb = $this->dbConn->getQueryBuilder();
372
373
				$type = $share->getNodeType();
374
375
				//Insert new share
376
				$qb->insert('share')
377
					->values([
378
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
379
						'share_with' => $qb->createNamedParameter($recipient),
380
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
381
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
382
						'parent' => $qb->createNamedParameter($share->getId()),
383
						'item_type' => $qb->createNamedParameter($type),
384
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
385
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
386
						'file_target' => $qb->createNamedParameter($share->getTarget()),
387
						'permissions' => $qb->createNamedParameter(0),
388
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
389
					])->execute();
390
391
			} else if ($data['permissions'] !== 0) {
392
393
				// Update existing usergroup share
394
				$qb = $this->dbConn->getQueryBuilder();
395
				$qb->update('share')
396
					->set('permissions', $qb->createNamedParameter(0))
397
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
398
					->execute();
399
			}
400
401
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
402
403
			if ($share->getSharedWith() !== $recipient) {
404
				throw new ProviderException('Recipient does not match');
405
			}
406
407
			// We can just delete user and link shares
408
			$this->delete($share);
409
		} else {
410
			throw new ProviderException('Invalid shareType');
411
		}
412
	}
413
414
	/**
415
	 * @inheritdoc
416
	 *
417
	 * For now this only works for group shares
418
	 * If this gets implemented for normal shares we have to extend it
419
	 */
420
	public function restore(IShare $share, string $recipient): IShare {
421
		$qb = $this->dbConn->getQueryBuilder();
422
		$qb->select('permissions')
423
			->from('share')
424
			->where(
425
				$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
426
			);
427
		$cursor = $qb->execute();
428
		$data = $cursor->fetch();
429
		$cursor->closeCursor();
430
431
		$originalPermission = $data['permissions'];
432
433
		$qb = $this->dbConn->getQueryBuilder();
434
		$qb->update('share')
435
			->set('permissions', $qb->createNamedParameter($originalPermission))
436
			->where(
437
				$qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
438
			)->andWhere(
439
				$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
440
			)->andWhere(
441
				$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
442
			);
443
444
		$qb->execute();
445
446
		return $this->getShareById($share->getId(), $recipient);
447
	}
448
449
	/**
450
	 * @inheritdoc
451
	 */
452
	public function move(\OCP\Share\IShare $share, $recipient) {
453
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
454
			// Just update the target
455
			$qb = $this->dbConn->getQueryBuilder();
456
			$qb->update('share')
457
				->set('file_target', $qb->createNamedParameter($share->getTarget()))
458
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
459
				->execute();
460
461
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
462
463
			// Check if there is a usergroup share
464
			$qb = $this->dbConn->getQueryBuilder();
465
			$stmt = $qb->select('id')
466
				->from('share')
467
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
468
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
469
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
470
				->andWhere($qb->expr()->orX(
471
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
472
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
473
				))
474
				->setMaxResults(1)
475
				->execute();
476
477
			$data = $stmt->fetch();
478
			$stmt->closeCursor();
479
480
			if ($data === false) {
481
				// No usergroup share yet. Create one.
482
				$qb = $this->dbConn->getQueryBuilder();
483
				$qb->insert('share')
484
					->values([
485
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
486
						'share_with' => $qb->createNamedParameter($recipient),
487
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
488
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
489
						'parent' => $qb->createNamedParameter($share->getId()),
490
						'item_type' => $qb->createNamedParameter($share->getNodeType()),
491
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
492
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
493
						'file_target' => $qb->createNamedParameter($share->getTarget()),
494
						'permissions' => $qb->createNamedParameter($share->getPermissions()),
495
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
496
					])->execute();
497
			} else {
498
				// Already a usergroup share. Update it.
499
				$qb = $this->dbConn->getQueryBuilder();
500
				$qb->update('share')
501
					->set('file_target', $qb->createNamedParameter($share->getTarget()))
502
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
503
					->execute();
504
			}
505
		}
506
507
		return $share;
508
	}
509
510
	public function getSharesInFolder($userId, Folder $node, $reshares) {
511
		$qb = $this->dbConn->getQueryBuilder();
512
		$qb->select('*')
513
			->from('share', 's')
514
			->andWhere($qb->expr()->orX(
515
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
516
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
517
			));
518
519
		$qb->andWhere($qb->expr()->orX(
520
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
521
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
522
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
523
		));
524
525
		/**
526
		 * Reshares for this user are shares where they are the owner.
527
		 */
528 View Code Duplication
		if ($reshares === false) {
529
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
530
		} else {
531
			$qb->andWhere(
532
				$qb->expr()->orX(
533
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
534
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
535
				)
536
			);
537
		}
538
539
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
540
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
541
542
		$qb->orderBy('id');
543
544
		$cursor = $qb->execute();
545
		$shares = [];
546
		while ($data = $cursor->fetch()) {
547
			$shares[$data['fileid']][] = $this->createShare($data);
548
		}
549
		$cursor->closeCursor();
550
551
		return $shares;
552
	}
553
554
	/**
555
	 * @inheritdoc
556
	 */
557
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
558
		$qb = $this->dbConn->getQueryBuilder();
559
		$qb->select('*')
560
			->from('share')
561
			->andWhere($qb->expr()->orX(
562
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
563
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
564
			));
565
566
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
567
568
		/**
569
		 * Reshares for this user are shares where they are the owner.
570
		 */
571
		if ($reshares === false) {
572
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
573
		} else {
574
			$qb->andWhere(
575
				$qb->expr()->orX(
576
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
577
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
578
				)
579
			);
580
		}
581
582
		if ($node !== null) {
583
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
584
		}
585
586
		if ($limit !== -1) {
587
			$qb->setMaxResults($limit);
588
		}
589
590
		$qb->setFirstResult($offset);
591
		$qb->orderBy('id');
592
593
		$cursor = $qb->execute();
594
		$shares = [];
595
		while($data = $cursor->fetch()) {
596
			$shares[] = $this->createShare($data);
597
		}
598
		$cursor->closeCursor();
599
600
		return $shares;
601
	}
602
603
	/**
604
	 * @inheritdoc
605
	 */
606
	public function getShareById($id, $recipientId = null) {
607
		$qb = $this->dbConn->getQueryBuilder();
608
609
		$qb->select('*')
610
			->from('share')
611
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
612
			->andWhere(
613
				$qb->expr()->in(
614
					'share_type',
615
					$qb->createNamedParameter([
616
						\OCP\Share::SHARE_TYPE_USER,
617
						\OCP\Share::SHARE_TYPE_GROUP,
618
						\OCP\Share::SHARE_TYPE_LINK,
619
					], IQueryBuilder::PARAM_INT_ARRAY)
620
				)
621
			)
622
			->andWhere($qb->expr()->orX(
623
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
624
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
625
			));
626
627
		$cursor = $qb->execute();
628
		$data = $cursor->fetch();
629
		$cursor->closeCursor();
630
631
		if ($data === false) {
632
			throw new ShareNotFound();
633
		}
634
635
		try {
636
			$share = $this->createShare($data);
637
		} catch (InvalidShare $e) {
638
			throw new ShareNotFound();
639
		}
640
641
		// If the recipient is set for a group share resolve to that user
642
		if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
643
			$share = $this->resolveGroupShares([$share], $recipientId)[0];
0 ignored issues
show
Documentation introduced by
array($share) is of type array<integer,object<OCP...<OCP\\Share\\IShare>"}>, but the function expects a array<integer,object<OC\Share20\Share>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
644
		}
645
646
		return $share;
647
	}
648
649
	/**
650
	 * Get shares for a given path
651
	 *
652
	 * @param \OCP\Files\Node $path
653
	 * @return \OCP\Share\IShare[]
654
	 */
655
	public function getSharesByPath(Node $path) {
656
		$qb = $this->dbConn->getQueryBuilder();
657
658
		$cursor = $qb->select('*')
659
			->from('share')
660
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
661
			->andWhere(
662
				$qb->expr()->orX(
663
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
664
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
665
				)
666
			)
667
			->andWhere($qb->expr()->orX(
668
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
669
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
670
			))
671
			->execute();
672
673
		$shares = [];
674
		while($data = $cursor->fetch()) {
675
			$shares[] = $this->createShare($data);
676
		}
677
		$cursor->closeCursor();
678
679
		return $shares;
680
	}
681
682
	/**
683
	 * Returns whether the given database result can be interpreted as
684
	 * a share with accessible file (not trashed, not deleted)
685
	 */
686
	private function isAccessibleResult($data) {
687
		// exclude shares leading to deleted file entries
688
		if ($data['fileid'] === null) {
689
			return false;
690
		}
691
692
		// exclude shares leading to trashbin on home storages
693
		$pathSections = explode('/', $data['path'], 2);
694
		// FIXME: would not detect rare md5'd home storage case properly
695
		if ($pathSections[0] !== 'files'
696
		    	&& in_array(explode(':', $data['storage_string_id'], 2)[0], array('home', 'object'))) {
697
			return false;
698
		}
699
		return true;
700
	}
701
702
	/**
703
	 * @inheritdoc
704
	 */
705
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
706
		/** @var Share[] $shares */
707
		$shares = [];
708
709
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
710
			//Get shares directly with this user
711
			$qb = $this->dbConn->getQueryBuilder();
712
			$qb->select('s.*',
713
				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
714
				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
715
				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
716
			)
717
				->selectAlias('st.id', 'storage_string_id')
718
				->from('share', 's')
719
				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
720
				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
721
722
			// Order by id
723
			$qb->orderBy('s.id');
724
725
			// Set limit and offset
726
			if ($limit !== -1) {
727
				$qb->setMaxResults($limit);
728
			}
729
			$qb->setFirstResult($offset);
730
731
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
732
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
733
				->andWhere($qb->expr()->orX(
734
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
735
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
736
				));
737
738
			// Filter by node if provided
739
			if ($node !== null) {
740
				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
741
			}
742
743
			$cursor = $qb->execute();
744
745
			while($data = $cursor->fetch()) {
746
				if ($this->isAccessibleResult($data)) {
747
					$shares[] = $this->createShare($data);
748
				}
749
			}
750
			$cursor->closeCursor();
751
752
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
753
			$user = $this->userManager->get($userId);
754
			$allGroups = $this->groupManager->getUserGroups($user);
755
756
			/** @var Share[] $shares2 */
757
			$shares2 = [];
758
759
			$start = 0;
760
			while(true) {
761
				$groups = array_slice($allGroups, $start, 100);
762
				$start += 100;
763
764
				if ($groups === []) {
765
					break;
766
				}
767
768
				$qb = $this->dbConn->getQueryBuilder();
769
				$qb->select('s.*',
770
					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
771
					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
772
					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
773
				)
774
					->selectAlias('st.id', 'storage_string_id')
775
					->from('share', 's')
776
					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
777
					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
778
					->orderBy('s.id')
779
					->setFirstResult(0);
780
781
				if ($limit !== -1) {
782
					$qb->setMaxResults($limit - count($shares));
783
				}
784
785
				// Filter by node if provided
786
				if ($node !== null) {
787
					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
788
				}
789
790
791
				$groups = array_filter($groups, function($group) { return $group instanceof IGroup; });
792
				$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
793
794
				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
795
					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
796
						$groups,
797
						IQueryBuilder::PARAM_STR_ARRAY
798
					)))
799
					->andWhere($qb->expr()->orX(
800
						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
801
						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
802
					));
803
804
				$cursor = $qb->execute();
805
				while($data = $cursor->fetch()) {
806
					if ($offset > 0) {
807
						$offset--;
808
						continue;
809
					}
810
811
					if ($this->isAccessibleResult($data)) {
812
						$shares2[] = $this->createShare($data);
813
					}
814
				}
815
				$cursor->closeCursor();
816
			}
817
818
			/*
819
 			 * Resolve all group shares to user specific shares
820
 			 */
821
			$shares = $this->resolveGroupShares($shares2, $userId);
0 ignored issues
show
Documentation introduced by
$shares2 is of type array<integer,object<OCP\Share\IShare>>, but the function expects a array<integer,object<OC\Share20\Share>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
822
		} else {
823
			throw new BackendError('Invalid backend');
824
		}
825
826
827
		return $shares;
828
	}
829
830
	/**
831
	 * Get a share by token
832
	 *
833
	 * @param string $token
834
	 * @return \OCP\Share\IShare
835
	 * @throws ShareNotFound
836
	 */
837
	public function getShareByToken($token) {
838
		$qb = $this->dbConn->getQueryBuilder();
839
840
		$cursor = $qb->select('*')
841
			->from('share')
842
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
843
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
844
			->andWhere($qb->expr()->orX(
845
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
846
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
847
			))
848
			->execute();
849
850
		$data = $cursor->fetch();
851
852
		if ($data === false) {
853
			throw new ShareNotFound();
854
		}
855
856
		try {
857
			$share = $this->createShare($data);
858
		} catch (InvalidShare $e) {
859
			throw new ShareNotFound();
860
		}
861
862
		return $share;
863
	}
864
865
	/**
866
	 * Create a share object from an database row
867
	 *
868
	 * @param mixed[] $data
869
	 * @return \OCP\Share\IShare
870
	 * @throws InvalidShare
871
	 */
872
	private function createShare($data) {
873
		$share = new Share($this->rootFolder, $this->userManager);
874
		$share->setId((int)$data['id'])
875
			->setShareType((int)$data['share_type'])
876
			->setPermissions((int)$data['permissions'])
877
			->setTarget($data['file_target'])
878
			->setMailSend((bool)$data['mail_send']);
879
880
		$shareTime = new \DateTime();
881
		$shareTime->setTimestamp((int)$data['stime']);
882
		$share->setShareTime($shareTime);
883
884
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
885
			$share->setSharedWith($data['share_with']);
886
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
887
			$share->setSharedWith($data['share_with']);
888
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
889
			$share->setPassword($data['password']);
890
			$share->setToken($data['token']);
891
		}
892
893
		$share->setSharedBy($data['uid_initiator']);
894
		$share->setShareOwner($data['uid_owner']);
895
896
		$share->setNodeId((int)$data['file_source']);
897
		$share->setNodeType($data['item_type']);
898
899 View Code Duplication
		if ($data['expiration'] !== null) {
900
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
901
			$share->setExpirationDate($expiration);
0 ignored issues
show
Security Bug introduced by
It seems like $expiration defined by \DateTime::createFromFor...', $data['expiration']) on line 900 can also be of type false; however, OC\Share20\Share::setExpirationDate() does only seem to accept null|object<DateTime>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
902
		}
903
904
		if (isset($data['f_permissions'])) {
905
			$entryData = $data;
906
			$entryData['permissions'] = $entryData['f_permissions'];
907
			$entryData['parent'] = $entryData['f_parent'];
908
			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
909
				\OC::$server->getMimeTypeLoader()));
910
		}
911
912
		$share->setProviderId($this->identifier());
913
914
		return $share;
915
	}
916
917
	/**
918
	 * @param Share[] $shares
919
	 * @param $userId
920
	 * @return Share[] The updates shares if no update is found for a share return the original
921
	 */
922
	private function resolveGroupShares($shares, $userId) {
923
		$result = [];
924
925
		$start = 0;
926
		while(true) {
927
			/** @var Share[] $shareSlice */
928
			$shareSlice = array_slice($shares, $start, 100);
929
			$start += 100;
930
931
			if ($shareSlice === []) {
932
				break;
933
			}
934
935
			/** @var int[] $ids */
936
			$ids = [];
937
			/** @var Share[] $shareMap */
938
			$shareMap = [];
939
940
			foreach ($shareSlice as $share) {
941
				$ids[] = (int)$share->getId();
942
				$shareMap[$share->getId()] = $share;
943
			}
944
945
			$qb = $this->dbConn->getQueryBuilder();
946
947
			$query = $qb->select('*')
948
				->from('share')
949
				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
950
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
951
				->andWhere($qb->expr()->orX(
952
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
953
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
954
				));
955
956
			$stmt = $query->execute();
957
958
			while($data = $stmt->fetch()) {
959
				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
960
				$shareMap[$data['parent']]->setTarget($data['file_target']);
961
				$shareMap[$data['parent']]->setParent($data['parent']);
962
			}
963
964
			$stmt->closeCursor();
965
966
			foreach ($shareMap as $share) {
967
				$result[] = $share;
968
			}
969
		}
970
971
		return $result;
972
	}
973
974
	/**
975
	 * A user is deleted from the system
976
	 * So clean up the relevant shares.
977
	 *
978
	 * @param string $uid
979
	 * @param int $shareType
980
	 */
981
	public function userDeleted($uid, $shareType) {
982
		$qb = $this->dbConn->getQueryBuilder();
983
984
		$qb->delete('share');
985
986
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
987
			/*
988
			 * Delete all user shares that are owned by this user
989
			 * or that are received by this user
990
			 */
991
992
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
993
994
			$qb->andWhere(
995
				$qb->expr()->orX(
996
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
997
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
998
				)
999
			);
1000
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
1001
			/*
1002
			 * Delete all group shares that are owned by this user
1003
			 * Or special user group shares that are received by this user
1004
			 */
1005
			$qb->where(
1006
				$qb->expr()->andX(
1007
					$qb->expr()->orX(
1008
						$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
1009
						$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
1010
					),
1011
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1012
				)
1013
			);
1014
1015
			$qb->orWhere(
1016
				$qb->expr()->andX(
1017
					$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
1018
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1019
				)
1020
			);
1021 View Code Duplication
		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
1022
			/*
1023
			 * Delete all link shares owned by this user.
1024
			 * And all link shares initiated by this user (until #22327 is in)
1025
			 */
1026
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
1027
1028
			$qb->andWhere(
1029
				$qb->expr()->orX(
1030
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1031
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1032
				)
1033
			);
1034
		}
1035
1036
		$qb->execute();
1037
	}
1038
1039
	/**
1040
	 * Delete all shares received by this group. As well as any custom group
1041
	 * shares for group members.
1042
	 *
1043
	 * @param string $gid
1044
	 */
1045
	public function groupDeleted($gid) {
1046
		/*
1047
		 * First delete all custom group shares for group members
1048
		 */
1049
		$qb = $this->dbConn->getQueryBuilder();
1050
		$qb->select('id')
1051
			->from('share')
1052
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1053
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1054
1055
		$cursor = $qb->execute();
1056
		$ids = [];
1057
		while($row = $cursor->fetch()) {
1058
			$ids[] = (int)$row['id'];
1059
		}
1060
		$cursor->closeCursor();
1061
1062
		if (!empty($ids)) {
1063
			$chunks = array_chunk($ids, 100);
1064
			foreach ($chunks as $chunk) {
1065
				$qb->delete('share')
1066
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1067
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1068
				$qb->execute();
1069
			}
1070
		}
1071
1072
		/*
1073
		 * Now delete all the group shares
1074
		 */
1075
		$qb = $this->dbConn->getQueryBuilder();
1076
		$qb->delete('share')
1077
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1078
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1079
		$qb->execute();
1080
	}
1081
1082
	/**
1083
	 * Delete custom group shares to this group for this user
1084
	 *
1085
	 * @param string $uid
1086
	 * @param string $gid
1087
	 */
1088
	public function userDeletedFromGroup($uid, $gid) {
1089
		/*
1090
		 * Get all group shares
1091
		 */
1092
		$qb = $this->dbConn->getQueryBuilder();
1093
		$qb->select('id')
1094
			->from('share')
1095
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1096
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1097
1098
		$cursor = $qb->execute();
1099
		$ids = [];
1100
		while($row = $cursor->fetch()) {
1101
			$ids[] = (int)$row['id'];
1102
		}
1103
		$cursor->closeCursor();
1104
1105 View Code Duplication
		if (!empty($ids)) {
1106
			$chunks = array_chunk($ids, 100);
1107
			foreach ($chunks as $chunk) {
1108
				/*
1109
				 * Delete all special shares wit this users for the found group shares
1110
				 */
1111
				$qb->delete('share')
1112
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1113
					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1114
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1115
				$qb->execute();
1116
			}
1117
		}
1118
	}
1119
1120
	/**
1121
	 * @inheritdoc
1122
	 */
1123
	public function getAccessList($nodes, $currentAccess) {
1124
		$ids = [];
1125
		foreach ($nodes as $node) {
1126
			$ids[] = $node->getId();
1127
		}
1128
1129
		$qb = $this->dbConn->getQueryBuilder();
1130
1131
		$or = $qb->expr()->orX(
1132
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
1133
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
1134
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
1135
		);
1136
1137
		if ($currentAccess) {
1138
			$or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)));
1139
		}
1140
1141
		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1142
			->from('share')
1143
			->where(
1144
				$or
1145
			)
1146
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1147
			->andWhere($qb->expr()->orX(
1148
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1149
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1150
			));
1151
		$cursor = $qb->execute();
1152
1153
		$users = [];
1154
		$link = false;
1155
		while($row = $cursor->fetch()) {
1156
			$type = (int)$row['share_type'];
1157
			if ($type === \OCP\Share::SHARE_TYPE_USER) {
1158
				$uid = $row['share_with'];
1159
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1160
				$users[$uid][$row['id']] = $row;
1161
			} else if ($type === \OCP\Share::SHARE_TYPE_GROUP) {
1162
				$gid = $row['share_with'];
1163
				$group = $this->groupManager->get($gid);
1164
1165
				if ($group === null) {
1166
					continue;
1167
				}
1168
1169
				$userList = $group->getUsers();
1170
				foreach ($userList as $user) {
1171
					$uid = $user->getUID();
1172
					$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1173
					$users[$uid][$row['id']] = $row;
1174
				}
1175
			} else if ($type === \OCP\Share::SHARE_TYPE_LINK) {
1176
				$link = true;
1177
			} else if ($type === self::SHARE_TYPE_USERGROUP && $currentAccess === true) {
1178
				$uid = $row['share_with'];
1179
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1180
				$users[$uid][$row['id']] = $row;
1181
			}
1182
		}
1183
		$cursor->closeCursor();
1184
1185
		if ($currentAccess === true) {
1186
			$users = array_map([$this, 'filterSharesOfUser'], $users);
1187
			$users = array_filter($users);
1188
		} else {
1189
			$users = array_keys($users);
1190
		}
1191
1192
		return ['users' => $users, 'public' => $link];
1193
	}
1194
1195
	/**
1196
	 * For each user the path with the fewest slashes is returned
1197
	 * @param array $shares
1198
	 * @return array
1199
	 */
1200
	protected function filterSharesOfUser(array $shares) {
1201
		// Group shares when the user has a share exception
1202
		foreach ($shares as $id => $share) {
1203
			$type = (int) $share['share_type'];
1204
			$permissions = (int) $share['permissions'];
1205
1206
			if ($type === self::SHARE_TYPE_USERGROUP) {
1207
				unset($shares[$share['parent']]);
1208
1209
				if ($permissions === 0) {
1210
					unset($shares[$id]);
1211
				}
1212
			}
1213
		}
1214
1215
		$best = [];
1216
		$bestDepth = 0;
1217
		foreach ($shares as $id => $share) {
1218
			$depth = substr_count($share['file_target'], '/');
1219
			if (empty($best) || $depth < $bestDepth) {
1220
				$bestDepth = $depth;
1221
				$best = [
1222
					'node_id' => $share['file_source'],
1223
					'node_path' => $share['file_target'],
1224
				];
1225
			}
1226
		}
1227
1228
		return $best;
1229
	}
1230
}
1231