Completed
Pull Request — master (#551)
by Maxence
02:37
created

ShareByCircleProviderDeprecated::get()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.0555
c 0
b 0
f 0
cc 9
nc 7
nop 3
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 daita\MySmallPhpTools\Model\SimpleDataStore;
35
use Exception;
36
use OC;
37
use OC\Files\Cache\Cache;
38
use OC\Share20\Exception\InvalidShare;
39
use OC\Share20\Share;
40
use OC\User\NoUserException;
41
use OCA\Circles\Api\v1\Circles;
42
use OCA\Circles\AppInfo\Application;
43
use OCA\Circles\Db\CircleProviderRequest;
44
use OCA\Circles\Db\DeprecatedCirclesRequest;
45
use OCA\Circles\Db\DeprecatedMembersRequest;
46
use OCA\Circles\Db\TokensRequest;
47
use OCA\Circles\Exceptions\CircleDoesNotExistException;
48
use OCA\Circles\Model\DeprecatedCircle;
49
use OCA\Circles\Model\GlobalScale\GSEvent;
50
use OCA\Circles\Model\DeprecatedMember;
51
use OCA\Circles\Service\CirclesService;
52
use OCA\Circles\Service\ConfigService;
53
use OCA\Circles\Service\GSUpstreamService;
54
use OCA\Circles\Service\MembersService;
55
use OCA\Circles\Service\MiscService;
56
use OCA\Circles\Service\TimezoneService;
57
use OCP\AppFramework\QueryException;
58
use OCP\DB\QueryBuilder\IQueryBuilder;
59
use OCP\Files\Folder;
60
use OCP\Files\InvalidPathException;
61
use OCP\Files\IRootFolder;
62
use OCP\Files\Node;
63
use OCP\Files\NotFoundException;
64
use OCP\IDBConnection;
65
use OCP\IL10N;
66
use OCP\ILogger;
67
use OCP\IURLGenerator;
68
use OCP\IUserManager;
69
use OCP\Security\ISecureRandom;
70
use OCP\Share\Exceptions\IllegalIDChangeException;
71
use OCP\Share\Exceptions\ShareNotFound;
72
use OCP\Share\IShare;
73
use OCP\Share\IShareProvider;
74
75
76
/**
77
 * Class ShareByCircleProvider
78
 *
79
 * @package OCA\Circles
80
 */
81
class ShareByCircleProviderDeprecated extends CircleProviderRequest implements IShareProvider {
82
83
84
	/** @var ILogger */
85
	private $logger;
86
87
	/** @var ISecureRandom */
88
	private $secureRandom;
89
90
	/** @var IUserManager */
91
	private $userManager;
92
93
	/** @var IRootFolder */
94
	private $rootFolder;
95
96
	/** @var IURLGenerator */
97
	private $urlGenerator;
98
99
	/** @var MembersService */
100
	private $membersService;
101
102
	/** @var DeprecatedCirclesRequest */
103
	private $circlesRequest;
104
105
	/** @var DeprecatedMembersRequest */
106
	private $membersRequest;
107
108
	/** @var TokensRequest */
109
	private $tokensRequest;
110
111
	/** @var GSUpstreamService */
112
	private $gsUpstreamService;
113
114
115
	/**
116
	 * ShareByCircleProvider constructor.
117
	 *
118
	 * @param IDBConnection $connection
119
	 * @param ISecureRandom $secureRandom
120
	 * @param IUserManager $userManager
121
	 * @param IRootFolder $rootFolder
122
	 * @param IL10N $l10n
123
	 * @param ILogger $logger
124
	 * @param IURLGenerator $urlGenerator
125
	 *
126
	 * @throws QueryException
127
	 */
128
	public function __construct(
129
		IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager,
130
		IRootFolder $rootFolder, IL10N $l10n, ILogger $logger, IURLGenerator $urlGenerator
131
	) {
132
		$app = \OC::$server->query(Application::class);
133
		$container = $app->getContainer();
134
		$configService = $container->query(ConfigService::class);
135
		$miscService = $container->query(MiscService::class);
136
		$timezoneService = $container->query(TimezoneService::class);
137
138
		parent::__construct($l10n, $connection, $configService, $timezoneService, $miscService);
139
140
		$this->secureRandom = $secureRandom;
141
		$this->userManager = $userManager;
142
		$this->rootFolder = $rootFolder;
143
		$this->logger = $logger;
144
		$this->urlGenerator = $urlGenerator;
145
		$this->membersService = $container->query(MembersService::class);
146
		$this->circlesRequest = $container->query(DeprecatedCirclesRequest::class);
147
		$this->membersRequest = $container->query(DeprecatedMembersRequest::class);
148
		$this->gsUpstreamService = $container->query(GSUpstreamService::class);
149
		$this->tokensRequest = $container->query(TokensRequest::class);
150
	}
151
152
153
	/**
154
	 * Return the identifier of this provider.
155
	 *
156
	 * @return string
157
	 */
158
	public function identifier() {
159
		return 'ocCircleShare';
160
	}
161
162
163
	/**
164
	 * Create a share if it does not exist already.
165
	 *
166
	 * @param IShare $share
167
	 *
168
	 * @return IShare The share object
169
	 * @throws Exception
170
	 */
171
	public function create(IShare $share) {
172
		try {
173
			$nodeId = $share->getNode()
174
							->getId();
175
176
			$qb = $this->findShareParentSql($nodeId, $share->getSharedWith());
177
			$exists = $qb->execute();
178
			$data = $exists->fetch();
179
			$exists->closeCursor();
180
181
			if ($data !== false) {
182
				throw $this->errorShareAlreadyExist($share);
183
			}
184
185
			$share->setToken($this->token(15));
186
187
			$this->createShare($share);
188
189
			$circle =
190
				$this->circlesRequest->getCircle($share->getSharedWith(), $share->getSharedby());
191
			$circle->getHigherViewer()
192
				   ->hasToBeMember();
193
194
			Circles::shareToCircle(
195
				$circle->getUniqueId(), 'files', '',
196
				['id' => $share->getId(), 'share' => $this->shareObjectToArray($share)],
197
				'\OCA\Circles\Circles\FileSharingBroadcaster'
198
			);
199
200
			return $this->getShareById($share->getId());
201
		} catch (Exception $e) {
202
			throw $e;
203
		}
204
	}
205
206
207
	/**
208
	 * Update a share
209
	 * permissions right, owner and initiator
210
	 *
211
	 * @param IShare $share
212
	 *
213
	 * @return IShare The share object
214
	 */
215
	public function update(IShare $share) {
216
217
		$qb = $this->getBaseUpdateSql();
218
		$this->limitToShare($qb, $share->getId());
219
		$qb->set('permissions', $qb->createNamedParameter($share->getPermissions()))
220
		   ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
221
		   ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
222
		$qb->execute();
223
224
		return $share;
225
	}
226
227
228
	/**
229
	 * Delete a share, and it's children
230
	 *
231
	 * @param IShare $share
232
	 *
233
	 * @throws Exception
234
	 */
235
	public function delete(IShare $share) {
236
		$qb = $this->getBaseDeleteSql();
237
		$this->limitToShareAndChildren($qb, $share->getId());
238
		$qb->execute();
239
240
		try {
241
			$circle = $this->circlesRequest->forceGetCircle($share->getSharedWith());
242
			$event = new GSEvent(GSEvent::FILE_UNSHARE, true);
243
			$event->setDeprecatedCircle($circle);
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Model\Global...::setDeprecatedCircle() has been deprecated.

This method has been deprecated.

Loading history...
244
245
			$store = new SimpleDataStore();
246
			$store->sArray(
247
				'share', [
248
						   'id' => $share->getId()
249
					   ]
250
			);
251
			$event->setData($store);
252
253
			$this->gsUpstreamService->newEvent($event);
254
		} catch (CircleDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
255
		}
256
257
		$this->tokensRequest->removeTokenByShareId($share->getId());
258
	}
259
260
261
	/**
262
	 * Unshare a file from self as recipient.
263
	 * Because every circles share are group shares, we will set permissions to 0
264
	 *
265
	 * @param IShare $share
266
	 * @param string $userId
267
	 */
268 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...
269
		$childId = $this->getShareChildId($share, $userId);
270
271
		$qb = $this->getBaseUpdateSql();
272
		$qb->set('permissions', $qb->createNamedParameter(0));
273
		$this->limitToShare($qb, $childId);
274
275
		$qb->execute();
276
	}
277
278
279
	/**
280
	 * Move a share as a recipient.
281
	 *
282
	 * @param IShare $share
283
	 * @param string $userId
284
	 *
285
	 * @return IShare
286
	 *
287
	 */
288 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...
289
290
		$childId = $this->getShareChildId($share, $userId);
291
292
		$qb = $this->getBaseUpdateSql();
293
		$qb->set('file_target', $qb->createNamedParameter($share->getTarget()));
294
		$this->limitToShare($qb, $childId);
295
		$qb->execute();
296
297
		return $share;
298
	}
299
300
301
	/**
302
	 * return the child ID of a share
303
	 *
304
	 * @param IShare $share
305
	 * @param string $userId
306
	 *
307
	 * @return bool
308
	 */
309
	private function getShareChildId(IShare $share, $userId) {
310
		$qb = $this->getBaseSelectSql($share->getId());
311
		$this->limitToShareChildren($qb, $userId, $share->getId());
312
313
		$child = $qb->execute();
314
		$data = $child->fetch();
315
		$child->closeCursor();
316
317
		if ($data === false) {
318
			return $this->createShareChild($userId, $share);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createShareChild($userId, $share); (integer) is incompatible with the return type documented by OCA\Circles\ShareByCircl...ecated::getShareChildId of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
319
		}
320
321
		return $data['id'];
322
	}
323
324
325
	/**
326
	 * Create a child and returns its ID
327
	 *
328
	 * @param IShare $share
329
	 *
330
	 * @throws NotFoundException
331
	 */
332
	private function createShare($share) {
333
		$this->miscService->log(
334
			'Creating share (1/4) - type: ' . $share->getShareType() . ' - token: '
335
			. $share->getToken() . ' - type: ' . $share->getShareType() . ' - with: '
336
			. $share->getSharedWith() . ' - permissions: ' . $share->getPermissions(), 0
337
		);
338
339
		$qb = $this->getBaseInsertSql($share);
340
		$this->miscService->log('Share creation (2/4) : ' . json_encode($qb->getSQL()), 0);
341
342
		$result = $qb->execute();
343
		$this->miscService->log('Share creation result (3/4) : ' . json_encode($result), 0);
344
345
		$id = $qb->getLastInsertId();
346
		$this->miscService->log('Share created ID (4/4) : ' . $id, 0);
347
348
		try {
349
			$share->setId($id);
350
		} 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...
351
		}
352
	}
353
354
355
	/**
356
	 * Create a child and returns its ID
357
	 *
358
	 * @param string $userId
359
	 * @param IShare $share
360
	 *
361
	 * @return int
362
	 * @throws NotFoundException
363
	 */
364
	private function createShareChild($userId, $share) {
365
		$qb = $this->getBaseInsertSql($share);
366
367
		$qb->setValue('parent', $qb->createNamedParameter($share->getId()));
368
		$qb->setValue('share_with', $qb->createNamedParameter($userId));
369
		$qb->execute();
370
		$id = $qb->getLastInsertId();
371
372
		return (int)$id;
373
	}
374
375
376
	/**
377
	 * Get all shares by the given user in a folder
378
	 *
379
	 * @param string $userId
380
	 * @param Folder $node
381
	 * @param bool $reshares Also get the shares where $user is the owner instead of just the
382
	 *     shares where $user is the initiator
383
	 *
384
	 * @return Share[]
385
	 */
386
	public function getSharesInFolder($userId, Folder $node, $reshares) {
387
		\OC::$server->getLogger()->log(3, 'deprecated>getSharesInFolder');
388
		return [];
389
//
390
//		$qb = $this->getBaseSelectSql();
391
//		$this->limitToShareOwner($qb, $userId, true);
392
//		$cursor = $qb->execute();
393
//
394
//		$shares = [];
395
//		while ($data = $cursor->fetch()) {
396
//			$shares[$data['file_source']][] = $this->createShareObject($data);
397
//		}
398
//		$cursor->closeCursor();
399
//
400
//		return $shares;
401
	}
402
403
404
	/**
405
	 * Get all shares by the given user
406
	 *
407
	 * @param string $userId
408
	 * @param int $shareType
409
	 * @param Node|null $node
410
	 * @param bool $reShares
411
	 * @param int $limit The maximum number of shares to be returned, -1 for all shares
412
	 * @param int $offset
413
	 *
414
	 * @return Share[]
415
	 * @throws NoUserException
416
	 * @throws NotFoundException
417
	 * @throws InvalidPathException
418
	 */
419
	public function getSharesBy($userId, $shareType, $node, $reShares, $limit, $offset) {
420
		$qb = $this->getBaseSelectSql();
421
422
		if ($node === null) {
423
			$this->limitToShareOwner($qb, $userId, $reShares);
424
		} else {
425
			$this->limitToFiles($qb, $node->getId());
426
		}
427
428
		$this->limitToPage($qb, $limit, $offset);
429
		$cursor = $qb->execute();
430
431
		$shares = [];
432
		while ($data = $cursor->fetch()) {
433
			$shares[] = $this->createShareObject($this->editShareEntry($data));
434
		}
435
		$cursor->closeCursor();
436
437
		return $shares;
438
	}
439
440
441
	/**
442
	 * returns a better formatted string to display more information about
443
	 * the circle to the Sharing UI
444
	 *
445
	 * @param $data
446
	 *
447
	 * @return array<string,string>
448
	 * @throws NoUserException
449
	 */
450
	private function editShareEntry($data) {
451
		$name = $data['circle_name'];
452
		if ($data['circle_alt_name'] !== '') {
453
			$name = $data['circle_alt_name'];
454
		}
455
456
		$data['share_with'] =
457
			sprintf(
458
				'%s (%s, %s) [%s]', $name,
459
				$this->l10n->t(DeprecatedCircle::TypeLongString($data['circle_type'])),
460
				$this->miscService->getDisplayName($data['circle_owner'], true), $data['share_with']
461
			);
462
463
464
		return $data;
465
	}
466
467
468
	/**
469
	 * Get share by its id
470
	 *
471
	 * @param int $shareId
472
	 * @param string|null $recipientId
473
	 *
474
	 * @return Share
475
	 * @throws ShareNotFound
476
	 */
477
	public function getShareById($shareId, $recipientId = null) {
478
		\OC::$server->getLogger()->log(3, 'deprecated>getShareById');
479
		$qb = $this->getBaseSelectSql();
480
481
		$this->limitToShare($qb, $shareId);
482
483
		$cursor = $qb->execute();
484
		$data = $cursor->fetch();
485
		$cursor->closeCursor();
486
487
		if ($data === false) {
488
			throw new ShareNotFound();
489
		}
490
491
		return $this->createShareObject($data);
492
	}
493
494
495
	/**
496
	 * Get shares for a given path
497
498
	 * @param Node $path
499
	 *
500
	 * @return IShare[]|null
501
	 * @throws InvalidPathException
502
	 * @throws NotFoundException *
503
	 */
504
	public function getSharesByPath(Node $path) {
505
		\OC::$server->getLogger()->log(3, 'deprecated>getSharesByPath');
506
		return [];
507
//		$qb = $this->getBaseSelectSql();
508
//		$this->limitToFiles($qb, [$path->getId()]);
509
//
510
//		$cursor = $qb->execute();
511
//
512
//		$shares = [];
513
//		while ($data = $cursor->fetch()) {
514
//			$shares[] = $this->createShareObject($data);
515
//		}
516
//		$cursor->closeCursor();
517
//
518
//		return $shares;
519
	}
520
521
522
	/**
523
	 * Get shared with the given user
524
	 *
525
	 * @param string $userId get shares where this user is the recipient
526
	 * @param int $shareType
527
	 * @param Node|null $node
528
	 * @param int $limit The max number of entries returned, -1 for all
529
	 * @param int $offset
530
	 *
531
	 * @return IShare[]
532
	 * @throws Exceptions\GSStatusException
533
	 * @throws InvalidPathException
534
	 * @throws NotFoundException
535
	 */
536
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
537
//		\OC::$server->getLogger()->log(3, 'deprecated>getSharedWith');
538
		return $this->getSharedWithCircleMembers($userId, $shareType, $node, $limit, $offset);
539
	}
540
541
542
	/**
543
	 * @param string $userId
544
	 * @param $shareType
545
	 * @param Node $node
546
	 * @param int $limit
547
	 * @param int $offset
548
	 *
549
	 * @return IShare[]
550
	 * @throws Exceptions\GSStatusException
551
	 * @throws InvalidPathException
552
	 * @throws NotFoundException
553
	 */
554
	private function getSharedWithCircleMembers($userId, $shareType, $node, $limit, $offset) {
555
556
		$qb = $this->getCompleteSelectSql();
557
		$this->linkToFileCache($qb, $userId);
558
		$this->limitToPage($qb, $limit, $offset);
559
560
		if ($node !== null) {
561
			$this->limitToFiles($qb, [$node->getId()]);
562
		}
563
564
		$this->linkToMember($qb, $userId, false, 'c');
565
566
		$shares = [];
567
		$cursor = $qb->execute();
568
		while ($data = $cursor->fetch()) {
569
			self::editShareFromParentEntry($data);
570
			if (self::isAccessibleResult($data)) {
571
				$shares[] = $this->createShareObject($data);
572
			}
573
		}
574
		$cursor->closeCursor();
575
576
		return $shares;
577
	}
578
579
580
	/**
581
	 * Get a share by token
582
	 *
583
	 * @param string $token
584
	 *
585
	 * @return IShare
586
	 * @throws ShareNotFound
587
	 * @deprecated - use local querybuilder lib instead
588
	 */
589
	public function getShareByToken($token) {
590
		\OC::$server->getLogger()->log(3, 'deprecated>getShareByToken');
591
		$qb = $this->dbConnection->getQueryBuilder();
592
593
		$this->miscService->log("Opening share by token '#" . $token . "'", 0);
594
595
		$cursor = $qb->select('*')
596
					 ->from('share')
597
					 ->where(
598
						 $qb->expr()
599
							->eq(
600
								'share_type',
601
								$qb->createNamedParameter(\OCP\Share::SHARE_TYPE_CIRCLE)
602
							)
603
					 )
604
					 ->andWhere(
605
						 $qb->expr()
606
							->eq('token', $qb->createNamedParameter($token))
607
					 )
608
					 ->execute();
609
610
		$data = $cursor->fetch();
611
612
		if ($data === false) {
613
			$this->miscService->log('data is false - checking personal token', 0);
614
			try {
615
				$data = $this->getShareByPersonalToken($token);
616
			} catch (Exception $e) {
617
				$this->miscService->log("Share '#" . $token . "' not found.", 0);
618
				throw new ShareNotFound('Share not found', $this->l10n->t('Could not find share'));
619
			}
620
		}
621
622
		try {
623
			$share = $this->createShareObject($data);
624
		} 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...
625
			$this->miscService->log(
626
				"Share Object '#" . $token . "' not created. " . json_encode($data), 0
627
			);
628
			throw new ShareNotFound('Share not found', $this->l10n->t('Could not find share'));
629
		}
630
631
		$share->setStatus(Ishare::STATUS_ACCEPTED);
632
633
		return $share;
634
	}
635
636
637
	/**
638
	 * @param string $token
639
	 *
640
	 * @return array
641
	 * @throws ShareNotFound
642
	 */
643
	private function getShareByPersonalToken($token) {
644
		$qb = $this->dbConnection->getQueryBuilder();
645
646
		$qb = $qb->select('s.*')
647
				 ->selectAlias('ct.password', 'personal_password')
648
				 ->selectAlias('ct.circle_id', 'personal_circle_id')
649
				 ->selectAlias('ct.user_id', 'personal_user_id')
650
				 ->selectAlias('ct.member_id', 'personal_member_id')
651
				 ->from('share', 's')
652
				 ->from('circle_tokens', 'ct')
653
				 ->where(
654
					 $qb->expr()
655
						->eq(
656
							's.share_type',
657
							$qb->createNamedParameter(\OCP\Share::SHARE_TYPE_CIRCLE)
658
						)
659
				 )
660
				 ->andWhere(
661
					 $qb->expr()
662
						->eq('ct.token', $qb->createNamedParameter($token))
663
				 )
664
				 ->andWhere(
665
					 $qb->expr()
666
						->eq('ct.share_id', 's.id')
667
				 );
668
		$cursor = $qb->execute();
669
670
		$data = $cursor->fetch();
671
		if ($data === false) {
672
			throw new ShareNotFound('personal check not found');
673
		}
674
675
		$member = null;
676
		try {
677
			$member = $this->membersService->getMemberById($data['personal_member_id']);
678
			if (!$member->isLevel(DeprecatedMember::LEVEL_MEMBER)) {
679
				throw new Exception();
680
			}
681
		} catch (Exception $e) {
682
			throw new ShareNotFound('invalid token');
683
		}
684
685
		return $data;
686
	}
687
688
689
	/**
690
	 * We don't return a thing about children.
691
	 * The call to this function is deprecated and should be removed in next release of NC.
692
	 * Also, we get the children in the delete() method.
693
	 *
694
	 * @param IShare $parent
695
	 *
696
	 * @return array
697
	 */
698
	public function getChildren(IShare $parent) {
699
		return [];
700
	}
701
702
703
	/**
704
	 * A user is deleted from the system
705
	 * So clean up the relevant shares.
706
	 *
707
	 * @param string $uid
708
	 * @param int $shareType
709
	 */
710
	public function userDeleted($uid, $shareType) {
711
		// TODO: Implement userDeleted() method.
712
	}
713
714
715
	/**
716
	 * A group is deleted from the system.
717
	 * We handle our own groups.
718
	 *
719
	 * @param string $gid
720
	 */
721
	public function groupDeleted($gid) {
722
		return;
723
	}
724
725
726
	/**
727
	 * A user is deleted from a group.
728
	 * We handle our own groups.
729
	 *
730
	 * @param string $uid
731
	 * @param string $gid
732
	 */
733
	public function userDeletedFromGroup($uid, $gid) {
734
		return;
735
	}
736
737
738
	/**
739
	 * Create a share object
740
	 *
741
	 * @param array $data
742
	 *
743
	 * @return IShare
744
	 * @throws IllegalIDChangeException
745
	 */
746
	private function createShareObject($data) {
747
		$share = new Share($this->rootFolder, $this->userManager);
748
		$share->setId((int)$data['id'])
749
			  ->setPermissions((int)$data['permissions'])
750
			  ->setNodeType($data['item_type']);
751
752
		if (($password = $this->get('personal_password', $data, '')) !== '') {
753
			$share->setPassword($this->get('personal_password', $data, ''));
754
		} else if (($password = $this->get('password', $data, '')) !== '') {
755
			$share->setPassword($this->get('password', $data, ''));
756
		}
757
758
		$share->setNodeId((int)$data['file_source'])
759
			  ->setTarget($data['file_target']);
760
761
		$this->assignShareObjectSharesProperties($share, $data);
762
		$this->assignShareObjectPropertiesFromParent($share, $data);
763
764
		$share->setProviderId($this->identifier());
765
		$share->setStatus(Ishare::STATUS_ACCEPTED);
766
767
		return $share;
768
	}
769
770
771
	/**
772
	 * @param IShare $share
773
	 * @param $data
774
	 */
775
	private function assignShareObjectPropertiesFromParent(IShare $share, $data) {
776
		if (isset($data['f_permissions'])) {
777
			$entryData = $data;
778
			$entryData['permissions'] = $entryData['f_permissions'];
779
			$entryData['parent'] = $entryData['f_parent'];
780
			$share->setNodeCacheEntry(
781
				Cache::cacheEntryFromData(
782
					$entryData,
783
					OC::$server->getMimeTypeLoader()
784
				)
785
			);
786
		}
787
	}
788
789
790
	/**
791
	 * @param IShare $share
792
	 * @param $data
793
	 *
794
	 * @throws NoUserException
795
	 */
796
	private function assignShareObjectSharesProperties(IShare $share, $data) {
797
		$shareTime = new \DateTime();
798
		$shareTime->setTimestamp((int)$data['stime']);
799
800
		$share->setShareTime($shareTime);
801
		$share->setSharedWith($data['share_with'])
802
			  ->setSharedBy($data['uid_initiator'])
803
			  ->setShareOwner($data['uid_owner'])
804
			  ->setShareType((int)$data['share_type']);
805
806
		if (array_key_exists('circle_type', $data)
807
			&& method_exists($share, 'setSharedWithDisplayName')) {
808
			$name = $data['circle_name'];
809
			if ($data['circle_alt_name'] !== '') {
810
				$name = $data['circle_alt_name'];
811
			}
812
813
			$share->setSharedWithAvatar(CirclesService::getCircleIcon($data['circle_type']))
814
				  ->setSharedWithDisplayName(
815
					  sprintf(
816
						  '%s (%s, %s)', $name,
817
						  $this->l10n->t(DeprecatedCircle::TypeLongString($data['circle_type'])),
818
						  $this->miscService->getDisplayName($data['circle_owner'], true)
819
					  )
820
				  );
821
		}
822
	}
823
824
825
	/**
826
	 * @param IShare $share
827
	 *
828
	 * @return Exception
829
	 * @throws NotFoundException
830
	 */
831
	private function errorShareAlreadyExist($share) {
832
		$share_src = $share->getNode()
833
						   ->getName();
834
835
		$message = 'Sharing %s failed, this item is already shared with this circle';
836
		$message_t = $this->l10n->t($message, array($share_src));
837
		$this->logger->debug(
838
			sprintf($message, $share_src, $share->getSharedWith()), ['app' => 'circles']
839
		);
840
841
		return new Exception($message_t);
842
	}
843
844
845
	/**
846
	 * Get the access list to the array of provided nodes.
847
	 *
848
	 * @param Node[] $nodes The list of nodes to get access for
849
	 * @param bool $currentAccess If current access is required (like for removed shares that might
850
	 *     get revived later)
851
	 *
852
	 * @return array
853
	 * @throws InvalidPathException
854
	 * @throws NotFoundException
855
	 * @see IManager::getAccessList() for sample docs
856
	 *
857
	 * @since 12
858
	 */
859
	public function getAccessList($nodes, $currentAccess) {
860
		$ids = [];
861
		foreach ($nodes as $node) {
862
			$ids[] = $node->getId();
863
		}
864
865
		$qb = $this->getAccessListBaseSelectSql();
866
		$this->limitToFiles($qb, $ids);
867
868
		$users = $this->parseAccessListResult($qb);
869
870
		if ($currentAccess === false) {
871
			$users = array_keys($users);
872
		}
873
874
		return ['users' => $users];
875
	}
876
877
878
	/**
879
	 * Restore a share for a given recipient. The implementation could be provider independant.
880
	 *
881
	 * @param IShare $share
882
	 * @param string $recipient
883
	 *
884
	 * @return IShare The restored share object
885
	 *
886
	 * @since 14.0.0
887
	 */
888
	public function restore(IShare $share, string $recipient): IShare {
889
		return $share;
890
	}
891
892
893
	/**
894
	 * return array regarding getAccessList format.
895
	 * ie. \OC\Share20\Manager::getAccessList()
896
	 *
897
	 * @param IQueryBuilder $qb
898
	 *
899
	 * @return array
900
	 */
901
	private function parseAccessListResult(IQueryBuilder $qb) {
902
903
		$cursor = $qb->execute();
904
		$users = [];
905
906
		while ($row = $cursor->fetch()) {
907
			$userId = $row['user_id'];
908
909
			if (!key_exists($userId, $users)) {
910
				$users[$userId] = [
911
					'node_id'   => $row['file_source'],
912
					'node_path' => $row['file_target']
913
				];
914
			}
915
		}
916
		$cursor->closeCursor();
917
918
		return $users;
919
	}
920
921
922
	/**
923
	 * @param IShare $share
924
	 *
925
	 * @return array
926
	 * @throws NotFoundException
927
	 */
928
	private function shareObjectToArray(IShare $share) {
929
		return [
930
			'id'          => $share->getId(),
931
			'sharedWith'  => $share->getSharedWith(),
932
			'sharedBy'    => $share->getSharedBy(),
933
			'nodeId'      => $share->getNodeId(),
934
			'shareOwner'  => $share->getShareOwner(),
935
			'permissions' => $share->getPermissions(),
936
			'token'       => $share->getToken(),
937
			'password'    => ($share->getPassword() === null) ? '' : $share->getPassword()
938
		];
939
	}
940
941
942
	/**
943
	 * @param string $k
944
	 * @param array $arr
945
	 * @param string $default
946
	 *
947
	 * @return string
948
	 */
949
	protected function get($k, array $arr, string $default = ''): string {
950
		if ($arr === null) {
951
			return $default;
952
		}
953
954
		if (!array_key_exists($k, $arr)) {
955
			$subs = explode('.', $k, 2);
956
			if (sizeof($subs) > 1) {
957
				if (!array_key_exists($subs[0], $arr)) {
958
					return $default;
959
				}
960
961
				$r = $arr[$subs[0]];
962
				if (!is_array($r)) {
963
					return $default;
964
				}
965
966
				return $this->get($subs[1], $r, $default);
967
			} else {
968
				return $default;
969
			}
970
		}
971
972
		if ($arr[$k] === null || (!is_string($arr[$k]) && (!is_int($arr[$k])))) {
973
			return $default;
974
		}
975
976
		return (string)$arr[$k];
977
	}
978
979
980
	/**
981
	 * @inheritDoc
982
	 */
983
	public function getAllShares(): iterable {
984
		$qb = $this->dbConnection->getQueryBuilder();
985
986
		$qb->select('*')
987
		   ->from('share')
988
		   ->where(
989
			   $qb->expr()
990
				  ->orX(
991
					  $qb->expr()
992
						 ->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))
993
				  )
994
		   );
995
996
		$cursor = $qb->execute();
997
		while ($data = $cursor->fetch()) {
998
			try {
999
				yield $this->createShareObject($data);
1000
			} 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...
1001
			};
1002
		}
1003
		$cursor->closeCursor();
1004
	}
1005
1006
1007
	/**
1008
	 * @param int $length
1009
	 *
1010
	 * @return string
1011
	 */
1012 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...
1013
		$chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
1014
1015
		$str = '';
1016
		$max = strlen($chars);
1017
		for ($i = 0; $i < $length; $i++) {
1018
			try {
1019
				$str .= $chars[random_int(0, $max - 1)];
1020
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1021
			}
1022
		}
1023
1024
		return $str;
1025
	}
1026
1027
1028
}
1029