Completed
Push — master ( 8e884b...bea04d )
by Joas
12:13 queued 11:33
created

ShareByMailProvider::sendMailNotification()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 57
Code Lines 39

Duplication

Lines 6
Ratio 10.53 %

Importance

Changes 0
Metric Value
cc 4
eloc 39
nc 4
nop 5
dl 6
loc 57
rs 9.0309
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 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\ShareNotFound;
46
use OCP\Share\IShare;
47
use OCP\Share\IShareProvider;
48
49
/**
50
 * Class ShareByMail
51
 *
52
 * @package OCA\ShareByMail
53
 */
54
class ShareByMailProvider implements IShareProvider {
55
56
	/** @var  IDBConnection */
57
	private $dbConnection;
58
59
	/** @var ILogger */
60
	private $logger;
61
62
	/** @var ISecureRandom */
63
	private $secureRandom;
64
65
	/** @var IUserManager */
66
	private $userManager;
67
68
	/** @var IRootFolder */
69
	private $rootFolder;
70
71
	/** @var IL10N */
72
	private $l;
73
74
	/** @var IMailer */
75
	private $mailer;
76
77
	/** @var IURLGenerator */
78
	private $urlGenerator;
79
80
	/** @var IManager  */
81
	private $activityManager;
82
83
	/** @var SettingsManager */
84
	private $settingsManager;
85
86
	/** @var Defaults */
87
	private $defaults;
88
89
	/** @var IHasher */
90
	private $hasher;
91
92
	/** @var  CapabilitiesManager */
93
	private $capabilitiesManager;
94
95
	/**
96
	 * Return the identifier of this provider.
97
	 *
98
	 * @return string Containing only [a-zA-Z0-9]
99
	 */
100
	public function identifier() {
101
		return 'ocMailShare';
102
	}
103
104
	/**
105
	 * DefaultShareProvider constructor.
106
	 *
107
	 * @param IDBConnection $connection
108
	 * @param ISecureRandom $secureRandom
109
	 * @param IUserManager $userManager
110
	 * @param IRootFolder $rootFolder
111
	 * @param IL10N $l
112
	 * @param ILogger $logger
113
	 * @param IMailer $mailer
114
	 * @param IURLGenerator $urlGenerator
115
	 * @param IManager $activityManager
116
	 * @param SettingsManager $settingsManager
117
	 * @param Defaults $defaults
118
	 * @param IHasher $hasher
119
	 * @param CapabilitiesManager $capabilitiesManager
120
	 */
121
	public function __construct(
122
		IDBConnection $connection,
123
		ISecureRandom $secureRandom,
124
		IUserManager $userManager,
125
		IRootFolder $rootFolder,
126
		IL10N $l,
127
		ILogger $logger,
128
		IMailer $mailer,
129
		IURLGenerator $urlGenerator,
130
		IManager $activityManager,
131
		SettingsManager $settingsManager,
132
		Defaults $defaults,
133
		IHasher $hasher,
134
		CapabilitiesManager $capabilitiesManager
135
	) {
136
		$this->dbConnection = $connection;
137
		$this->secureRandom = $secureRandom;
138
		$this->userManager = $userManager;
139
		$this->rootFolder = $rootFolder;
140
		$this->l = $l;
141
		$this->logger = $logger;
142
		$this->mailer = $mailer;
143
		$this->urlGenerator = $urlGenerator;
144
		$this->activityManager = $activityManager;
145
		$this->settingsManager = $settingsManager;
146
		$this->defaults = $defaults;
147
		$this->hasher = $hasher;
148
		$this->capabilitiesManager = $capabilitiesManager;
149
	}
150
151
	/**
152
	 * Share a path
153
	 *
154
	 * @param IShare $share
155
	 * @return IShare The share object
156
	 * @throws ShareNotFound
157
	 * @throws \Exception
158
	 */
159
	public function create(IShare $share) {
160
161
		$shareWith = $share->getSharedWith();
162
		/*
163
		 * Check if file is not already shared with the remote user
164
		 */
165
		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166 View Code Duplication
		if (!empty($alreadyShared)) {
167
			$message = 'Sharing %s failed, this item is already shared with %s';
168
			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
			throw new \Exception($message_t);
171
		}
172
173
		// if the admin enforces a password for all mail shares we create a
174
		// random password and send it to the recipient
175
		$password = '';
176
		$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
		if ($passwordEnforced) {
178
			$password = $this->autoGeneratePassword($share);
179
		}
180
181
		$shareId = $this->createMailShare($share);
182
		$send = $this->sendPassword($share, $password);
183
		if ($passwordEnforced && $send === false) {
184
			$this->sendPasswordToOwner($share, $password);
185
		}
186
187
		$this->createShareActivity($share);
188
		$data = $this->getRawShare($shareId);
189
190
		return $this->createShareObject($data);
191
192
	}
193
194
	/**
195
	 * auto generate password in case of password enforcement on mail shares
196
	 *
197
	 * @param IShare $share
198
	 * @return string
199
	 * @throws \Exception
200
	 */
201
	protected function autoGeneratePassword($share) {
202
		$initiatorUser = $this->userManager->get($share->getSharedBy());
203
		$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
		$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
206
		if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
			throw new \Exception(
208
				$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.")
209
			);
210
		}
211
212
		$passwordPolicy = $this->getPasswordPolicy();
213
		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
		$passwordLength = 8;
215
		if (!empty($passwordPolicy)) {
216
			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
		}
219
220
		$password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
222
		$share->setPassword($this->hasher->hash($password));
223
224
		return $password;
225
	}
226
227
	/**
228
	 * get password policy
229
	 *
230
	 * @return array
231
	 */
232
	protected function getPasswordPolicy() {
233
		$capabilities = $this->capabilitiesManager->getCapabilities();
234
		if (isset($capabilities['password_policy'])) {
235
			return $capabilities['password_policy'];
236
		}
237
238
		return [];
239
	}
240
241
	/**
242
	 * create activity if a file/folder was shared by mail
243
	 *
244
	 * @param IShare $share
245
	 */
246
	protected function createShareActivity(IShare $share) {
247
248
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
250
		$this->publishActivity(
251
			Activity::SUBJECT_SHARED_EMAIL_SELF,
252
			[$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
			$share->getSharedBy(),
254
			$share->getNode()->getId(),
255
			$userFolder->getRelativePath($share->getNode()->getPath())
256
		);
257
258
		if ($share->getShareOwner() !== $share->getSharedBy()) {
259
			$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
			$fileId = $share->getNode()->getId();
261
			$nodes = $ownerFolder->getById($fileId);
262
			$ownerPath = $nodes[0]->getPath();
263
			$this->publishActivity(
264
				Activity::SUBJECT_SHARED_EMAIL_BY,
265
				[$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
				$share->getShareOwner(),
267
				$fileId,
268
				$ownerFolder->getRelativePath($ownerPath)
269
			);
270
		}
271
272
	}
273
274
	/**
275
	 * create activity if a file/folder was shared by mail
276
	 *
277
	 * @param IShare $share
278
	 * @param string $sharedWith
279
	 * @param bool $sendToSelf
280
	 */
281
	protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
283
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
285
		if ($sendToSelf) {
286
			$this->publishActivity(
287
				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
				[$userFolder->getRelativePath($share->getNode()->getPath())],
289
				$share->getSharedBy(),
290
				$share->getNode()->getId(),
291
				$userFolder->getRelativePath($share->getNode()->getPath())
292
			);
293
		} else {
294
			$this->publishActivity(
295
				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
				[$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
				$share->getSharedBy(),
298
				$share->getNode()->getId(),
299
				$userFolder->getRelativePath($share->getNode()->getPath())
300
			);
301
		}
302
	}
303
304
305
	/**
306
	 * publish activity if a file/folder was shared by mail
307
	 *
308
	 * @param $subject
309
	 * @param $parameters
310
	 * @param $affectedUser
311
	 * @param $fileId
312
	 * @param $filePath
313
	 */
314 View Code Duplication
	protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
		$event = $this->activityManager->generateEvent();
316
		$event->setApp('sharebymail')
317
			->setType('shared')
318
			->setSubject($subject, $parameters)
319
			->setAffectedUser($affectedUser)
320
			->setObject('files', $fileId, $filePath);
321
		$this->activityManager->publish($event);
322
323
	}
324
325
	/**
326
	 * @param IShare $share
327
	 * @return int
328
	 * @throws \Exception
329
	 */
330
	protected function createMailShare(IShare $share) {
331
		$share->setToken($this->generateToken());
332
		$shareId = $this->addShareToDB(
333
			$share->getNodeId(),
334
			$share->getNodeType(),
335
			$share->getSharedWith(),
336
			$share->getSharedBy(),
337
			$share->getShareOwner(),
338
			$share->getPermissions(),
339
			$share->getToken(),
340
			$share->getPassword()
341
		);
342
343
		try {
344
			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
				['token' => $share->getToken()]);
346
			$this->sendMailNotification(
347
				$share->getNode()->getName(),
348
				$link,
349
				$share->getSharedBy(),
350
				$share->getSharedWith(),
351
				$share->getExpirationDate()
352
			);
353
		} catch (HintException $hintException) {
354
			$this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
355
			$this->removeShareFromTable($shareId);
356
			throw $hintException;
357
		} catch (\Exception $e) {
358
			$this->logger->error('Failed to send share by email: ' . $e->getMessage());
359
			$this->removeShareFromTable($shareId);
360
			throw new HintException('Failed to send share by mail',
361
				$this->l->t('Failed to send share by email'));
362
		}
363
364
		return $shareId;
365
366
	}
367
368
	/**
369
	 * @param string $filename
370
	 * @param string $link
371
	 * @param string $initiator
372
	 * @param string $shareWith
373
	 * @param \DateTime|null $expiration
374
	 * @throws \Exception If mail couldn't be sent
375
	 */
376
	protected function sendMailNotification($filename,
377
											$link,
378
											$initiator,
379
											$shareWith,
380
											\DateTime $expiration = null) {
381
		$initiatorUser = $this->userManager->get($initiator);
382
		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
383
		$message = $this->mailer->createMessage();
384
385
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
386
			'filename' => $filename,
387
			'link' => $link,
388
			'initiator' => $initiatorDisplayName,
389
			'expiration' => $expiration,
390
			'shareWith' => $shareWith,
391
		]);
392
393
		$emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
394
		$emailTemplate->addHeader();
395
		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
396
		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
397
398
		$emailTemplate->addBodyText(
399
			$text . ' ' . $this->l->t('Click the button below to open it.'),
400
			$text
401
		);
402
		$emailTemplate->addBodyButton(
403
			$this->l->t('Open »%s«', [$filename]),
404
			$link
405
		);
406
407
		$message->setTo([$shareWith]);
408
409
		// The "From" contains the sharers name
410
		$instanceName = $this->defaults->getName();
411
		$senderName = $this->l->t(
412
			'%s via %s',
413
			[
414
				$initiatorDisplayName,
415
				$instanceName
416
			]
417
		);
418
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
419
420
		// The "Reply-To" is set to the sharer if an mail address is configured
421
		// also the default footer contains a "Do not reply" which needs to be adjusted.
422
		$initiatorEmail = $initiatorUser->getEMailAddress();
423 View Code Duplication
		if($initiatorEmail !== null) {
424
			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
425
			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
426
		} else {
427
			$emailTemplate->addFooter();
428
		}
429
430
		$message->useTemplate($emailTemplate);
431
		$this->mailer->send($message);
432
	}
433
434
	/**
435
	 * send password to recipient of a mail share
436
	 *
437
	 * @param IShare $share
438
	 * @param string $password
439
	 * @return bool
440
	 */
441
	protected function sendPassword(IShare $share, $password) {
442
443
		$filename = $share->getNode()->getName();
444
		$initiator = $share->getSharedBy();
445
		$shareWith = $share->getSharedWith();
446
447
		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
448
			return false;
449
		}
450
451
		$initiatorUser = $this->userManager->get($initiator);
452
		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
453
		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
454
455
		$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]);
456
		$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]);
457
458
		$message = $this->mailer->createMessage();
459
460
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
461
			'filename' => $filename,
462
			'password' => $password,
463
			'initiator' => $initiatorDisplayName,
464
			'initiatorEmail' => $initiatorEmailAddress,
465
			'shareWith' => $shareWith,
466
		]);
467
468
		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
469
		$emailTemplate->addHeader();
470
		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
471
		$emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
472
		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
473
474
		// The "From" contains the sharers name
475
		$instanceName = $this->defaults->getName();
476
		$senderName = $this->l->t(
477
			'%s via %s',
478
			[
479
				$initiatorDisplayName,
480
				$instanceName
481
			]
482
		);
483
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
484 View Code Duplication
		if ($initiatorEmailAddress !== null) {
485
			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
486
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
487
		} else {
488
			$emailTemplate->addFooter();
489
		}
490
491
		$message->setTo([$shareWith]);
492
		$message->useTemplate($emailTemplate);
493
		$this->mailer->send($message);
494
495
		$this->createPasswordSendActivity($share, $shareWith, false);
496
497
		return true;
498
	}
499
500
	/**
501
	 * send auto generated password to the owner. This happens if the admin enforces
502
	 * a password for mail shares and forbid to send the password by mail to the recipient
503
	 *
504
	 * @param IShare $share
505
	 * @param string $password
506
	 * @return bool
507
	 * @throws \Exception
508
	 */
509
	protected function sendPasswordToOwner(IShare $share, $password) {
510
511
		$filename = $share->getNode()->getName();
512
		$initiator = $this->userManager->get($share->getSharedBy());
513
		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
514
		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
515
		$shareWith = $share->getSharedWith();
516
517
		if ($initiatorEMailAddress === null) {
518
			throw new \Exception(
519
				$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.")
520
			);
521
		}
522
523
		$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()]);
524
525
		$message = $this->mailer->createMessage();
526
		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
527
			'filename' => $filename,
528
			'password' => $password,
529
			'initiator' => $initiatorDisplayName,
530
			'initiatorEmail' => $initiatorEMailAddress,
531
			'shareWith' => $shareWith,
532
		]);
533
534
		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
535
		$emailTemplate->addHeader();
536
		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
537
		$emailTemplate->addBodyText($bodyPart);
538
		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
539
		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
540
		$emailTemplate->addFooter();
541
542
		if ($initiatorEMailAddress) {
543
			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
544
		}
545
		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
546
		$message->useTemplate($emailTemplate);
547
		$this->mailer->send($message);
548
549
		$this->createPasswordSendActivity($share, $shareWith, true);
550
551
		return true;
552
	}
553
554
	/**
555
	 * generate share token
556
	 *
557
	 * @return string
558
	 */
559
	protected function generateToken($size = 15) {
560
		$token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
561
		return $token;
562
	}
563
564
	/**
565
	 * Get all children of this share
566
	 *
567
	 * @param IShare $parent
568
	 * @return IShare[]
569
	 */
570 View Code Duplication
	public function getChildren(IShare $parent) {
571
		$children = [];
572
573
		$qb = $this->dbConnection->getQueryBuilder();
574
		$qb->select('*')
575
			->from('share')
576
			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
577
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
578
			->orderBy('id');
579
580
		$cursor = $qb->execute();
581
		while($data = $cursor->fetch()) {
582
			$children[] = $this->createShareObject($data);
583
		}
584
		$cursor->closeCursor();
585
586
		return $children;
587
	}
588
589
	/**
590
	 * add share to the database and return the ID
591
	 *
592
	 * @param int $itemSource
593
	 * @param string $itemType
594
	 * @param string $shareWith
595
	 * @param string $sharedBy
596
	 * @param string $uidOwner
597
	 * @param int $permissions
598
	 * @param string $token
599
	 * @return int
600
	 */
601
	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
602
		$qb = $this->dbConnection->getQueryBuilder();
603
		$qb->insert('share')
604
			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
605
			->setValue('item_type', $qb->createNamedParameter($itemType))
606
			->setValue('item_source', $qb->createNamedParameter($itemSource))
607
			->setValue('file_source', $qb->createNamedParameter($itemSource))
608
			->setValue('share_with', $qb->createNamedParameter($shareWith))
609
			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
610
			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
611
			->setValue('permissions', $qb->createNamedParameter($permissions))
612
			->setValue('token', $qb->createNamedParameter($token))
613
			->setValue('password', $qb->createNamedParameter($password))
614
			->setValue('stime', $qb->createNamedParameter(time()));
615
616
		/*
617
		 * Added to fix https://github.com/owncloud/core/issues/22215
618
		 * Can be removed once we get rid of ajax/share.php
619
		 */
620
		$qb->setValue('file_target', $qb->createNamedParameter(''));
621
622
		$qb->execute();
623
		$id = $qb->getLastInsertId();
624
625
		return (int)$id;
626
	}
627
628
	/**
629
	 * Update a share
630
	 *
631
	 * @param IShare $share
632
	 * @param string|null $plainTextPassword
633
	 * @return IShare The share object
634
	 */
635
	public function update(IShare $share, $plainTextPassword = null) {
636
637
		$originalShare = $this->getShareById($share->getId());
638
639
		// a real password was given
640
		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
641
642
		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
643
			$this->sendPassword($share, $plainTextPassword);
644
		}
645
		/*
646
		 * We allow updating the permissions and password of mail shares
647
		 */
648
		$qb = $this->dbConnection->getQueryBuilder();
649
		$qb->update('share')
650
			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
651
			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
652
			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
653
			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
654
			->set('password', $qb->createNamedParameter($share->getPassword()))
655
			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
656
			->execute();
657
658
		return $share;
659
	}
660
661
	/**
662
	 * @inheritdoc
663
	 */
664
	public function move(IShare $share, $recipient) {
665
		/**
666
		 * nothing to do here, mail shares are only outgoing shares
667
		 */
668
		return $share;
669
	}
670
671
	/**
672
	 * Delete a share (owner unShares the file)
673
	 *
674
	 * @param IShare $share
675
	 */
676
	public function delete(IShare $share) {
677
		$this->removeShareFromTable($share->getId());
678
	}
679
680
	/**
681
	 * @inheritdoc
682
	 */
683
	public function deleteFromSelf(IShare $share, $recipient) {
684
		// nothing to do here, mail shares are only outgoing shares
685
		return;
686
	}
687
688
	/**
689
	 * @inheritdoc
690
	 */
691 View Code Duplication
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
692
		$qb = $this->dbConnection->getQueryBuilder();
693
		$qb->select('*')
694
			->from('share');
695
696
		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
697
698
		/**
699
		 * Reshares for this user are shares where they are the owner.
700
		 */
701
		if ($reshares === false) {
702
			//Special case for old shares created via the web UI
703
			$or1 = $qb->expr()->andX(
704
				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
705
				$qb->expr()->isNull('uid_initiator')
706
			);
707
708
			$qb->andWhere(
709
				$qb->expr()->orX(
710
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
711
					$or1
712
				)
713
			);
714
		} else {
715
			$qb->andWhere(
716
				$qb->expr()->orX(
717
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
718
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
719
				)
720
			);
721
		}
722
723
		if ($node !== null) {
724
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
725
		}
726
727
		if ($limit !== -1) {
728
			$qb->setMaxResults($limit);
729
		}
730
731
		$qb->setFirstResult($offset);
732
		$qb->orderBy('id');
733
734
		$cursor = $qb->execute();
735
		$shares = [];
736
		while($data = $cursor->fetch()) {
737
			$shares[] = $this->createShareObject($data);
738
		}
739
		$cursor->closeCursor();
740
741
		return $shares;
742
	}
743
744
	/**
745
	 * @inheritdoc
746
	 */
747 View Code Duplication
	public function getShareById($id, $recipientId = null) {
748
		$qb = $this->dbConnection->getQueryBuilder();
749
750
		$qb->select('*')
751
			->from('share')
752
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
753
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
754
755
		$cursor = $qb->execute();
756
		$data = $cursor->fetch();
757
		$cursor->closeCursor();
758
759
		if ($data === false) {
760
			throw new ShareNotFound();
761
		}
762
763
		try {
764
			$share = $this->createShareObject($data);
765
		} catch (InvalidShare $e) {
766
			throw new ShareNotFound();
767
		}
768
769
		return $share;
770
	}
771
772
	/**
773
	 * Get shares for a given path
774
	 *
775
	 * @param \OCP\Files\Node $path
776
	 * @return IShare[]
777
	 */
778 View Code Duplication
	public function getSharesByPath(Node $path) {
779
		$qb = $this->dbConnection->getQueryBuilder();
780
781
		$cursor = $qb->select('*')
782
			->from('share')
783
			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
784
			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
785
			->execute();
786
787
		$shares = [];
788
		while($data = $cursor->fetch()) {
789
			$shares[] = $this->createShareObject($data);
790
		}
791
		$cursor->closeCursor();
792
793
		return $shares;
794
	}
795
796
	/**
797
	 * @inheritdoc
798
	 */
799 View Code Duplication
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
800
		/** @var IShare[] $shares */
801
		$shares = [];
802
803
		//Get shares directly with this user
804
		$qb = $this->dbConnection->getQueryBuilder();
805
		$qb->select('*')
806
			->from('share');
807
808
		// Order by id
809
		$qb->orderBy('id');
810
811
		// Set limit and offset
812
		if ($limit !== -1) {
813
			$qb->setMaxResults($limit);
814
		}
815
		$qb->setFirstResult($offset);
816
817
		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
818
		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
819
820
		// Filter by node if provided
821
		if ($node !== null) {
822
			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
823
		}
824
825
		$cursor = $qb->execute();
826
827
		while($data = $cursor->fetch()) {
828
			$shares[] = $this->createShareObject($data);
829
		}
830
		$cursor->closeCursor();
831
832
833
		return $shares;
834
	}
835
836
	/**
837
	 * Get a share by token
838
	 *
839
	 * @param string $token
840
	 * @return IShare
841
	 * @throws ShareNotFound
842
	 */
843 View Code Duplication
	public function getShareByToken($token) {
844
		$qb = $this->dbConnection->getQueryBuilder();
845
846
		$cursor = $qb->select('*')
847
			->from('share')
848
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
849
			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
850
			->execute();
851
852
		$data = $cursor->fetch();
853
854
		if ($data === false) {
855
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
856
		}
857
858
		try {
859
			$share = $this->createShareObject($data);
860
		} catch (InvalidShare $e) {
861
			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
862
		}
863
864
		return $share;
865
	}
866
867
	/**
868
	 * remove share from table
869
	 *
870
	 * @param string $shareId
871
	 */
872
	protected function removeShareFromTable($shareId) {
873
		$qb = $this->dbConnection->getQueryBuilder();
874
		$qb->delete('share')
875
			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
876
		$qb->execute();
877
	}
878
879
	/**
880
	 * Create a share object from an database row
881
	 *
882
	 * @param array $data
883
	 * @return IShare
884
	 * @throws InvalidShare
885
	 * @throws ShareNotFound
886
	 */
887
	protected function createShareObject($data) {
888
889
		$share = new Share($this->rootFolder, $this->userManager);
890
		$share->setId((int)$data['id'])
891
			->setShareType((int)$data['share_type'])
892
			->setPermissions((int)$data['permissions'])
893
			->setTarget($data['file_target'])
894
			->setMailSend((bool)$data['mail_send'])
895
			->setToken($data['token']);
896
897
		$shareTime = new \DateTime();
898
		$shareTime->setTimestamp((int)$data['stime']);
899
		$share->setShareTime($shareTime);
900
		$share->setSharedWith($data['share_with']);
901
		$share->setPassword($data['password']);
902
903
		if ($data['uid_initiator'] !== null) {
904
			$share->setShareOwner($data['uid_owner']);
905
			$share->setSharedBy($data['uid_initiator']);
906
		} else {
907
			//OLD SHARE
908
			$share->setSharedBy($data['uid_owner']);
909
			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
910
911
			$owner = $path->getOwner();
912
			$share->setShareOwner($owner->getUID());
913
		}
914
915 View Code Duplication
		if ($data['expiration'] !== null) {
916
			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
917
			if ($expiration !== false) {
918
				$share->setExpirationDate($expiration);
919
			}
920
		}
921
922
		$share->setNodeId((int)$data['file_source']);
923
		$share->setNodeType($data['item_type']);
924
925
		$share->setProviderId($this->identifier());
926
927
		return $share;
928
	}
929
930
	/**
931
	 * Get the node with file $id for $user
932
	 *
933
	 * @param string $userId
934
	 * @param int $id
935
	 * @return \OCP\Files\File|\OCP\Files\Folder
936
	 * @throws InvalidShare
937
	 */
938 View Code Duplication
	private function getNode($userId, $id) {
939
		try {
940
			$userFolder = $this->rootFolder->getUserFolder($userId);
941
		} catch (NoUserException $e) {
942
			throw new InvalidShare();
943
		}
944
945
		$nodes = $userFolder->getById($id);
946
947
		if (empty($nodes)) {
948
			throw new InvalidShare();
949
		}
950
951
		return $nodes[0];
952
	}
953
954
	/**
955
	 * A user is deleted from the system
956
	 * So clean up the relevant shares.
957
	 *
958
	 * @param string $uid
959
	 * @param int $shareType
960
	 */
961 View Code Duplication
	public function userDeleted($uid, $shareType) {
962
		$qb = $this->dbConnection->getQueryBuilder();
963
964
		$qb->delete('share')
965
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
966
			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
967
			->execute();
968
	}
969
970
	/**
971
	 * This provider does not support group shares
972
	 *
973
	 * @param string $gid
974
	 */
975
	public function groupDeleted($gid) {
976
		return;
977
	}
978
979
	/**
980
	 * This provider does not support group shares
981
	 *
982
	 * @param string $uid
983
	 * @param string $gid
984
	 */
985
	public function userDeletedFromGroup($uid, $gid) {
986
		return;
987
	}
988
989
	/**
990
	 * get database row of a give share
991
	 *
992
	 * @param $id
993
	 * @return array
994
	 * @throws ShareNotFound
995
	 */
996
	protected function getRawShare($id) {
997
998
		// Now fetch the inserted share and create a complete share object
999
		$qb = $this->dbConnection->getQueryBuilder();
1000
		$qb->select('*')
1001
			->from('share')
1002
			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1003
1004
		$cursor = $qb->execute();
1005
		$data = $cursor->fetch();
1006
		$cursor->closeCursor();
1007
1008
		if ($data === false) {
1009
			throw new ShareNotFound;
1010
		}
1011
1012
		return $data;
1013
	}
1014
1015 View Code Duplication
	public function getSharesInFolder($userId, Folder $node, $reshares) {
1016
		$qb = $this->dbConnection->getQueryBuilder();
1017
		$qb->select('*')
1018
			->from('share', 's')
1019
			->andWhere($qb->expr()->orX(
1020
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1021
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1022
			))
1023
			->andWhere(
1024
				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1025
			);
1026
1027
		/**
1028
		 * Reshares for this user are shares where they are the owner.
1029
		 */
1030
		if ($reshares === false) {
1031
			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1032
		} else {
1033
			$qb->andWhere(
1034
				$qb->expr()->orX(
1035
					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1036
					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1037
				)
1038
			);
1039
		}
1040
1041
		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1042
		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1043
1044
		$qb->orderBy('id');
1045
1046
		$cursor = $qb->execute();
1047
		$shares = [];
1048
		while ($data = $cursor->fetch()) {
1049
			$shares[$data['fileid']][] = $this->createShareObject($data);
1050
		}
1051
		$cursor->closeCursor();
1052
1053
		return $shares;
1054
	}
1055
1056
	/**
1057
	 * @inheritdoc
1058
	 */
1059
	public function getAccessList($nodes, $currentAccess) {
1060
		$ids = [];
1061
		foreach ($nodes as $node) {
1062
			$ids[] = $node->getId();
1063
		}
1064
1065
		$qb = $this->dbConnection->getQueryBuilder();
1066
		$qb->select('share_with')
1067
			->from('share')
1068
			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1069
			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1070
			->andWhere($qb->expr()->orX(
1071
				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1072
				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1073
			))
1074
			->setMaxResults(1);
1075
		$cursor = $qb->execute();
1076
1077
		$mail = $cursor->fetch() !== false;
1078
		$cursor->closeCursor();
1079
1080
		return ['public' => $mail];
1081
	}
1082
1083
}
1084