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

ShareByMailProvider::getSharesByPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 17
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 17
loc 17
rs 9.7
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Bjoern Schiessle <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\ShareByMail;
23
24
use OC\CapabilitiesManager;
25
use OC\HintException;
26
use OC\Share20\Exception\InvalidShare;
27
use OC\User\NoUserException;
28
use OCA\ShareByMail\Settings\SettingsManager;
29
use OCP\Activity\IManager;
30
use OCP\DB\QueryBuilder\IQueryBuilder;
31
use OCP\Defaults;
32
use OCP\Files\Folder;
33
use OCP\Files\IRootFolder;
34
use OCP\Files\Node;
35
use OCP\IDBConnection;
36
use OCP\IL10N;
37
use OCP\ILogger;
38
use OCP\IURLGenerator;
39
use OCP\IUser;
40
use OCP\IUserManager;
41
use OCP\Mail\IMailer;
42
use OCP\Security\IHasher;
43
use OCP\Security\ISecureRandom;
44
use OC\Share20\Share;
45
use OCP\Share\Exceptions\GenericShareException;
46
use OCP\Share\Exceptions\ShareNotFound;
47
use OCP\Share\IShare;
48
use OCP\Share\IShareProvider;
49
50
/**
51
 * Class ShareByMail
52
 *
53
 * @package OCA\ShareByMail
54
 */
55
class ShareByMailProvider implements IShareProvider {
56
57
	/** @var  IDBConnection */
58
	private $dbConnection;
59
60
	/** @var ILogger */
61
	private $logger;
62
63
	/** @var ISecureRandom */
64
	private $secureRandom;
65
66
	/** @var IUserManager */
67
	private $userManager;
68
69
	/** @var IRootFolder */
70
	private $rootFolder;
71
72
	/** @var IL10N */
73
	private $l;
74
75
	/** @var IMailer */
76
	private $mailer;
77
78
	/** @var IURLGenerator */
79
	private $urlGenerator;
80
81
	/** @var IManager  */
82
	private $activityManager;
83
84
	/** @var SettingsManager */
85
	private $settingsManager;
86
87
	/** @var Defaults */
88
	private $defaults;
89
90
	/** @var IHasher */
91
	private $hasher;
92
93
	/** @var  CapabilitiesManager */
94
	private $capabilitiesManager;
95
96
	/**
97
	 * Return the identifier of this provider.
98
	 *
99
	 * @return string Containing only [a-zA-Z0-9]
100
	 */
101
	public function identifier() {
102
		return 'ocMailShare';
103
	}
104
105
	/**
106
	 * DefaultShareProvider constructor.
107
	 *
108
	 * @param IDBConnection $connection
109
	 * @param ISecureRandom $secureRandom
110
	 * @param IUserManager $userManager
111
	 * @param IRootFolder $rootFolder
112
	 * @param IL10N $l
113
	 * @param ILogger $logger
114
	 * @param IMailer $mailer
115
	 * @param IURLGenerator $urlGenerator
116
	 * @param IManager $activityManager
117
	 * @param SettingsManager $settingsManager
118
	 * @param Defaults $defaults
119
	 * @param IHasher $hasher
120
	 * @param CapabilitiesManager $capabilitiesManager
121
	 */
122
	public function __construct(
123
		IDBConnection $connection,
124
		ISecureRandom $secureRandom,
125
		IUserManager $userManager,
126
		IRootFolder $rootFolder,
127
		IL10N $l,
128
		ILogger $logger,
129
		IMailer $mailer,
130
		IURLGenerator $urlGenerator,
131
		IManager $activityManager,
132
		SettingsManager $settingsManager,
133
		Defaults $defaults,
134
		IHasher $hasher,
135
		CapabilitiesManager $capabilitiesManager
136
	) {
137
		$this->dbConnection = $connection;
138
		$this->secureRandom = $secureRandom;
139
		$this->userManager = $userManager;
140
		$this->rootFolder = $rootFolder;
141
		$this->l = $l;
142
		$this->logger = $logger;
143
		$this->mailer = $mailer;
144
		$this->urlGenerator = $urlGenerator;
145
		$this->activityManager = $activityManager;
146
		$this->settingsManager = $settingsManager;
147
		$this->defaults = $defaults;
148
		$this->hasher = $hasher;
149
		$this->capabilitiesManager = $capabilitiesManager;
150
	}
151
152
	/**
153
	 * Share a path
154
	 *
155
	 * @param IShare $share
156
	 * @return IShare The share object
157
	 * @throws ShareNotFound
158
	 * @throws \Exception
159
	 */
160
	public function create(IShare $share) {
161
162
		$shareWith = $share->getSharedWith();
163
		/*
164
		 * Check if file is not already shared with the remote user
165
		 */
166
		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
167 View Code Duplication
		if (!empty($alreadyShared)) {
168
			$message = 'Sharing %s failed, this item is already shared with %s';
169
			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
170
			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
171
			throw new \Exception($message_t);
172
		}
173
174
		// if the admin enforces a password for all mail shares we create a
175
		// random password and send it to the recipient
176
		$password = '';
177
		$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
178
		if ($passwordEnforced) {
179
			$password = $this->autoGeneratePassword($share);
180
		}
181
182
		$shareId = $this->createMailShare($share);
183
		$send = $this->sendPassword($share, $password);
184
		if ($passwordEnforced && $send === false) {
185
			$this->sendPasswordToOwner($share, $password);
186
		}
187
188
		$this->createShareActivity($share);
189
		$data = $this->getRawShare($shareId);
190
191
		return $this->createShareObject($data);
192
193
	}
194
195
	/**
196
	 * auto generate password in case of password enforcement on mail shares
197
	 *
198
	 * @param IShare $share
199
	 * @return string
200
	 * @throws \Exception
201
	 */
202
	protected function autoGeneratePassword($share) {
203
		$initiatorUser = $this->userManager->get($share->getSharedBy());
204
		$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
205
		$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
206
207
		if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
208
			throw new \Exception(
209
				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
210
			);
211
		}
212
213
		$passwordPolicy = $this->getPasswordPolicy();
214
		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
215
		$passwordLength = 8;
216
		if (!empty($passwordPolicy)) {
217
			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
218
			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
219
		}
220
221
		$password = $this->secureRandom->generate($passwordLength, $passwordCharset);
222
223
		$share->setPassword($this->hasher->hash($password));
224
225
		return $password;
226
	}
227
228
	/**
229
	 * get password policy
230
	 *
231
	 * @return array
232
	 */
233
	protected function getPasswordPolicy() {
234
		$capabilities = $this->capabilitiesManager->getCapabilities();
235
		if (isset($capabilities['password_policy'])) {
236
			return $capabilities['password_policy'];
237
		}
238
239
		return [];
240
	}
241
242
	/**
243
	 * create activity if a file/folder was shared by mail
244
	 *
245
	 * @param IShare $share
246
	 */
247
	protected function createShareActivity(IShare $share) {
248
249
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
250
251
		$this->publishActivity(
252
			Activity::SUBJECT_SHARED_EMAIL_SELF,
253
			[$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
254
			$share->getSharedBy(),
255
			$share->getNode()->getId(),
256
			$userFolder->getRelativePath($share->getNode()->getPath())
257
		);
258
259
		if ($share->getShareOwner() !== $share->getSharedBy()) {
260
			$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
261
			$fileId = $share->getNode()->getId();
262
			$nodes = $ownerFolder->getById($fileId);
263
			$ownerPath = $nodes[0]->getPath();
264
			$this->publishActivity(
265
				Activity::SUBJECT_SHARED_EMAIL_BY,
266
				[$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
267
				$share->getShareOwner(),
268
				$fileId,
269
				$ownerFolder->getRelativePath($ownerPath)
270
			);
271
		}
272
273
	}
274
275
	/**
276
	 * create activity if a file/folder was shared by mail
277
	 *
278
	 * @param IShare $share
279
	 * @param string $sharedWith
280
	 * @param bool $sendToSelf
281
	 */
282
	protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
283
284
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
285
286
		if ($sendToSelf) {
287
			$this->publishActivity(
288
				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
289
				[$userFolder->getRelativePath($share->getNode()->getPath())],
290
				$share->getSharedBy(),
291
				$share->getNode()->getId(),
292
				$userFolder->getRelativePath($share->getNode()->getPath())
293
			);
294
		} else {
295
			$this->publishActivity(
296
				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
297
				[$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
298
				$share->getSharedBy(),
299
				$share->getNode()->getId(),
300
				$userFolder->getRelativePath($share->getNode()->getPath())
301
			);
302
		}
303
	}
304
305
306
	/**
307
	 * publish activity if a file/folder was shared by mail
308
	 *
309
	 * @param $subject
310
	 * @param $parameters
311
	 * @param $affectedUser
312
	 * @param $fileId
313
	 * @param $filePath
314
	 */
315 View Code Duplication
	protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
316
		$event = $this->activityManager->generateEvent();
317
		$event->setApp('sharebymail')
318
			->setType('shared')
319
			->setSubject($subject, $parameters)
320
			->setAffectedUser($affectedUser)
321
			->setObject('files', $fileId, $filePath);
322
		$this->activityManager->publish($event);
323
324
	}
325
326
	/**
327
	 * @param IShare $share
328
	 * @return int
329
	 * @throws \Exception
330
	 */
331
	protected function createMailShare(IShare $share) {
332
		$share->setToken($this->generateToken());
333
		$shareId = $this->addShareToDB(
334
			$share->getNodeId(),
335
			$share->getNodeType(),
336
			$share->getSharedWith(),
337
			$share->getSharedBy(),
338
			$share->getShareOwner(),
339
			$share->getPermissions(),
340
			$share->getToken(),
341
			$share->getPassword()
342
		);
343
344
		try {
345
			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
346
				['token' => $share->getToken()]);
347
			$this->sendMailNotification(
348
				$share->getNode()->getName(),
349
				$link,
350
				$share->getSharedBy(),
351
				$share->getSharedWith(),
352
				$share->getExpirationDate()
353
			);
354
		} catch (HintException $hintException) {
355
			$this->logger->logException($hintException, [
0 ignored issues
show
Documentation introduced by
$hintException is of type object<OC\HintException>, but the function expects a object<Throwable>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
356
				'message' => 'Failed to send share by mail.',
357
				'level' => ILogger::ERROR,
358
				'app' => 'sharebymail',
359
			]);
360
			$this->removeShareFromTable($shareId);
361
			throw $hintException;
362
		} catch (\Exception $e) {
363
			$this->logger->logException($e, [
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
364
				'message' => 'Failed to send share by mail.',
365
				'level' => ILogger::ERROR,
366
				'app' => 'sharebymail',
367
			]);
368
			$this->removeShareFromTable($shareId);
369
			throw new HintException('Failed to send share by mail',
370
				$this->l->t('Failed to send share by email'));
371
		}
372
373
		return $shareId;
374
375
	}
376
377
	/**
378
	 * @param string $filename
379
	 * @param string $link
380
	 * @param string $initiator
381
	 * @param string $shareWith
382
	 * @param \DateTime|null $expiration
383
	 * @throws \Exception If mail couldn't be sent
384
	 */
385
	protected function sendMailNotification($filename,
386
											$link,
387
											$initiator,
388
											$shareWith,
389
											\DateTime $expiration = null) {
390
		$initiatorUser = $this->userManager->get($initiator);
391
		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
392
		$message = $this->mailer->createMessage();
393
394
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
395
			'filename' => $filename,
396
			'link' => $link,
397
			'initiator' => $initiatorDisplayName,
398
			'expiration' => $expiration,
399
			'shareWith' => $shareWith,
400
		]);
401
402
		$emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
403
		$emailTemplate->addHeader();
404
		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
405
		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
406
407
		$emailTemplate->addBodyText(
408
			htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')),
409
			$text
410
		);
411
		$emailTemplate->addBodyButton(
412
			$this->l->t('Open »%s«', [$filename]),
413
			$link
414
		);
415
416
		$message->setTo([$shareWith]);
417
418
		// The "From" contains the sharers name
419
		$instanceName = $this->defaults->getName();
420
		$senderName = $this->l->t(
421
			'%s via %s',
422
			[
423
				$initiatorDisplayName,
424
				$instanceName
425
			]
426
		);
427
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
428
429
		// The "Reply-To" is set to the sharer if an mail address is configured
430
		// also the default footer contains a "Do not reply" which needs to be adjusted.
431
		$initiatorEmail = $initiatorUser->getEMailAddress();
432 View Code Duplication
		if($initiatorEmail !== null) {
433
			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
434
			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
435
		} else {
436
			$emailTemplate->addFooter();
437
		}
438
439
		$message->useTemplate($emailTemplate);
440
		$this->mailer->send($message);
441
	}
442
443
	/**
444
	 * send password to recipient of a mail share
445
	 *
446
	 * @param IShare $share
447
	 * @param string $password
448
	 * @return bool
449
	 */
450
	protected function sendPassword(IShare $share, $password) {
451
452
		$filename = $share->getNode()->getName();
453
		$initiator = $share->getSharedBy();
454
		$shareWith = $share->getSharedWith();
455
456
		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
457
			return false;
458
		}
459
460
		$initiatorUser = $this->userManager->get($initiator);
461
		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
462
		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
463
464
		$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
465
		$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
466
467
		$message = $this->mailer->createMessage();
468
469
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
470
			'filename' => $filename,
471
			'password' => $password,
472
			'initiator' => $initiatorDisplayName,
473
			'initiatorEmail' => $initiatorEmailAddress,
474
			'shareWith' => $shareWith,
475
		]);
476
477
		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
478
		$emailTemplate->addHeader();
479
		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
480
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
481
		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
482
483
		// The "From" contains the sharers name
484
		$instanceName = $this->defaults->getName();
485
		$senderName = $this->l->t(
486
			'%s via %s',
487
			[
488
				$initiatorDisplayName,
489
				$instanceName
490
			]
491
		);
492
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
493 View Code Duplication
		if ($initiatorEmailAddress !== null) {
494
			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
495
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
496
		} else {
497
			$emailTemplate->addFooter();
498
		}
499
500
		$message->setTo([$shareWith]);
501
		$message->useTemplate($emailTemplate);
502
		$this->mailer->send($message);
503
504
		$this->createPasswordSendActivity($share, $shareWith, false);
505
506
		return true;
507
	}
508
509
	/**
510
	 * send auto generated password to the owner. This happens if the admin enforces
511
	 * a password for mail shares and forbid to send the password by mail to the recipient
512
	 *
513
	 * @param IShare $share
514
	 * @param string $password
515
	 * @return bool
516
	 * @throws \Exception
517
	 */
518
	protected function sendPasswordToOwner(IShare $share, $password) {
519
520
		$filename = $share->getNode()->getName();
521
		$initiator = $this->userManager->get($share->getSharedBy());
522
		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
523
		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
524
		$shareWith = $share->getSharedWith();
525
526
		if ($initiatorEMailAddress === null) {
527
			throw new \Exception(
528
				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
529
			);
530
		}
531
532
		$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
533
534
		$message = $this->mailer->createMessage();
535
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
536
			'filename' => $filename,
537
			'password' => $password,
538
			'initiator' => $initiatorDisplayName,
539
			'initiatorEmail' => $initiatorEMailAddress,
540
			'shareWith' => $shareWith,
541
		]);
542
543
		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
544
		$emailTemplate->addHeader();
545
		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
546
		$emailTemplate->addBodyText($bodyPart);
547
		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
548
		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
549
		$emailTemplate->addFooter();
550
551
		if ($initiatorEMailAddress) {
552
			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
553
		}
554
		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
555
		$message->useTemplate($emailTemplate);
556
		$this->mailer->send($message);
557
558
		$this->createPasswordSendActivity($share, $shareWith, true);
559
560
		return true;
561
	}
562
563
	/**
564
	 * generate share token
565
	 *
566
	 * @return string
567
	 */
568
	protected function generateToken($size = 15) {
569
		$token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
570
		return $token;
571
	}
572
573
	/**
574
	 * Get all children of this share
575
	 *
576
	 * @param IShare $parent
577
	 * @return IShare[]
578
	 */
579 View Code Duplication
	public function getChildren(IShare $parent) {
580
		$children = [];
581
582
		$qb = $this->dbConnection->getQueryBuilder();
583
		$qb->select('*')
584
			->from('share')
585
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
586
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
587
			->orderBy('id');
588
589
		$cursor = $qb->execute();
590
		while($data = $cursor->fetch()) {
591
			$children[] = $this->createShareObject($data);
592
		}
593
		$cursor->closeCursor();
594
595
		return $children;
596
	}
597
598
	/**
599
	 * add share to the database and return the ID
600
	 *
601
	 * @param int $itemSource
602
	 * @param string $itemType
603
	 * @param string $shareWith
604
	 * @param string $sharedBy
605
	 * @param string $uidOwner
606
	 * @param int $permissions
607
	 * @param string $token
608
	 * @return int
609
	 */
610
	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
611
		$qb = $this->dbConnection->getQueryBuilder();
612
		$qb->insert('share')
613
			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
614
			->setValue('item_type', $qb->createNamedParameter($itemType))
615
			->setValue('item_source', $qb->createNamedParameter($itemSource))
616
			->setValue('file_source', $qb->createNamedParameter($itemSource))
617
			->setValue('share_with', $qb->createNamedParameter($shareWith))
618
			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
619
			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
620
			->setValue('permissions', $qb->createNamedParameter($permissions))
621
			->setValue('token', $qb->createNamedParameter($token))
622
			->setValue('password', $qb->createNamedParameter($password))
623
			->setValue('stime', $qb->createNamedParameter(time()));
624
625
		/*
626
		 * Added to fix https://github.com/owncloud/core/issues/22215
627
		 * Can be removed once we get rid of ajax/share.php
628
		 */
629
		$qb->setValue('file_target', $qb->createNamedParameter(''));
630
631
		$qb->execute();
632
		$id = $qb->getLastInsertId();
633
634
		return (int)$id;
635
	}
636
637
	/**
638
	 * Update a share
639
	 *
640
	 * @param IShare $share
641
	 * @param string|null $plainTextPassword
642
	 * @return IShare The share object
643
	 */
644
	public function update(IShare $share, $plainTextPassword = null) {
645
646
		$originalShare = $this->getShareById($share->getId());
647
648
		// a real password was given
649
		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
650
651
		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
652
			$this->sendPassword($share, $plainTextPassword);
653
		}
654
		/*
655
		 * We allow updating the permissions and password of mail shares
656
		 */
657
		$qb = $this->dbConnection->getQueryBuilder();
658
		$qb->update('share')
659
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
660
			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
661
			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
662
			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
663
			->set('password', $qb->createNamedParameter($share->getPassword()))
664
			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
665
			->execute();
666
667
		return $share;
668
	}
669
670
	/**
671
	 * @inheritdoc
672
	 */
673
	public function move(IShare $share, $recipient) {
674
		/**
675
		 * nothing to do here, mail shares are only outgoing shares
676
		 */
677
		return $share;
678
	}
679
680
	/**
681
	 * Delete a share (owner unShares the file)
682
	 *
683
	 * @param IShare $share
684
	 */
685
	public function delete(IShare $share) {
686
		$this->removeShareFromTable($share->getId());
687
	}
688
689
	/**
690
	 * @inheritdoc
691
	 */
692
	public function deleteFromSelf(IShare $share, $recipient) {
693
		// nothing to do here, mail shares are only outgoing shares
694
	}
695
696
	public function restore(IShare $share, string $recipient): IShare {
697
		throw new GenericShareException('not implemented');
698
	}
699
700
	/**
701
	 * @inheritdoc
702
	 */
703 View Code Duplication
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
704
		$qb = $this->dbConnection->getQueryBuilder();
705
		$qb->select('*')
706
			->from('share');
707
708
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
709
710
		/**
711
		 * Reshares for this user are shares where they are the owner.
712
		 */
713
		if ($reshares === false) {
714
			//Special case for old shares created via the web UI
715
			$or1 = $qb->expr()->andX(
716
				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
717
				$qb->expr()->isNull('uid_initiator')
718
			);
719
720
			$qb->andWhere(
721
				$qb->expr()->orX(
722
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
723
					$or1
724
				)
725
			);
726
		} else {
727
			$qb->andWhere(
728
				$qb->expr()->orX(
729
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
730
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
731
				)
732
			);
733
		}
734
735
		if ($node !== null) {
736
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
737
		}
738
739
		if ($limit !== -1) {
740
			$qb->setMaxResults($limit);
741
		}
742
743
		$qb->setFirstResult($offset);
744
		$qb->orderBy('id');
745
746
		$cursor = $qb->execute();
747
		$shares = [];
748
		while($data = $cursor->fetch()) {
749
			$shares[] = $this->createShareObject($data);
750
		}
751
		$cursor->closeCursor();
752
753
		return $shares;
754
	}
755
756
	/**
757
	 * @inheritdoc
758
	 */
759 View Code Duplication
	public function getShareById($id, $recipientId = null) {
760
		$qb = $this->dbConnection->getQueryBuilder();
761
762
		$qb->select('*')
763
			->from('share')
764
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
765
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
766
767
		$cursor = $qb->execute();
768
		$data = $cursor->fetch();
769
		$cursor->closeCursor();
770
771
		if ($data === false) {
772
			throw new ShareNotFound();
773
		}
774
775
		try {
776
			$share = $this->createShareObject($data);
777
		} catch (InvalidShare $e) {
778
			throw new ShareNotFound();
779
		}
780
781
		return $share;
782
	}
783
784
	/**
785
	 * Get shares for a given path
786
	 *
787
	 * @param \OCP\Files\Node $path
788
	 * @return IShare[]
789
	 */
790 View Code Duplication
	public function getSharesByPath(Node $path) {
791
		$qb = $this->dbConnection->getQueryBuilder();
792
793
		$cursor = $qb->select('*')
794
			->from('share')
795
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
796
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
797
			->execute();
798
799
		$shares = [];
800
		while($data = $cursor->fetch()) {
801
			$shares[] = $this->createShareObject($data);
802
		}
803
		$cursor->closeCursor();
804
805
		return $shares;
806
	}
807
808
	/**
809
	 * @inheritdoc
810
	 */
811 View Code Duplication
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
812
		/** @var IShare[] $shares */
813
		$shares = [];
814
815
		//Get shares directly with this user
816
		$qb = $this->dbConnection->getQueryBuilder();
817
		$qb->select('*')
818
			->from('share');
819
820
		// Order by id
821
		$qb->orderBy('id');
822
823
		// Set limit and offset
824
		if ($limit !== -1) {
825
			$qb->setMaxResults($limit);
826
		}
827
		$qb->setFirstResult($offset);
828
829
		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
830
		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
831
832
		// Filter by node if provided
833
		if ($node !== null) {
834
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
835
		}
836
837
		$cursor = $qb->execute();
838
839
		while($data = $cursor->fetch()) {
840
			$shares[] = $this->createShareObject($data);
841
		}
842
		$cursor->closeCursor();
843
844
845
		return $shares;
846
	}
847
848
	/**
849
	 * Get a share by token
850
	 *
851
	 * @param string $token
852
	 * @return IShare
853
	 * @throws ShareNotFound
854
	 */
855 View Code Duplication
	public function getShareByToken($token) {
856
		$qb = $this->dbConnection->getQueryBuilder();
857
858
		$cursor = $qb->select('*')
859
			->from('share')
860
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
861
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
862
			->execute();
863
864
		$data = $cursor->fetch();
865
866
		if ($data === false) {
867
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
868
		}
869
870
		try {
871
			$share = $this->createShareObject($data);
872
		} catch (InvalidShare $e) {
873
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
874
		}
875
876
		return $share;
877
	}
878
879
	/**
880
	 * remove share from table
881
	 *
882
	 * @param string $shareId
883
	 */
884
	protected function removeShareFromTable($shareId) {
885
		$qb = $this->dbConnection->getQueryBuilder();
886
		$qb->delete('share')
887
			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
888
		$qb->execute();
889
	}
890
891
	/**
892
	 * Create a share object from an database row
893
	 *
894
	 * @param array $data
895
	 * @return IShare
896
	 * @throws InvalidShare
897
	 * @throws ShareNotFound
898
	 */
899
	protected function createShareObject($data) {
900
901
		$share = new Share($this->rootFolder, $this->userManager);
902
		$share->setId((int)$data['id'])
903
			->setShareType((int)$data['share_type'])
904
			->setPermissions((int)$data['permissions'])
905
			->setTarget($data['file_target'])
906
			->setMailSend((bool)$data['mail_send'])
907
			->setToken($data['token']);
908
909
		$shareTime = new \DateTime();
910
		$shareTime->setTimestamp((int)$data['stime']);
911
		$share->setShareTime($shareTime);
912
		$share->setSharedWith($data['share_with']);
913
		$share->setPassword($data['password']);
914
915
		if ($data['uid_initiator'] !== null) {
916
			$share->setShareOwner($data['uid_owner']);
917
			$share->setSharedBy($data['uid_initiator']);
918
		} else {
919
			//OLD SHARE
920
			$share->setSharedBy($data['uid_owner']);
921
			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
922
923
			$owner = $path->getOwner();
924
			$share->setShareOwner($owner->getUID());
925
		}
926
927 View Code Duplication
		if ($data['expiration'] !== null) {
928
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
929
			if ($expiration !== false) {
930
				$share->setExpirationDate($expiration);
931
			}
932
		}
933
934
		$share->setNodeId((int)$data['file_source']);
935
		$share->setNodeType($data['item_type']);
936
937
		$share->setProviderId($this->identifier());
938
939
		return $share;
940
	}
941
942
	/**
943
	 * Get the node with file $id for $user
944
	 *
945
	 * @param string $userId
946
	 * @param int $id
947
	 * @return \OCP\Files\File|\OCP\Files\Folder
948
	 * @throws InvalidShare
949
	 */
950 View Code Duplication
	private function getNode($userId, $id) {
951
		try {
952
			$userFolder = $this->rootFolder->getUserFolder($userId);
953
		} catch (NoUserException $e) {
954
			throw new InvalidShare();
955
		}
956
957
		$nodes = $userFolder->getById($id);
958
959
		if (empty($nodes)) {
960
			throw new InvalidShare();
961
		}
962
963
		return $nodes[0];
964
	}
965
966
	/**
967
	 * A user is deleted from the system
968
	 * So clean up the relevant shares.
969
	 *
970
	 * @param string $uid
971
	 * @param int $shareType
972
	 */
973 View Code Duplication
	public function userDeleted($uid, $shareType) {
974
		$qb = $this->dbConnection->getQueryBuilder();
975
976
		$qb->delete('share')
977
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
978
			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
979
			->execute();
980
	}
981
982
	/**
983
	 * This provider does not support group shares
984
	 *
985
	 * @param string $gid
986
	 */
987
	public function groupDeleted($gid) {
988
	}
989
990
	/**
991
	 * This provider does not support group shares
992
	 *
993
	 * @param string $uid
994
	 * @param string $gid
995
	 */
996
	public function userDeletedFromGroup($uid, $gid) {
997
	}
998
999
	/**
1000
	 * get database row of a give share
1001
	 *
1002
	 * @param $id
1003
	 * @return array
1004
	 * @throws ShareNotFound
1005
	 */
1006 View Code Duplication
	protected function getRawShare($id) {
1007
1008
		// Now fetch the inserted share and create a complete share object
1009
		$qb = $this->dbConnection->getQueryBuilder();
1010
		$qb->select('*')
1011
			->from('share')
1012
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1013
1014
		$cursor = $qb->execute();
1015
		$data = $cursor->fetch();
1016
		$cursor->closeCursor();
1017
1018
		if ($data === false) {
1019
			throw new ShareNotFound;
1020
		}
1021
1022
		return $data;
1023
	}
1024
1025 View Code Duplication
	public function getSharesInFolder($userId, Folder $node, $reshares) {
1026
		$qb = $this->dbConnection->getQueryBuilder();
1027
		$qb->select('*')
1028
			->from('share', 's')
1029
			->andWhere($qb->expr()->orX(
1030
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1031
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1032
			))
1033
			->andWhere(
1034
				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1035
			);
1036
1037
		/**
1038
		 * Reshares for this user are shares where they are the owner.
1039
		 */
1040
		if ($reshares === false) {
1041
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1042
		} else {
1043
			$qb->andWhere(
1044
				$qb->expr()->orX(
1045
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1046
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1047
				)
1048
			);
1049
		}
1050
1051
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1052
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1053
1054
		$qb->orderBy('id');
1055
1056
		$cursor = $qb->execute();
1057
		$shares = [];
1058
		while ($data = $cursor->fetch()) {
1059
			$shares[$data['fileid']][] = $this->createShareObject($data);
1060
		}
1061
		$cursor->closeCursor();
1062
1063
		return $shares;
1064
	}
1065
1066
	/**
1067
	 * @inheritdoc
1068
	 */
1069
	public function getAccessList($nodes, $currentAccess) {
1070
		$ids = [];
1071
		foreach ($nodes as $node) {
1072
			$ids[] = $node->getId();
1073
		}
1074
1075
		$qb = $this->dbConnection->getQueryBuilder();
1076
		$qb->select('share_with')
1077
			->from('share')
1078
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1079
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1080
			->andWhere($qb->expr()->orX(
1081
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1082
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1083
			))
1084
			->setMaxResults(1);
1085
		$cursor = $qb->execute();
1086
1087
		$mail = $cursor->fetch() !== false;
1088
		$cursor->closeCursor();
1089
1090
		return ['public' => $mail];
1091
	}
1092
1093
}
1094