Completed
Push — stable10 ( e9c41b...4d0037 )
by Morris
07:25
created

DefaultShareProvider::move()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 57
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 44
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 57
rs 9.0309

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 OCP\Files\File;
27
use OCP\Share\IShareProvider;
28
use OC\Share20\Exception\InvalidShare;
29
use OC\Share20\Exception\ProviderException;
30
use OCP\Share\Exceptions\ShareNotFound;
31
use OC\Share20\Exception\BackendError;
32
use OCP\DB\QueryBuilder\IQueryBuilder;
33
use OCP\IGroup;
34
use OCP\IGroupManager;
35
use OCP\IUserManager;
36
use OCP\Files\IRootFolder;
37
use OCP\IDBConnection;
38
use OCP\Files\Node;
39
40
/**
41
 * Class DefaultShareProvider
42
 *
43
 * @package OC\Share20
44
 */
45
class DefaultShareProvider implements IShareProvider {
46
47
	// Special share type for user modified group shares
48
	const SHARE_TYPE_USERGROUP = 2;
49
50
	/** @var IDBConnection */
51
	private $dbConn;
52
53
	/** @var IUserManager */
54
	private $userManager;
55
56
	/** @var IGroupManager */
57
	private $groupManager;
58
59
	/** @var IRootFolder */
60
	private $rootFolder;
61
62
	/**
63
	 * DefaultShareProvider constructor.
64
	 *
65
	 * @param IDBConnection $connection
66
	 * @param IUserManager $userManager
67
	 * @param IGroupManager $groupManager
68
	 * @param IRootFolder $rootFolder
69
	 */
70
	public function __construct(
71
			IDBConnection $connection,
72
			IUserManager $userManager,
73
			IGroupManager $groupManager,
74
			IRootFolder $rootFolder) {
75
		$this->dbConn = $connection;
76
		$this->userManager = $userManager;
77
		$this->groupManager = $groupManager;
78
		$this->rootFolder = $rootFolder;
79
	}
80
81
	/**
82
	 * Return the identifier of this provider.
83
	 *
84
	 * @return string Containing only [a-zA-Z0-9]
85
	 */
86
	public function identifier() {
87
		return 'ocinternal';
88
	}
89
90
	/**
91
	 * Share a path
92
	 *
93
	 * @param \OCP\Share\IShare $share
94
	 * @return \OCP\Share\IShare The share object
95
	 * @throws ShareNotFound
96
	 * @throws \Exception
97
	 */
98
	public function create(\OCP\Share\IShare $share) {
99
		$qb = $this->dbConn->getQueryBuilder();
100
101
		$qb->insert('share');
102
		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
103
104
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
105
			//Set the UID of the user we share with
106
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
107
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
108
			//Set the GID of the group we share with
109
			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
110
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
111
			//Set the token of the share
112
			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
113
114
			//If a password is set store it
115
			if ($share->getPassword() !== null) {
116
				$qb->setValue('share_with', $qb->createNamedParameter($share->getPassword()));
117
			}
118
119
			//If an expiration date is set store it
120
			if ($share->getExpirationDate() !== null) {
121
				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
122
			}
123
124
			if (method_exists($share, 'getParent')) {
125
				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
126
			}
127
		} else {
128
			throw new \Exception('invalid share type!');
129
		}
130
131
		// Set what is shares
132
		$qb->setValue('item_type', $qb->createParameter('itemType'));
133
		if ($share->getNode() instanceof \OCP\Files\File) {
134
			$qb->setParameter('itemType', 'file');
135
		} else {
136
			$qb->setParameter('itemType', 'folder');
137
		}
138
139
		// Set the file id
140
		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
141
		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
142
143
		// set the permissions
144
		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
145
146
		// Set who created this share
147
		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
148
149
		// Set who is the owner of this file/folder (and this the owner of the share)
150
		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
151
152
		// Set the file target
153
		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
154
155
		// Set the time this share was created
156
		$qb->setValue('stime', $qb->createNamedParameter(time()));
157
158
		// insert the data and fetch the id of the share
159
		$this->dbConn->beginTransaction();
160
		$qb->execute();
161
		$id = $this->dbConn->lastInsertId('*PREFIX*share');
162
		$this->dbConn->commit();
163
164
		// Now fetch the inserted share and create a complete share object
165
		$qb = $this->dbConn->getQueryBuilder();
166
		$qb->select('*')
167
			->from('share')
168
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
169
170
		$cursor = $qb->execute();
171
		$data = $cursor->fetch();
172
		$cursor->closeCursor();
173
174
		if ($data === false) {
175
			throw new ShareNotFound();
176
		}
177
178
		$share = $this->createShare($data);
179
		return $share;
180
	}
181
182
	/**
183
	 * Update a share
184
	 *
185
	 * @param \OCP\Share\IShare $share
186
	 * @return \OCP\Share\IShare The share object
187
	 */
188
	public function update(\OCP\Share\IShare $share) {
189
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
190
			/*
191
			 * We allow updating the recipient on user shares.
192
			 */
193
			$qb = $this->dbConn->getQueryBuilder();
194
			$qb->update('share')
195
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
196
				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
197
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
198
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
199
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
200
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
201
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
202
				->execute();
203
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
204
			$qb = $this->dbConn->getQueryBuilder();
205
			$qb->update('share')
206
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
207
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
208
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
209
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
210
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
211
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
212
				->execute();
213
214
			/*
215
			 * Update all user defined group shares
216
			 */
217
			$qb = $this->dbConn->getQueryBuilder();
218
			$qb->update('share')
219
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
220
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
221
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
222
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
223
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
224
				->execute();
225
226
			/*
227
			 * Now update the permissions for all children that have not set it to 0
228
			 */
229
			$qb = $this->dbConn->getQueryBuilder();
230
			$qb->update('share')
231
				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
232
				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
233
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
234
				->execute();
235
236
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
237
			$qb = $this->dbConn->getQueryBuilder();
238
			$qb->update('share')
239
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
240
				->set('share_with', $qb->createNamedParameter($share->getPassword()))
241
				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
242
				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
243
				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
244
				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
245
				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
246
				->set('token', $qb->createNamedParameter($share->getToken()))
247
				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
248
				->execute();
249
		}
250
251
		return $share;
252
	}
253
254
	/**
255
	 * Get all children of this share
256
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
257
	 *
258
	 * @param \OCP\Share\IShare $parent
259
	 * @return \OCP\Share\IShare[]
260
	 */
261
	public function getChildren(\OCP\Share\IShare $parent) {
262
		$children = [];
263
264
		$qb = $this->dbConn->getQueryBuilder();
265
		$qb->select('*')
266
			->from('share')
267
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
268
			->andWhere(
269
				$qb->expr()->in(
270
					'share_type',
271
					$qb->createNamedParameter([
272
						\OCP\Share::SHARE_TYPE_USER,
273
						\OCP\Share::SHARE_TYPE_GROUP,
274
						\OCP\Share::SHARE_TYPE_LINK,
275
					], IQueryBuilder::PARAM_INT_ARRAY)
276
				)
277
			)
278
			->andWhere($qb->expr()->orX(
279
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
280
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
281
			))
282
			->orderBy('id');
283
284
		$cursor = $qb->execute();
285
		while($data = $cursor->fetch()) {
286
			$children[] = $this->createShare($data);
287
		}
288
		$cursor->closeCursor();
289
290
		return $children;
291
	}
292
293
	/**
294
	 * Delete a share
295
	 *
296
	 * @param \OCP\Share\IShare $share
297
	 */
298 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...
299
		$qb = $this->dbConn->getQueryBuilder();
300
		$qb->delete('share')
301
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
302
303
		/*
304
		 * If the share is a group share delete all possible
305
		 * user defined groups shares.
306
		 */
307
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
308
			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
309
		}
310
311
		$qb->execute();
312
	}
313
314
	/**
315
	 * Unshare a share from the recipient. If this is a group share
316
	 * this means we need a special entry in the share db.
317
	 *
318
	 * @param \OCP\Share\IShare $share
319
	 * @param string $recipient UserId of recipient
320
	 * @throws BackendError
321
	 * @throws ProviderException
322
	 */
323
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
324
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
325
326
			$group = $this->groupManager->get($share->getSharedWith());
327
			$user = $this->userManager->get($recipient);
328
329
			if (!$group->inGroup($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($recipient) on line 327 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...
330
				throw new ProviderException('Recipient not in receiving group');
331
			}
332
333
			// Try to fetch user specific share
334
			$qb = $this->dbConn->getQueryBuilder();
335
			$stmt = $qb->select('*')
336
				->from('share')
337
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
338
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
339
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
340
				->andWhere($qb->expr()->orX(
341
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
342
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
343
				))
344
				->execute();
345
346
			$data = $stmt->fetch();
347
348
			/*
349
			 * Check if there already is a user specific group share.
350
			 * If there is update it (if required).
351
			 */
352
			if ($data === false) {
353
				$qb = $this->dbConn->getQueryBuilder();
354
355
				$type = $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder';
356
357
				//Insert new share
358
				$qb->insert('share')
359
					->values([
360
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
361
						'share_with' => $qb->createNamedParameter($recipient),
362
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
363
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
364
						'parent' => $qb->createNamedParameter($share->getId()),
365
						'item_type' => $qb->createNamedParameter($type),
366
						'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
367
						'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
368
						'file_target' => $qb->createNamedParameter($share->getTarget()),
369
						'permissions' => $qb->createNamedParameter(0),
370
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
371
					])->execute();
372
373
			} else if ($data['permissions'] !== 0) {
374
375
				// Update existing usergroup share
376
				$qb = $this->dbConn->getQueryBuilder();
377
				$qb->update('share')
378
					->set('permissions', $qb->createNamedParameter(0))
379
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
380
					->execute();
381
			}
382
383
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
384
385
			if ($share->getSharedWith() !== $recipient) {
386
				throw new ProviderException('Recipient does not match');
387
			}
388
389
			// We can just delete user and link shares
390
			$this->delete($share);
391
		} else {
392
			throw new ProviderException('Invalid shareType');
393
		}
394
	}
395
396
	/**
397
	 * @inheritdoc
398
	 */
399
	public function move(\OCP\Share\IShare $share, $recipient) {
400
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
401
			// Just update the target
402
			$qb = $this->dbConn->getQueryBuilder();
403
			$qb->update('share')
404
				->set('file_target', $qb->createNamedParameter($share->getTarget()))
405
				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
406
				->execute();
407
408
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
409
410
			// Check if there is a usergroup share
411
			$qb = $this->dbConn->getQueryBuilder();
412
			$stmt = $qb->select('id')
413
				->from('share')
414
				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
415
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
416
				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
417
				->andWhere($qb->expr()->orX(
418
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
419
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
420
				))
421
				->setMaxResults(1)
422
				->execute();
423
424
			$data = $stmt->fetch();
425
			$stmt->closeCursor();
426
427
			if ($data === false) {
428
				// No usergroup share yet. Create one.
429
				$qb = $this->dbConn->getQueryBuilder();
430
				$qb->insert('share')
431
					->values([
432
						'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
433
						'share_with' => $qb->createNamedParameter($recipient),
434
						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
435
						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
436
						'parent' => $qb->createNamedParameter($share->getId()),
437
						'item_type' => $qb->createNamedParameter($share->getNodeType()),
438
						'item_source' => $qb->createNamedParameter($share->getNodeId()),
439
						'file_source' => $qb->createNamedParameter($share->getNodeId()),
440
						'file_target' => $qb->createNamedParameter($share->getTarget()),
441
						'permissions' => $qb->createNamedParameter($share->getPermissions()),
442
						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
443
					])->execute();
444
			} else {
445
				// Already a usergroup share. Update it.
446
				$qb = $this->dbConn->getQueryBuilder();
447
				$qb->update('share')
448
					->set('file_target', $qb->createNamedParameter($share->getTarget()))
449
					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
450
					->execute();
451
			}
452
		}
453
454
		return $share;
455
	}
456
457
	/**
458
	 * @inheritdoc
459
	 */
460
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
461
		$qb = $this->dbConn->getQueryBuilder();
462
		$qb->select('*')
463
			->from('share')
464
			->andWhere($qb->expr()->orX(
465
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
466
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
467
			));
468
469
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
470
471
		/**
472
		 * Reshares for this user are shares where they are the owner.
473
		 */
474 View Code Duplication
		if ($reshares === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
475
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
476
		} else {
477
			$qb->andWhere(
478
				$qb->expr()->orX(
479
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
480
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
481
				)
482
			);
483
		}
484
485
		if ($node !== null) {
486
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
487
		}
488
489
		if ($limit !== -1) {
490
			$qb->setMaxResults($limit);
491
		}
492
493
		$qb->setFirstResult($offset);
494
		$qb->orderBy('id');
495
496
		$cursor = $qb->execute();
497
		$shares = [];
498
		while($data = $cursor->fetch()) {
499
			$shares[] = $this->createShare($data);
500
		}
501
		$cursor->closeCursor();
502
503
		return $shares;
504
	}
505
506
	/**
507
	 * @inheritdoc
508
	 */
509
	public function getShareById($id, $recipientId = null) {
510
		$qb = $this->dbConn->getQueryBuilder();
511
512
		$qb->select('*')
513
			->from('share')
514
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
515
			->andWhere(
516
				$qb->expr()->in(
517
					'share_type',
518
					$qb->createNamedParameter([
519
						\OCP\Share::SHARE_TYPE_USER,
520
						\OCP\Share::SHARE_TYPE_GROUP,
521
						\OCP\Share::SHARE_TYPE_LINK,
522
					], IQueryBuilder::PARAM_INT_ARRAY)
523
				)
524
			)
525
			->andWhere($qb->expr()->orX(
526
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
527
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
528
			));
529
		
530
		$cursor = $qb->execute();
531
		$data = $cursor->fetch();
532
		$cursor->closeCursor();
533
534
		if ($data === false) {
535
			throw new ShareNotFound();
536
		}
537
538
		try {
539
			$share = $this->createShare($data);
540
		} catch (InvalidShare $e) {
541
			throw new ShareNotFound();
542
		}
543
544
		// If the recipient is set for a group share resolve to that user
545
		if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
546
			$share = $this->resolveGroupShare($share, $recipientId);
547
		}
548
549
		return $share;
550
	}
551
552
	/**
553
	 * Get shares for a given path
554
	 *
555
	 * @param \OCP\Files\Node $path
556
	 * @return \OCP\Share\IShare[]
557
	 */
558
	public function getSharesByPath(Node $path) {
559
		$qb = $this->dbConn->getQueryBuilder();
560
561
		$cursor = $qb->select('*')
562
			->from('share')
563
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
564
			->andWhere(
565
				$qb->expr()->orX(
566
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
567
					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
568
				)
569
			)
570
			->andWhere($qb->expr()->orX(
571
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
572
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
573
			))
574
			->execute();
575
576
		$shares = [];
577
		while($data = $cursor->fetch()) {
578
			$shares[] = $this->createShare($data);
579
		}
580
		$cursor->closeCursor();
581
582
		return $shares;
583
	}
584
585
	/**
586
	 * @inheritdoc
587
	 */
588
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
589
		/** @var Share[] $shares */
590
		$shares = [];
591
592
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
593
			//Get shares directly with this user
594
			$qb = $this->dbConn->getQueryBuilder();
595
			$qb->select('*')
596
				->from('share');
597
598
			// Order by id
599
			$qb->orderBy('id');
600
601
			// Set limit and offset
602
			if ($limit !== -1) {
603
				$qb->setMaxResults($limit);
604
			}
605
			$qb->setFirstResult($offset);
606
607
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)))
608
				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
609
				->andWhere($qb->expr()->orX(
610
					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
611
					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
612
				));
613
614
			// Filter by node if provided
615
			if ($node !== null) {
616
				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
617
			}
618
619
			$cursor = $qb->execute();
620
621
			while($data = $cursor->fetch()) {
622
				$shares[] = $this->createShare($data);
623
			}
624
			$cursor->closeCursor();
625
626
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
627
			$user = $this->userManager->get($userId);
628
			$allGroups = $this->groupManager->getUserGroups($user);
629
630
			/** @var Share[] $shares2 */
631
			$shares2 = [];
632
633
			$start = 0;
634
			while(true) {
635
				$groups = array_slice($allGroups, $start, 100);
636
				$start += 100;
637
638
				if ($groups === []) {
639
					break;
640
				}
641
642
				$qb = $this->dbConn->getQueryBuilder();
643
				$qb->select('*')
644
					->from('share')
645
					->orderBy('id')
646
					->setFirstResult(0);
647
648
				if ($limit !== -1) {
649
					$qb->setMaxResults($limit - count($shares));
650
				}
651
652
				// Filter by node if provided
653
				if ($node !== null) {
654
					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
655
				}
656
657
				$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
658
659
				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
660
					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
661
						$groups,
662
						IQueryBuilder::PARAM_STR_ARRAY
663
					)))
664
					->andWhere($qb->expr()->orX(
665
						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
666
						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
667
					));
668
669
				$cursor = $qb->execute();
670
				while($data = $cursor->fetch()) {
671
					if ($offset > 0) {
672
						$offset--;
673
						continue;
674
					}
675
					$shares2[] = $this->createShare($data);
676
				}
677
				$cursor->closeCursor();
678
			}
679
680
			/*
681
 			 * Resolve all group shares to user specific shares
682
 			 * TODO: Optmize this!
683
 			 */
684
			foreach($shares2 as $share) {
685
				$shares[] = $this->resolveGroupShare($share, $userId);
686
			}
687
		} else {
688
			throw new BackendError('Invalid backend');
689
		}
690
691
692
		return $shares;
693
	}
694
695
	/**
696
	 * Get a share by token
697
	 *
698
	 * @param string $token
699
	 * @return \OCP\Share\IShare
700
	 * @throws ShareNotFound
701
	 */
702
	public function getShareByToken($token) {
703
		$qb = $this->dbConn->getQueryBuilder();
704
705
		$cursor = $qb->select('*')
706
			->from('share')
707
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)))
708
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
709
			->andWhere($qb->expr()->orX(
710
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
711
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
712
			))
713
			->execute();
714
715
		$data = $cursor->fetch();
716
717
		if ($data === false) {
718
			throw new ShareNotFound();
719
		}
720
721
		try {
722
			$share = $this->createShare($data);
723
		} catch (InvalidShare $e) {
724
			throw new ShareNotFound();
725
		}
726
727
		return $share;
728
	}
729
	
730
	/**
731
	 * Create a share object from an database row
732
	 *
733
	 * @param mixed[] $data
734
	 * @return \OCP\Share\IShare
735
	 * @throws InvalidShare
736
	 */
737
	private function createShare($data) {
738
		$share = new Share($this->rootFolder, $this->userManager);
739
		$share->setId((int)$data['id'])
740
			->setShareType((int)$data['share_type'])
741
			->setPermissions((int)$data['permissions'])
742
			->setTarget($data['file_target'])
743
			->setMailSend((bool)$data['mail_send']);
744
745
		$shareTime = new \DateTime();
746
		$shareTime->setTimestamp((int)$data['stime']);
747
		$share->setShareTime($shareTime);
748
749
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
750
			$share->setSharedWith($data['share_with']);
751
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
752
			$share->setSharedWith($data['share_with']);
753
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
754
			$share->setPassword($data['share_with']);
755
			$share->setToken($data['token']);
756
		}
757
758
		$share->setSharedBy($data['uid_initiator']);
759
		$share->setShareOwner($data['uid_owner']);
760
761
		$share->setNodeId((int)$data['file_source']);
762
		$share->setNodeType($data['item_type']);
763
764
		if ($data['expiration'] !== null) {
765
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
766
			$share->setExpirationDate($expiration);
0 ignored issues
show
Security Bug introduced by
It seems like $expiration defined by \DateTime::createFromFor...', $data['expiration']) on line 765 can also be of type false; however, OC\Share20\Share::setExpirationDate() does only seem to accept 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...
767
		}
768
769
		$share->setProviderId($this->identifier());
770
771
		return $share;
772
	}
773
774
	/**
775
	 * Resolve a group share to a user specific share
776
	 * Thus if the user moved their group share make sure this is properly reflected here.
777
	 *
778
	 * @param \OCP\Share\IShare $share
779
	 * @param string $userId
780
	 * @return Share Returns the updated share if one was found else return the original share.
781
	 */
782
	private function resolveGroupShare(\OCP\Share\IShare $share, $userId) {
783
		$qb = $this->dbConn->getQueryBuilder();
784
785
		$stmt = $qb->select('*')
786
			->from('share')
787
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
788
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
789
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
790
			->andWhere($qb->expr()->orX(
791
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
792
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
793
			))
794
			->setMaxResults(1)
795
			->execute();
796
797
		$data = $stmt->fetch();
798
		$stmt->closeCursor();
799
800
		if ($data !== false) {
801
			$share->setPermissions((int)$data['permissions']);
802
			$share->setTarget($data['file_target']);
803
		}
804
805
		return $share;
806
	}
807
808
	/**
809
	 * A user is deleted from the system
810
	 * So clean up the relevant shares.
811
	 *
812
	 * @param string $uid
813
	 * @param int $shareType
814
	 */
815
	public function userDeleted($uid, $shareType) {
816
		$qb = $this->dbConn->getQueryBuilder();
817
818
		$qb->delete('share');
819
820
		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
821
			/*
822
			 * Delete all user shares that are owned by this user
823
			 * or that are received by this user
824
			 */
825
826
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
827
828
			$qb->andWhere(
829
				$qb->expr()->orX(
830
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
831
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
832
				)
833
			);
834
		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
835
			/*
836
			 * Delete all group shares that are owned by this user
837
			 * Or special user group shares that are received by this user
838
			 */
839
			$qb->where(
840
				$qb->expr()->andX(
841
					$qb->expr()->orX(
842
						$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
843
						$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
844
					),
845
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
846
				)
847
			);
848
849
			$qb->orWhere(
850
				$qb->expr()->andX(
851
					$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
852
					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
853
				)
854
			);
855 View Code Duplication
		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
856
			/*
857
			 * Delete all link shares owned by this user.
858
			 * And all link shares initiated by this user (until #22327 is in)
859
			 */
860
			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
861
862
			$qb->andWhere(
863
				$qb->expr()->orX(
864
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
865
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
866
				)
867
			);
868
		}
869
870
		$qb->execute();
871
	}
872
873
	/**
874
	 * Delete all shares received by this group. As well as any custom group
875
	 * shares for group members.
876
	 *
877
	 * @param string $gid
878
	 */
879
	public function groupDeleted($gid) {
880
		/*
881
		 * First delete all custom group shares for group members
882
		 */
883
		$qb = $this->dbConn->getQueryBuilder();
884
		$qb->select('id')
885
			->from('share')
886
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
887
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
888
889
		$cursor = $qb->execute();
890
		$ids = [];
891
		while($row = $cursor->fetch()) {
892
			$ids[] = (int)$row['id'];
893
		}
894
		$cursor->closeCursor();
895
896
		if (!empty($ids)) {
897
			$chunks = array_chunk($ids, 100);
898
			foreach ($chunks as $chunk) {
899
				$qb->delete('share')
900
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
901
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
902
				$qb->execute();
903
			}
904
		}
905
906
		/*
907
		 * Now delete all the group shares
908
		 */
909
		$qb = $this->dbConn->getQueryBuilder();
910
		$qb->delete('share')
911
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
912
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
913
		$qb->execute();
914
	}
915
916
	/**
917
	 * Delete custom group shares to this group for this user
918
	 *
919
	 * @param string $uid
920
	 * @param string $gid
921
	 */
922
	public function userDeletedFromGroup($uid, $gid) {
923
		/*
924
		 * Get all group shares
925
		 */
926
		$qb = $this->dbConn->getQueryBuilder();
927
		$qb->select('id')
928
			->from('share')
929
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)))
930
			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
931
932
		$cursor = $qb->execute();
933
		$ids = [];
934
		while($row = $cursor->fetch()) {
935
			$ids[] = (int)$row['id'];
936
		}
937
		$cursor->closeCursor();
938
939 View Code Duplication
		if (!empty($ids)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
940
			$chunks = array_chunk($ids, 100);
941
			foreach ($chunks as $chunk) {
942
				/*
943
				 * Delete all special shares wit this users for the found group shares
944
				 */
945
				$qb->delete('share')
946
					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
947
					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
948
					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
949
				$qb->execute();
950
			}
951
		}
952
	}
953
}
954