Completed
Branch master (69e0e2)
by Johannes
12:46
created

DefaultShareProvider::filterSharesOfUser()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
305
		$qb = $this->dbConn->getQueryBuilder();
306
		$qb->delete('share')
307
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
308
309
		/*
310
		 * If the share is a group share delete all possible
311
		 * user defined groups shares.
312
		 */
313
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
314
			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
315
		}
316
317
		$qb->execute();
318
	}
319
320
	/**
321
	 * Unshare a share from the recipient. If this is a group share
322
	 * this means we need a special entry in the share db.
323
	 *
324
	 * @param \OCP\Share\IShare $share
325
	 * @param string $recipient UserId of recipient
326
	 * @throws BackendError
327
	 * @throws ProviderException
328
	 */
329
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
330
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
331
332
			$group = $this->groupManager->get($share->getSharedWith());
333
			$user = $this->userManager->get($recipient);
334
335
			if (is_null($group)) {
336
				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
337
			}
338
339
			if (!$group->inGroup($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($recipient) on line 333 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...
340
				throw new ProviderException('Recipient not in receiving group');
341
			}
342
343
			// Try to fetch user specific share
344
			$qb = $this->dbConn->getQueryBuilder();
345
			$stmt = $qb->select('*')
346
				->from('share')
347
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
348
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
349
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
350
				->andWhere($qb->expr()->orX(
351
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
352
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
353
				))
354
				->execute();
355
356
			$data = $stmt->fetch();
357
358
			/*
359
			 * Check if there already is a user specific group share.
360
			 * If there is update it (if required).
361
			 */
362
			if ($data === false) {
363
				$qb = $this->dbConn->getQueryBuilder();
364
365
				$type = $share->getNodeType();
366
367
				//Insert new share
368
				$qb->insert('share')
369
					->values([
370
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
371
						'share_with' => $qb->createNamedParameter($recipient),
372
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
373
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
374
						'parent' => $qb->createNamedParameter($share->getId()),
375
						'item_type' => $qb->createNamedParameter($type),
376
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
377
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
378
						'file_target' => $qb->createNamedParameter($share->getTarget()),
379
						'permissions' => $qb->createNamedParameter(0),
380
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
381
					])->execute();
382
383
			} else if ($data['permissions'] !== 0) {
384
385
				// Update existing usergroup share
386
				$qb = $this->dbConn->getQueryBuilder();
387
				$qb->update('share')
388
					->set('permissions', $qb->createNamedParameter(0))
389
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
390
					->execute();
391
			}
392
393
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
394
395
			if ($share->getSharedWith() !== $recipient) {
396
				throw new ProviderException('Recipient does not match');
397
			}
398
399
			// We can just delete user and link shares
400
			$this->delete($share);
401
		} else {
402
			throw new ProviderException('Invalid shareType');
403
		}
404
	}
405
406
	/**
407
	 * @inheritdoc
408
	 */
409
	public function move(\OCP\Share\IShare $share, $recipient) {
410
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
411
			// Just update the target
412
			$qb = $this->dbConn->getQueryBuilder();
413
			$qb->update('share')
414
				->set('file_target', $qb->createNamedParameter($share->getTarget()))
415
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
416
				->execute();
417
418
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
419
420
			// Check if there is a usergroup share
421
			$qb = $this->dbConn->getQueryBuilder();
422
			$stmt = $qb->select('id')
423
				->from('share')
424
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
425
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
426
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
427
				->andWhere($qb->expr()->orX(
428
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
429
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
430
				))
431
				->setMaxResults(1)
432
				->execute();
433
434
			$data = $stmt->fetch();
435
			$stmt->closeCursor();
436
437
			if ($data === false) {
438
				// No usergroup share yet. Create one.
439
				$qb = $this->dbConn->getQueryBuilder();
440
				$qb->insert('share')
441
					->values([
442
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
443
						'share_with' => $qb->createNamedParameter($recipient),
444
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
445
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
446
						'parent' => $qb->createNamedParameter($share->getId()),
447
						'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
448
						'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
449
						'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
450
						'file_target' => $qb->createNamedParameter($share->getTarget()),
451
						'permissions' => $qb->createNamedParameter($share->getPermissions()),
452
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
453
					])->execute();
454
			} else {
455
				// Already a usergroup share. Update it.
456
				$qb = $this->dbConn->getQueryBuilder();
457
				$qb->update('share')
458
					->set('file_target', $qb->createNamedParameter($share->getTarget()))
459
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
460
					->execute();
461
			}
462
		}
463
464
		return $share;
465
	}
466
467
	public function getSharesInFolder($userId, Folder $node, $reshares) {
468
		$qb = $this->dbConn->getQueryBuilder();
469
		$qb->select('*')
470
			->from('share', 's')
471
			->andWhere($qb->expr()->orX(
472
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
473
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
474
			));
475
476
		$qb->andWhere($qb->expr()->orX(
477
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
478
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
479
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
480
		));
481
482
		/**
483
		 * Reshares for this user are shares where they are the owner.
484
		 */
485 View Code Duplication
		if ($reshares === false) {
486
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
487
		} else {
488
			$qb->andWhere(
489
				$qb->expr()->orX(
490
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
491
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
492
				)
493
			);
494
		}
495
496
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
497
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
498
499
		$qb->orderBy('id');
500
501
		$cursor = $qb->execute();
502
		$shares = [];
503
		while ($data = $cursor->fetch()) {
504
			$shares[$data['fileid']][] = $this->createShare($data);
505
		}
506
		$cursor->closeCursor();
507
508
		return $shares;
509
	}
510
511
	/**
512
	 * @inheritdoc
513
	 */
514
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
515
		$qb = $this->dbConn->getQueryBuilder();
516
		$qb->select('*')
517
			->from('share')
518
			->andWhere($qb->expr()->orX(
519
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
520
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
521
			));
522
523
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
524
525
		/**
526
		 * Reshares for this user are shares where they are the owner.
527
		 */
528
		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
		if ($node !== null) {
540
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
541
		}
542
543
		if ($limit !== -1) {
544
			$qb->setMaxResults($limit);
545
		}
546
547
		$qb->setFirstResult($offset);
548
		$qb->orderBy('id');
549
550
		$cursor = $qb->execute();
551
		$shares = [];
552
		while($data = $cursor->fetch()) {
553
			$shares[] = $this->createShare($data);
554
		}
555
		$cursor->closeCursor();
556
557
		return $shares;
558
	}
559
560
	/**
561
	 * @inheritdoc
562
	 */
563
	public function getShareById($id, $recipientId = null) {
564
		$qb = $this->dbConn->getQueryBuilder();
565
566
		$qb->select('*')
567
			->from('share')
568
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
569
			->andWhere(
570
				$qb->expr()->in(
571
					'share_type',
572
					$qb->createNamedParameter([
573
						\OCP\Share::SHARE_TYPE_USER,
574
						\OCP\Share::SHARE_TYPE_GROUP,
575
						\OCP\Share::SHARE_TYPE_LINK,
576
					], IQueryBuilder::PARAM_INT_ARRAY)
577
				)
578
			)
579
			->andWhere($qb->expr()->orX(
580
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
581
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
582
			));
583
584
		$cursor = $qb->execute();
585
		$data = $cursor->fetch();
586
		$cursor->closeCursor();
587
588
		if ($data === false) {
589
			throw new ShareNotFound();
590
		}
591
592
		try {
593
			$share = $this->createShare($data);
594
		} catch (InvalidShare $e) {
595
			throw new ShareNotFound();
596
		}
597
598
		// If the recipient is set for a group share resolve to that user
599
		if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
600
			$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...
601
		}
602
603
		return $share;
604
	}
605
606
	/**
607
	 * Get shares for a given path
608
	 *
609
	 * @param \OCP\Files\Node $path
610
	 * @return \OCP\Share\IShare[]
611
	 */
612
	public function getSharesByPath(Node $path) {
613
		$qb = $this->dbConn->getQueryBuilder();
614
615
		$cursor = $qb->select('*')
616
			->from('share')
617
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
618
			->andWhere(
619
				$qb->expr()->orX(
620
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
621
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
622
				)
623
			)
624
			->andWhere($qb->expr()->orX(
625
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
626
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
627
			))
628
			->execute();
629
630
		$shares = [];
631
		while($data = $cursor->fetch()) {
632
			$shares[] = $this->createShare($data);
633
		}
634
		$cursor->closeCursor();
635
636
		return $shares;
637
	}
638
639
	/**
640
	 * Returns whether the given database result can be interpreted as
641
	 * a share with accessible file (not trashed, not deleted)
642
	 */
643
	private function isAccessibleResult($data) {
644
		// exclude shares leading to deleted file entries
645
		if ($data['fileid'] === null) {
646
			return false;
647
		}
648
649
		// exclude shares leading to trashbin on home storages
650
		$pathSections = explode('/', $data['path'], 2);
651
		// FIXME: would not detect rare md5'd home storage case properly
652
		if ($pathSections[0] !== 'files'
653
		    	&& in_array(explode(':', $data['storage_string_id'], 2)[0], array('home', 'object'))) {
654
			return false;
655
		}
656
		return true;
657
	}
658
659
	/**
660
	 * @inheritdoc
661
	 */
662
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
663
		/** @var Share[] $shares */
664
		$shares = [];
665
666
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
667
			//Get shares directly with this user
668
			$qb = $this->dbConn->getQueryBuilder();
669
			$qb->select('s.*',
670
				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
671
				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
672
				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
673
			)
674
				->selectAlias('st.id', 'storage_string_id')
675
				->from('share', 's')
676
				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
677
				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
678
679
			// Order by id
680
			$qb->orderBy('s.id');
681
682
			// Set limit and offset
683
			if ($limit !== -1) {
684
				$qb->setMaxResults($limit);
685
			}
686
			$qb->setFirstResult($offset);
687
688
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
689
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
690
				->andWhere($qb->expr()->orX(
691
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
692
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
693
				));
694
695
			// Filter by node if provided
696
			if ($node !== null) {
697
				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
698
			}
699
700
			$cursor = $qb->execute();
701
702
			while($data = $cursor->fetch()) {
703
				if ($this->isAccessibleResult($data)) {
704
					$shares[] = $this->createShare($data);
705
				}
706
			}
707
			$cursor->closeCursor();
708
709
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
710
			$user = $this->userManager->get($userId);
711
			$allGroups = $this->groupManager->getUserGroups($user);
712
713
			/** @var Share[] $shares2 */
714
			$shares2 = [];
715
716
			$start = 0;
717
			while(true) {
718
				$groups = array_slice($allGroups, $start, 100);
719
				$start += 100;
720
721
				if ($groups === []) {
722
					break;
723
				}
724
725
				$qb = $this->dbConn->getQueryBuilder();
726
				$qb->select('s.*',
727
					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
728
					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
729
					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
730
				)
731
					->selectAlias('st.id', 'storage_string_id')
732
					->from('share', 's')
733
					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
734
					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
735
					->orderBy('s.id')
736
					->setFirstResult(0);
737
738
				if ($limit !== -1) {
739
					$qb->setMaxResults($limit - count($shares));
740
				}
741
742
				// Filter by node if provided
743
				if ($node !== null) {
744
					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
745
				}
746
747
				$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
748
749
				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
750
					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
751
						$groups,
752
						IQueryBuilder::PARAM_STR_ARRAY
753
					)))
754
					->andWhere($qb->expr()->orX(
755
						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
756
						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
757
					));
758
759
				$cursor = $qb->execute();
760
				while($data = $cursor->fetch()) {
761
					if ($offset > 0) {
762
						$offset--;
763
						continue;
764
					}
765
766
					if ($this->isAccessibleResult($data)) {
767
						$shares2[] = $this->createShare($data);
768
					}
769
				}
770
				$cursor->closeCursor();
771
			}
772
773
			/*
774
 			 * Resolve all group shares to user specific shares
775
 			 */
776
			$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...
777
		} else {
778
			throw new BackendError('Invalid backend');
779
		}
780
781
782
		return $shares;
783
	}
784
785
	/**
786
	 * Get a share by token
787
	 *
788
	 * @param string $token
789
	 * @return \OCP\Share\IShare
790
	 * @throws ShareNotFound
791
	 */
792
	public function getShareByToken($token) {
793
		$qb = $this->dbConn->getQueryBuilder();
794
795
		$cursor = $qb->select('*')
796
			->from('share')
797
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
798
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
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
			->execute();
804
805
		$data = $cursor->fetch();
806
807
		if ($data === false) {
808
			throw new ShareNotFound();
809
		}
810
811
		try {
812
			$share = $this->createShare($data);
813
		} catch (InvalidShare $e) {
814
			throw new ShareNotFound();
815
		}
816
817
		return $share;
818
	}
819
820
	/**
821
	 * Create a share object from an database row
822
	 *
823
	 * @param mixed[] $data
824
	 * @return \OCP\Share\IShare
825
	 * @throws InvalidShare
826
	 */
827
	private function createShare($data) {
828
		$share = new Share($this->rootFolder, $this->userManager);
829
		$share->setId((int)$data['id'])
830
			->setShareType((int)$data['share_type'])
831
			->setPermissions((int)$data['permissions'])
832
			->setTarget($data['file_target'])
833
			->setMailSend((bool)$data['mail_send']);
834
835
		$shareTime = new \DateTime();
836
		$shareTime->setTimestamp((int)$data['stime']);
837
		$share->setShareTime($shareTime);
838
839
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
840
			$share->setSharedWith($data['share_with']);
841
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
842
			$share->setSharedWith($data['share_with']);
843
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
844
			$share->setPassword($data['password']);
845
			$share->setToken($data['token']);
846
		}
847
848
		$share->setSharedBy($data['uid_initiator']);
849
		$share->setShareOwner($data['uid_owner']);
850
851
		$share->setNodeId((int)$data['file_source']);
852
		$share->setNodeType($data['item_type']);
853
854 View Code Duplication
		if ($data['expiration'] !== null) {
855
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
856
			$share->setExpirationDate($expiration);
0 ignored issues
show
Security Bug introduced by
It seems like $expiration defined by \DateTime::createFromFor...', $data['expiration']) on line 855 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...
857
		}
858
859
		if (isset($data['f_permissions'])) {
860
			$entryData = $data;
861
			$entryData['permissions'] = $entryData['f_permissions'];
862
			$entryData['parent'] = $entryData['f_parent'];;
863
			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
864
				\OC::$server->getMimeTypeLoader()));
865
		}
866
867
		$share->setProviderId($this->identifier());
868
869
		return $share;
870
	}
871
872
	/**
873
	 * @param Share[] $shares
874
	 * @param $userId
875
	 * @return Share[] The updates shares if no update is found for a share return the original
876
	 */
877
	private function resolveGroupShares($shares, $userId) {
878
		$result = [];
879
880
		$start = 0;
881
		while(true) {
882
			/** @var Share[] $shareSlice */
883
			$shareSlice = array_slice($shares, $start, 100);
884
			$start += 100;
885
886
			if ($shareSlice === []) {
887
				break;
888
			}
889
890
			/** @var int[] $ids */
891
			$ids = [];
892
			/** @var Share[] $shareMap */
893
			$shareMap = [];
894
895
			foreach ($shareSlice as $share) {
896
				$ids[] = (int)$share->getId();
897
				$shareMap[$share->getId()] = $share;
898
			}
899
900
			$qb = $this->dbConn->getQueryBuilder();
901
902
			$query = $qb->select('*')
903
				->from('share')
904
				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
905
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
906
				->andWhere($qb->expr()->orX(
907
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
908
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
909
				));
910
911
			$stmt = $query->execute();
912
913
			while($data = $stmt->fetch()) {
914
				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
915
				$shareMap[$data['parent']]->setTarget($data['file_target']);
916
			}
917
918
			$stmt->closeCursor();
919
920
			foreach ($shareMap as $share) {
921
				$result[] = $share;
922
			}
923
		}
924
925
		return $result;
926
	}
927
928
	/**
929
	 * A user is deleted from the system
930
	 * So clean up the relevant shares.
931
	 *
932
	 * @param string $uid
933
	 * @param int $shareType
934
	 */
935
	public function userDeleted($uid, $shareType) {
936
		$qb = $this->dbConn->getQueryBuilder();
937
938
		$qb->delete('share');
939
940
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
941
			/*
942
			 * Delete all user shares that are owned by this user
943
			 * or that are received by this user
944
			 */
945
946
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
947
948
			$qb->andWhere(
949
				$qb->expr()->orX(
950
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
951
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
952
				)
953
			);
954
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
955
			/*
956
			 * Delete all group shares that are owned by this user
957
			 * Or special user group shares that are received by this user
958
			 */
959
			$qb->where(
960
				$qb->expr()->andX(
961
					$qb->expr()->orX(
962
						$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
963
						$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
964
					),
965
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
966
				)
967
			);
968
969
			$qb->orWhere(
970
				$qb->expr()->andX(
971
					$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
972
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
973
				)
974
			);
975 View Code Duplication
		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
976
			/*
977
			 * Delete all link shares owned by this user.
978
			 * And all link shares initiated by this user (until #22327 is in)
979
			 */
980
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
981
982
			$qb->andWhere(
983
				$qb->expr()->orX(
984
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
985
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
986
				)
987
			);
988
		}
989
990
		$qb->execute();
991
	}
992
993
	/**
994
	 * Delete all shares received by this group. As well as any custom group
995
	 * shares for group members.
996
	 *
997
	 * @param string $gid
998
	 */
999
	public function groupDeleted($gid) {
1000
		/*
1001
		 * First delete all custom group shares for group members
1002
		 */
1003
		$qb = $this->dbConn->getQueryBuilder();
1004
		$qb->select('id')
1005
			->from('share')
1006
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1007
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1008
1009
		$cursor = $qb->execute();
1010
		$ids = [];
1011
		while($row = $cursor->fetch()) {
1012
			$ids[] = (int)$row['id'];
1013
		}
1014
		$cursor->closeCursor();
1015
1016
		if (!empty($ids)) {
1017
			$chunks = array_chunk($ids, 100);
1018
			foreach ($chunks as $chunk) {
1019
				$qb->delete('share')
1020
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1021
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1022
				$qb->execute();
1023
			}
1024
		}
1025
1026
		/*
1027
		 * Now delete all the group shares
1028
		 */
1029
		$qb = $this->dbConn->getQueryBuilder();
1030
		$qb->delete('share')
1031
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1032
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1033
		$qb->execute();
1034
	}
1035
1036
	/**
1037
	 * Delete custom group shares to this group for this user
1038
	 *
1039
	 * @param string $uid
1040
	 * @param string $gid
1041
	 */
1042
	public function userDeletedFromGroup($uid, $gid) {
1043
		/*
1044
		 * Get all group shares
1045
		 */
1046
		$qb = $this->dbConn->getQueryBuilder();
1047
		$qb->select('id')
1048
			->from('share')
1049
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
1050
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1051
1052
		$cursor = $qb->execute();
1053
		$ids = [];
1054
		while($row = $cursor->fetch()) {
1055
			$ids[] = (int)$row['id'];
1056
		}
1057
		$cursor->closeCursor();
1058
1059 View Code Duplication
		if (!empty($ids)) {
1060
			$chunks = array_chunk($ids, 100);
1061
			foreach ($chunks as $chunk) {
1062
				/*
1063
				 * Delete all special shares wit this users for the found group shares
1064
				 */
1065
				$qb->delete('share')
1066
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
1067
					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1068
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1069
				$qb->execute();
1070
			}
1071
		}
1072
	}
1073
1074
	/**
1075
	 * @inheritdoc
1076
	 */
1077
	public function getAccessList($nodes, $currentAccess) {
1078
		$ids = [];
1079
		foreach ($nodes as $node) {
1080
			$ids[] = $node->getId();
1081
		}
1082
1083
		$qb = $this->dbConn->getQueryBuilder();
1084
1085
		$or = $qb->expr()->orX(
1086
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
1087
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
1088
			$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))
1089
		);
1090
1091
		if ($currentAccess) {
1092
			$or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)));
1093
		}
1094
1095
		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1096
			->from('share')
1097
			->where(
1098
				$or
1099
			)
1100
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1101
			->andWhere($qb->expr()->orX(
1102
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1103
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1104
			));
1105
		$cursor = $qb->execute();
1106
1107
		$users = [];
1108
		$link = false;
1109
		while($row = $cursor->fetch()) {
1110
			$type = (int)$row['share_type'];
1111
			if ($type === \OCP\Share::SHARE_TYPE_USER) {
1112
				$uid = $row['share_with'];
1113
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1114
				$users[$uid][$row['id']] = $row;
1115
			} else if ($type === \OCP\Share::SHARE_TYPE_GROUP) {
1116
				$gid = $row['share_with'];
1117
				$group = $this->groupManager->get($gid);
1118
1119
				if ($gid === null) {
1120
					continue;
1121
				}
1122
1123
				$userList = $group->getUsers();
1124
				foreach ($userList as $user) {
1125
					$uid = $user->getUID();
1126
					$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1127
					$users[$uid][$row['id']] = $row;
1128
				}
1129
			} else if ($type === \OCP\Share::SHARE_TYPE_LINK) {
1130
				$link = true;
1131
			} else if ($type === self::SHARE_TYPE_USERGROUP && $currentAccess === true) {
1132
				$uid = $row['share_with'];
1133
				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1134
				$users[$uid][$row['id']] = $row;
1135
			}
1136
		}
1137
		$cursor->closeCursor();
1138
1139
		if ($currentAccess === true) {
1140
			$users = array_map([$this, 'filterSharesOfUser'], $users);
1141
			$users = array_filter($users);
1142
		} else {
1143
			$users = array_keys($users);
1144
		}
1145
1146
		return ['users' => $users, 'public' => $link];
1147
	}
1148
1149
	/**
1150
	 * For each user the path with the fewest slashes is returned
1151
	 * @param array $shares
1152
	 * @return array
1153
	 */
1154
	protected function filterSharesOfUser(array $shares) {
1155
		// Group shares when the user has a share exception
1156
		foreach ($shares as $id => $share) {
1157
			$type = (int) $share['share_type'];
1158
			$permissions = (int) $share['permissions'];
1159
1160
			if ($type === self::SHARE_TYPE_USERGROUP) {
1161
				unset($shares[$share['parent']]);
1162
1163
				if ($permissions === 0) {
1164
					unset($shares[$id]);
1165
				}
1166
			}
1167
		}
1168
1169
		$best = [];
1170
		$bestDepth = 0;
1171
		foreach ($shares as $id => $share) {
1172
			$depth = substr_count($share['file_target'], '/');
1173
			if (empty($best) || $depth < $bestDepth) {
1174
				$bestDepth = $depth;
1175
				$best = [
1176
					'node_id' => $share['file_source'],
1177
					'node_path' => $share['file_target'],
1178
				];
1179
			}
1180
		}
1181
1182
		return $best;
1183
	}
1184
}
1185