Completed
Push — master ( 4f3689...17b456 )
by Maxence
02:12 queued 11s
created

ShareByCircleProvider::shareObjectToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @author Vinicius Cubas Brand <[email protected]>
10
 * @author Daniel Tygel <[email protected]>
11
 *
12
 * @copyright 2017
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\Circles;
32
33
34
use Exception;
35
use OC;
36
use OC\Files\Cache\Cache;
37
use OC\Share20\Exception\InvalidShare;
38
use OC\Share20\Share;
39
use OC\User\NoUserException;
40
use OCA\Circles\Api\v1\Circles;
41
use OCA\Circles\AppInfo\Application;
42
use OCA\Circles\Db\CircleProviderRequest;
43
use OCA\Circles\Db\CirclesRequest;
44
use OCA\Circles\Db\MembersRequest;
45
use OCA\Circles\Db\TokensRequest;
46
use OCA\Circles\Model\Circle;
47
use OCA\Circles\Model\Member;
48
use OCA\Circles\Service\CirclesService;
49
use OCA\Circles\Service\ConfigService;
50
use OCA\Circles\Service\MembersService;
51
use OCA\Circles\Service\MiscService;
52
use OCA\Circles\Service\TimezoneService;
53
use OCP\AppFramework\QueryException;
54
use OCP\DB\QueryBuilder\IQueryBuilder;
55
use OCP\Files\Folder;
56
use OCP\Files\IRootFolder;
57
use OCP\Files\Node;
58
use OCP\Files\NotFoundException;
59
use OCP\IDBConnection;
60
use OCP\IL10N;
61
use OCP\ILogger;
62
use OCP\IURLGenerator;
63
use OCP\IUserManager;
64
use OCP\Security\ISecureRandom;
65
use OCP\Share\Exceptions\IllegalIDChangeException;
66
use OCP\Share\Exceptions\ShareNotFound;
67
use OCP\Share\IShare;
68
use OCP\Share\IShareProvider;
69
70
71
class ShareByCircleProvider extends CircleProviderRequest implements IShareProvider {
72
73
74
	/** @var ILogger */
75
	private $logger;
76
77
	/** @var ISecureRandom */
78
	private $secureRandom;
79
80
	/** @var IUserManager */
81
	private $userManager;
82
83
	/** @var IRootFolder */
84
	private $rootFolder;
85
86
	/** @var IURLGenerator */
87
	private $urlGenerator;
88
89
	/** @var MembersService */
90
	private $membersService;
91
92
	/** @var CirclesRequest */
93
	private $circlesRequest;
94
95
	/** @var MembersRequest */
96
	private $membersRequest;
97
98
	/** @var TokensRequest */
99
	private $tokensRequest;
100
101
	/**
102
	 * DefaultShareProvider constructor.
103
	 *
104
	 * @param IDBConnection $connection
105
	 * @param ISecureRandom $secureRandom
106
	 * @param IUserManager $userManager
107
	 * @param IRootFolder $rootFolder
108
	 * @param IL10N $l10n
109
	 * @param ILogger $logger
110
	 * @param IURLGenerator $urlGenerator
111
	 *
112
	 * @throws QueryException
113
	 */
114
	public function __construct(
115
		IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager,
116
		IRootFolder $rootFolder, IL10N $l10n, ILogger $logger, IURLGenerator $urlGenerator
117
	) {
118
		$app = new Application();
119
		$container = $app->getContainer();
120
		$configService = $container->query(ConfigService::class);
121
		$miscService = $container->query(MiscService::class);
122
		$timezoneService = $container->query(TimezoneService::class);
123
124
		parent::__construct($l10n, $connection, $configService, $timezoneService, $miscService);
125
126
		$this->secureRandom = $secureRandom;
127
		$this->userManager = $userManager;
128
		$this->rootFolder = $rootFolder;
129
		$this->logger = $logger;
130
		$this->urlGenerator = $urlGenerator;
131
		$this->membersService = $container->query(MembersService::class);
132
		$this->circlesRequest = $container->query(CirclesRequest::class);
133
		$this->membersRequest = $container->query(MembersRequest::class);
134
		$this->tokensRequest = $container->query(TokensRequest::class);
135
	}
136
137
138
	/**
139
	 * Return the identifier of this provider.
140
	 *
141
	 * @return string
142
	 */
143
	public function identifier() {
144
		return 'ocCircleShare';
145
	}
146
147
148
	/**
149
	 * Create a share if it does not exist already.
150
	 *
151
	 * @param IShare $share
152
	 *
153
	 * @return IShare The share object
154
	 * @throws Exception
155
	 */
156
	public function create(IShare $share) {
157
		try {
158
			$nodeId = $share->getNode()
159
							->getId();
160
161
			$qb = $this->findShareParentSql($nodeId, $share->getSharedWith());
162
			$exists = $qb->execute();
163
			$data = $exists->fetch();
164
			$exists->closeCursor();
165
166
			if ($data !== false) {
167
				throw $this->errorShareAlreadyExist($share);
168
			}
169
170
			$share->setToken($this->token(15));
171
//			if ($this->configService->enforcePasswordProtection()) {
172
//				$share->setPassword($this->miscService->uuid(15));
173
//			}
174
175
			$this->createShare($share);
176
177
			$circle =
178
				$this->circlesRequest->getCircle($share->getSharedWith(), $share->getSharedby());
179
			$circle->getHigherViewer()
180
				   ->hasToBeMember();
181
182
			Circles::shareToCircle(
183
				$circle->getUniqueId(), 'files', '',
184
				['id' => $share->getId(), 'share' => $this->shareObjectToArray($share)],
185
				'\OCA\Circles\Circles\FileSharingBroadcaster'
186
			);
187
188
			return $this->getShareById($share->getId());
189
		} catch (Exception $e) {
190
			throw $e;
191
		}
192
	}
193
194
195
	/**
196
	 * Update a share
197
	 * permissions right, owner and initiator
198
	 *
199
	 * @param IShare $share
200
	 *
201
	 * @return IShare The share object
202
	 */
203
	public function update(IShare $share) {
204
205
		$qb = $this->getBaseUpdateSql();
206
		$this->limitToShare($qb, $share->getId());
207
		$qb->set('permissions', $qb->createNamedParameter($share->getPermissions()))
208
		   ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
209
		   ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
210
		$qb->execute();
211
212
		return $share;
213
	}
214
215
216
	/**
217
	 * Delete a share, and it's children
218
	 *
219
	 * @param IShare $share
220
	 */
221
	public function delete(IShare $share) {
222
		$qb = $this->getBaseDeleteSql();
223
		$this->limitToShareAndChildren($qb, $share->getId());
224
		$qb->execute();
225
226
		$this->tokensRequest->removeTokenByShareId($share->getId());
227
	}
228
229
230
	/**
231
	 * Unshare a file from self as recipient.
232
	 * Because every circles share are group shares, we will set permissions to 0
233
	 *
234
	 * @param IShare $share
235
	 * @param string $userId
236
	 */
237 View Code Duplication
	public function deleteFromSelf(IShare $share, $userId) {
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...
238
		$childId = $this->getShareChildId($share, $userId);
239
240
		$qb = $this->getBaseUpdateSql();
241
		$qb->set('permissions', $qb->createNamedParameter(0));
242
		$this->limitToShare($qb, $childId);
243
244
		$qb->execute();
245
	}
246
247
248
	/**
249
	 * Move a share as a recipient.
250
	 *
251
	 * @param IShare $share
252
	 * @param string $userId
253
	 *
254
	 * @return IShare
255
	 *
256
	 */
257 View Code Duplication
	public function move(IShare $share, $userId) {
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...
258
259
		$childId = $this->getShareChildId($share, $userId);
260
261
		$qb = $this->getBaseUpdateSql();
262
		$qb->set('file_target', $qb->createNamedParameter($share->getTarget()));
263
		$this->limitToShare($qb, $childId);
264
		$qb->execute();
265
266
		return $share;
267
	}
268
269
270
	/**
271
	 * return the child ID of a share
272
	 *
273
	 * @param IShare $share
274
	 * @param string $userId
275
	 *
276
	 * @return bool
277
	 */
278
	private function getShareChildId(IShare $share, $userId) {
279
		$qb = $this->getBaseSelectSql($share->getId());
280
		$this->limitToShareChildren($qb, $userId, $share->getId());
281
282
		$child = $qb->execute();
283
		$data = $child->fetch();
284
		$child->closeCursor();
285
286
		if ($data === false) {
287
			return $this->createShareChild($userId, $share);
288
		}
289
290
		return $data['id'];
291
	}
292
293
294
	/**
295
	 * Create a child and returns its ID
296
	 *
297
	 * @param IShare $share
298
	 *
299
	 * @throws NotFoundException
300
	 */
301
	private function createShare($share) {
302
		$this->miscService->log(
303
			'Creating share (1/4) - type: ' . $share->getShareType() . ' - token: '
304
			. $share->getToken() . ' - type: ' . $share->getShareType() . ' - with: '
305
			. $share->getSharedWith() . ' - permissions: ' . $share->getPermissions(), 0
306
		);
307
308
		$qb = $this->getBaseInsertSql($share);
309
		$this->miscService->log('Share creation (2/4) : ' . json_encode($qb->getSQL()), 0);
310
311
		$result = $qb->execute();
312
		$this->miscService->log('Share creation result (3/4) : ' . json_encode($result), 0);
313
314
		$id = $qb->getLastInsertId();
315
		$this->miscService->log('Share created ID (4/4) : ' . $id, 0);
316
317
		try {
318
			$share->setId($id);
319
		} catch (IllegalIDChangeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class OCP\Share\Exceptions\IllegalIDChangeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
320
		}
321
	}
322
323
324
	/**
325
	 * Create a child and returns its ID
326
	 *
327
	 * @param string $userId
328
	 * @param IShare $share
329
	 *
330
	 * @return int
331
	 */
332
	private function createShareChild($userId, $share) {
333
		$qb = $this->getBaseInsertSql($share);
334
335
		$qb->setValue('parent', $qb->createNamedParameter($share->getId()));
336
		$qb->setValue('share_with', $qb->createNamedParameter($userId));
337
		$qb->execute();
338
		$id = $qb->getLastInsertId();
339
340
		return (int)$id;
341
	}
342
343
344
	/**
345
	 * Get all shares by the given user in a folder
346
	 *
347
	 * @param string $userId
348
	 * @param Folder $node
349
	 * @param bool $reshares Also get the shares where $user is the owner instead of just the
350
	 *     shares where $user is the initiator
351
	 *
352
	 * @return Share[]
353
	 */
354
	public function getSharesInFolder($userId, Folder $node, $reshares) {
355
		$qb = $this->getBaseSelectSql();
356
		$this->limitToShareOwner($qb, $userId, true);
357
		$cursor = $qb->execute();
358
359
		$shares = [];
360
		while ($data = $cursor->fetch()) {
361
			$shares[$data['file_source']][] = $this->createShareObject($data);
362
		}
363
		$cursor->closeCursor();
364
365
		return $shares;
366
	}
367
368
369
	/**
370
	 * Get all shares by the given user
371
	 *
372
	 * @param string $userId
373
	 * @param int $shareType
374
	 * @param Node|null $node
375
	 * @param bool $reShares
376
	 * @param int $limit The maximum number of shares to be returned, -1 for all shares
377
	 * @param int $offset
378
	 *
379
	 * @return Share[]
380
	 */
381
	public function getSharesBy($userId, $shareType, $node, $reShares, $limit, $offset) {
382
		$qb = $this->getBaseSelectSql();
383
384
		if ($node === null) {
385
			$this->limitToShareOwner($qb, $userId, $reShares);
386
		} else {
387
			$this->limitToFiles($qb, $node->getId());
388
		}
389
390
		$this->limitToPage($qb, $limit, $offset);
391
		$cursor = $qb->execute();
392
393
		$shares = [];
394
		while ($data = $cursor->fetch()) {
395
			$shares[] = $this->createShareObject($this->editShareEntry($data));
396
		}
397
		$cursor->closeCursor();
398
399
		return $shares;
400
	}
401
402
403
	/**
404
	 * returns a better formatted string to display more information about
405
	 * the circle to the Sharing UI
406
	 *
407
	 * @param $data
408
	 *
409
	 * @return array<string,string>
410
	 * @throws NoUserException
411
	 */
412
	private function editShareEntry($data) {
413
		$data['share_with'] =
414
			sprintf(
415
				'%s (%s, %s) [%s]', $data['circle_name'],
416
				Circle::TypeLongString($data['circle_type']),
417
				$this->miscService->getDisplayName($data['circle_owner'], true), $data['share_with']
418
			);
419
420
421
		return $data;
422
	}
423
424
425
	/**
426
	 * Get share by its id
427
	 *
428
	 * @param int $shareId
429
	 * @param string|null $recipientId
430
	 *
431
	 * @return Share
0 ignored issues
show
Documentation introduced by
Should the return type not be IShare?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
432
	 * @throws ShareNotFound
433
	 */
434 View Code Duplication
	public function getShareById($shareId, $recipientId = null) {
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...
435
		$qb = $this->getBaseSelectSql();
436
437
		$this->limitToShare($qb, $shareId);
438
439
		$cursor = $qb->execute();
440
		$data = $cursor->fetch();
441
		$cursor->closeCursor();
442
443
		if ($data === false) {
444
			throw new ShareNotFound();
445
		}
446
447
		return $this->createShareObject($data);
448
	}
449
450
451
	/**
452
	 * Get shares for a given path
453
	 *
454
	 * @param Node $path
455
	 *
456
	 * @return IShare[]|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
457
	 */
458
	public function getSharesByPath(Node $path) {
459
		$qb = $this->getBaseSelectSql();
460
		$this->limitToFiles($qb, [$path->getId()]);
461
462
		$cursor = $qb->execute();
463
464
		$shares = [];
465
		while ($data = $cursor->fetch()) {
466
			$shares[] = $this->createShareObject($data);
467
		}
468
		$cursor->closeCursor();
469
470
		return $shares;
471
	}
472
473
474
	/**
475
	 * Get shared with the given user
476
	 *
477
	 * @param string $userId get shares where this user is the recipient
478
	 * @param int $shareType
479
	 * @param Node|null $node
480
	 * @param int $limit The max number of entries returned, -1 for all
481
	 * @param int $offset
482
	 *
483
	 * @return IShare[]
484
	 */
485
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
486
487
		$shares = $this->getSharedWithCircleMembers($userId, $shareType, $node, $limit, $offset);
488
489
		return $shares;
490
	}
491
492
493
	/**
494
	 * @param string $userId
495
	 * @param $shareType
496
	 * @param Node $node
497
	 * @param int $limit
498
	 * @param int $offset
499
	 *
500
	 * @return IShare[]
501
	 */
502
	private function getSharedWithCircleMembers($userId, $shareType, $node, $limit, $offset) {
503
504
		$qb = $this->getCompleteSelectSql();
505
		$this->linkToFileCache($qb, $userId);
506
		$this->limitToPage($qb, $limit, $offset);
507
508
		if ($node !== null) {
509
			$this->limitToFiles($qb, [$node->getId()]);
510
		}
511
512
		$this->linkToMember($qb, $userId, $this->configService->isLinkedGroupsAllowed());
513
514
		$cursor = $qb->execute();
515
516
		$shares = [];
517
		while ($data = $cursor->fetch()) {
518
			self::editShareFromParentEntry($data);
519
			if (self::isAccessibleResult($data)) {
520
				$shares[] = $this->createShareObject($data);
521
			}
522
		}
523
		$cursor->closeCursor();
524
525
		return $shares;
526
	}
527
528
529
	/**
530
	 * Get a share by token
531
	 *
532
	 * @param string $token
533
	 *
534
	 * @return IShare
535
	 * @throws ShareNotFound
536
	 * @deprecated - use local querybuilder lib instead
537
	 */
538
	public function getShareByToken($token) {
539
		$qb = $this->dbConnection->getQueryBuilder();
540
541
		$this->miscService->log("Opening share by token '#" . $token . "'", 0);
542
543
		$cursor = $qb->select('*')
544
					 ->from('share')
545
					 ->where(
546
						 $qb->expr()
547
							->eq(
548
								'share_type',
549
								$qb->createNamedParameter(\OCP\Share::SHARE_TYPE_CIRCLE)
550
							)
551
					 )
552
					 ->andWhere(
553
						 $qb->expr()
554
							->eq('token', $qb->createNamedParameter($token))
555
					 )
556
					 ->execute();
557
558
		$data = $cursor->fetch();
559
560
		if ($data === false) {
561
			$this->miscService->log('data is false - checking personal token', 0);
562
			try {
563
				$data = $this->getShareByPersonalToken($token);
564
			} catch (Exception $e) {
565
				$this->miscService->log("Share '#" . $token . "' not found.", 0);
566
				throw new ShareNotFound('Share not found', $this->l10n->t('Could not find share'));
567
			}
568
		}
569
570
		try {
571
			$share = $this->createShareObject($data);
572
		} catch (InvalidShare $e) {
0 ignored issues
show
Bug introduced by
The class OC\Share20\Exception\InvalidShare does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
573
			$this->miscService->log(
574
				"Share Object '#" . $token . "' not created. " . json_encode($data), 0
575
			);
576
			throw new ShareNotFound('Share not found', $this->l10n->t('Could not find share'));
577
		}
578
579
		$share->setStatus(Ishare::STATUS_ACCEPTED);
580
581
		return $share;
582
	}
583
584
585
	/**
586
	 * @param string $token
587
	 *
588
	 * @return array
589
	 * @throws ShareNotFound
590
	 */
591
	private function getShareByPersonalToken($token) {
592
		$qb = $this->dbConnection->getQueryBuilder();
593
594
		$qb = $qb->select('s.*')
595
				 ->selectAlias('ct.password', 'personal_password')
596
				 ->selectAlias('ct.circle_id', 'personal_circle_id')
597
				 ->selectAlias('ct.user_id', 'personal_user_id')
598
				 ->selectAlias('ct.member_id', 'personal_member_id')
599
				 ->from('share', 's')
600
				 ->from('circles_tokens', 'ct')
601
				 ->where(
602
					 $qb->expr()
603
						->eq(
604
							's.share_type',
605
							$qb->createNamedParameter(\OCP\Share::SHARE_TYPE_CIRCLE)
606
						)
607
				 )
608
				 ->andWhere(
609
					 $qb->expr()
610
						->eq('ct.token', $qb->createNamedParameter($token))
611
				 )
612
				 ->andWhere(
613
					 $qb->expr()
614
						->eq('ct.share_id', 's.id')
615
				 );
616
		$cursor = $qb->execute();
617
618
		$data = $cursor->fetch();
619
		if ($data === false) {
620
			throw new ShareNotFound('personal check not found');
621
		}
622
623
		\OC::$server->getLogger()->log(3, '#### ' . json_encode($data));
624
		$member = null;
625
		try {
626
			$member = $this->membersService->getMemberById($data['personal_member_id']);
627
			if (!$member->isLevel(Member::LEVEL_MEMBER)) {
628
				throw new Exception();
629
			}
630
		} catch (Exception $e) {
631
			throw new ShareNotFound('invalid token');
632
		}
633
634
		return $data;
635
	}
636
637
638
	/**
639
	 * We don't return a thing about children.
640
	 * The call to this function is deprecated and should be removed in next release of NC.
641
	 * Also, we get the children in the delete() method.
642
	 *
643
	 * @param IShare $parent
644
	 *
645
	 * @return array
646
	 */
647
	public function getChildren(IShare $parent) {
648
		return [];
649
	}
650
651
652
	/**
653
	 * A user is deleted from the system
654
	 * So clean up the relevant shares.
655
	 *
656
	 * @param string $uid
657
	 * @param int $shareType
658
	 */
659
	public function userDeleted($uid, $shareType) {
660
		// TODO: Implement userDeleted() method.
661
	}
662
663
664
	/**
665
	 * A group is deleted from the system.
666
	 * We handle our own groups.
667
	 *
668
	 * @param string $gid
669
	 */
670
	public function groupDeleted($gid) {
671
		return;
672
	}
673
674
675
	/**
676
	 * A user is deleted from a group.
677
	 * We handle our own groups.
678
	 *
679
	 * @param string $uid
680
	 * @param string $gid
681
	 */
682
	public function userDeletedFromGroup($uid, $gid) {
683
		return;
684
	}
685
686
687
	/**
688
	 * Create a share object
689
	 *
690
	 * @param array $data
691
	 *
692
	 * @return IShare
693
	 */
694
	private function createShareObject($data) {
695
		$share = new Share($this->rootFolder, $this->userManager);
696
		$share->setId((int)$data['id'])
697
			  ->setPermissions((int)$data['permissions'])
698
			  ->setNodeType($data['item_type']);
699
700
		if (($password = $this->get('personal_password', $data, '')) !== '') {
701
			$share->setPassword($this->get('personal_password', $data, ''));
702
		} else if (($password = $this->get('password', $data, '')) !== '') {
703
			$share->setPassword($this->get('password', $data, ''));
704
		}
705
706
		$share->setNodeId((int)$data['file_source'])
707
			  ->setTarget($data['file_target']);
708
709
		$this->assignShareObjectSharesProperties($share, $data);
710
		$this->assignShareObjectPropertiesFromParent($share, $data);
711
712
		$share->setProviderId($this->identifier());
713
		$share->setStatus(Ishare::STATUS_ACCEPTED);
714
715
		return $share;
716
	}
717
718
719
	/**
720
	 * @param IShare $share
721
	 * @param $data
722
	 */
723
	private function assignShareObjectPropertiesFromParent(IShare &$share, $data) {
724
		if (isset($data['f_permissions'])) {
725
			$entryData = $data;
726
			$entryData['permissions'] = $entryData['f_permissions'];
727
			$entryData['parent'] = $entryData['f_parent'];
728
			$share->setNodeCacheEntry(
729
				Cache::cacheEntryFromData(
730
					$entryData,
731
					OC::$server->getMimeTypeLoader()
732
				)
733
			);
734
		}
735
	}
736
737
738
	/**
739
	 * @param IShare $share
740
	 * @param $data
741
	 */
742
	private function assignShareObjectSharesProperties(IShare &$share, $data) {
743
		$shareTime = new \DateTime();
744
		$shareTime->setTimestamp((int)$data['stime']);
745
746
		$share->setShareTime($shareTime);
747
		$share->setSharedWith($data['share_with'])
748
			  ->setSharedBy($data['uid_initiator'])
749
			  ->setShareOwner($data['uid_owner'])
750
			  ->setShareType((int)$data['share_type']);
751
752
		if (array_key_exists('circle_type', $data)
753
			&& method_exists($share, 'setSharedWithDisplayName')) {
754
			$share->setSharedWithAvatar(CirclesService::getCircleIcon($data['circle_type']))
755
				  ->setSharedWithDisplayName(
756
					  sprintf(
757
						  '%s (%s, %s)', $data['circle_name'],
758
						  Circle::TypeLongString($data['circle_type']),
759
						  $this->miscService->getDisplayName($data['circle_owner'], true)
760
					  )
761
				  );
762
		}
763
	}
764
765
766
	/**
767
	 * @param IShare $share
768
	 *
769
	 * @return Exception
770
	 */
771
	private function errorShareAlreadyExist($share) {
772
		$share_src = $share->getNode()
773
						   ->getName();
774
775
		$message = 'Sharing %s failed, this item is already shared with this circle';
776
		$message_t = $this->l10n->t($message, array($share_src));
777
		$this->logger->debug(
778
			sprintf($message, $share_src, $share->getSharedWith()), ['app' => 'circles']
779
		);
780
781
		return new Exception($message_t);
782
	}
783
784
785
	/**
786
	 * Get the access list to the array of provided nodes.
787
	 *
788
	 * @param Node[] $nodes The list of nodes to get access for
789
	 * @param bool $currentAccess If current access is required (like for removed shares that might
790
	 *     get revived later)
791
	 *
792
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
793
	 * @see IManager::getAccessList() for sample docs
794
	 *
795
	 * @since 12
796
	 */
797
	public function getAccessList($nodes, $currentAccess) {
798
		$ids = [];
799
		foreach ($nodes as $node) {
800
			$ids[] = $node->getId();
801
		}
802
803
		$qb = $this->getAccessListBaseSelectSql();
804
		$this->limitToFiles($qb, $ids);
805
806
		$users = $this->parseAccessListResult($qb);
807
808
		if ($currentAccess === false) {
809
			$users = array_keys($users);
810
		}
811
812
		return ['users' => $users];
813
	}
814
815
816
	/**
817
	 * Restore a share for a given recipient. The implementation could be provider independant.
818
	 *
819
	 * @param IShare $share
820
	 * @param string $recipient
821
	 *
822
	 * @return IShare The restored share object
823
	 *
824
	 * @since 14.0.0
825
	 */
826
	public function restore(IShare $share, string $recipient): IShare {
827
		return $share;
828
	}
829
830
831
	/**
832
	 * return array regarding getAccessList format.
833
	 * ie. \OC\Share20\Manager::getAccessList()
834
	 *
835
	 * @param IQueryBuilder $qb
836
	 *
837
	 * @return array
838
	 */
839
	private function parseAccessListResult(IQueryBuilder $qb) {
840
841
		$cursor = $qb->execute();
842
		$users = [];
843
844
		while ($row = $cursor->fetch()) {
845
			$userId = $row['user_id'];
846
847
			if (!key_exists($userId, $users)) {
848
				$users[$userId] = [
849
					'node_id'   => $row['file_source'],
850
					'node_path' => $row['file_target']
851
				];
852
			}
853
		}
854
		$cursor->closeCursor();
855
856
		return $users;
857
	}
858
859
860
	/**
861
	 * @param IShare $share
862
	 *
863
	 * @return array
864
	 * @throws NotFoundException
865
	 */
866
	private function shareObjectToArray(IShare $share) {
867
		return [
868
			'id'          => $share->getId(),
869
			'sharedWith'  => $share->getSharedWith(),
870
			'sharedBy'    => $share->getSharedBy(),
871
			'nodeId'      => $share->getNodeId(),
872
			'shareOwner'  => $share->getShareOwner(),
873
			'permissions' => $share->getPermissions(),
874
			'token'       => $share->getToken(),
875
			'password'    => ($share->getPassword() === null) ? '' : $share->getPassword()
876
		];
877
	}
878
879
880
	/**
881
	 * @param string $k
882
	 * @param array $arr
883
	 * @param string $default
884
	 *
885
	 * @return string
886
	 */
887
	protected function get($k, array $arr, string $default = ''): string {
888
		if ($arr === null) {
889
			return $default;
890
		}
891
892
		if (!array_key_exists($k, $arr)) {
893
			$subs = explode('.', $k, 2);
894
			if (sizeof($subs) > 1) {
895
				if (!array_key_exists($subs[0], $arr)) {
896
					return $default;
897
				}
898
899
				$r = $arr[$subs[0]];
900
				if (!is_array($r)) {
901
					return $default;
902
				}
903
904
				return $this->get($subs[1], $r, $default);
905
			} else {
906
				return $default;
907
			}
908
		}
909
910
		if ($arr[$k] === null || (!is_string($arr[$k]) && (!is_int($arr[$k])))) {
911
			return $default;
912
		}
913
914
		return (string)$arr[$k];
915
	}
916
917
918
	/**
919
	 * @inheritDoc
920
	 */
921
	public function getAllShares(): iterable {
922
		$qb = $this->dbConnection->getQueryBuilder();
923
924
		$qb->select('*')
925
		   ->from('share')
926
		   ->where(
927
			   $qb->expr()
928
				  ->orX(
929
					  $qb->expr()
930
						 ->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))
931
				  )
932
		   );
933
934
		$cursor = $qb->execute();
935
		while ($data = $cursor->fetch()) {
936
			$share = $this->createShareObject($data);
937
938
			yield $share;
939
		}
940
		$cursor->closeCursor();
941
	}
942
943
944
	/**
945
	 * @param int $length
946
	 *
947
	 * @return string
948
	 */
949 View Code Duplication
	protected function token(int $length = 15): string {
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...
950
		$chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
951
952
		$str = '';
953
		$max = strlen($chars);
954
		for ($i = 0; $i < $length; $i++) {
955
			try {
956
				$str .= $chars[random_int(0, $max - 1)];
957
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
958
			}
959
		}
960
961
		return $str;
962
	}
963
964
965
}
966