Completed
Pull Request — master (#347)
by Maxence
02:20
created

FileSharingBroadcaster::generateEmailTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.536
c 0
b 0
f 0
cc 1
nc 1
nop 6
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
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
28
namespace OCA\Circles\Circles;
29
30
use daita\MySmallPhpTools\Traits\TArrayTools;
31
use Exception;
32
use OC;
33
use OC\Share20\Share;
34
use OCA\Circles\AppInfo\Application;
35
use OCA\Circles\Db\SharesRequest;
36
use OCA\Circles\Db\TokensRequest;
37
use OCA\Circles\Exceptions\TokenDoesNotExistException;
38
use OCA\Circles\IBroadcaster;
39
use OCA\Circles\Model\Circle;
40
use OCA\Circles\Model\Member;
41
use OCA\Circles\Model\SharesToken;
42
use OCA\Circles\Model\SharingFrame;
43
use OCA\Circles\Service\ConfigService;
44
use OCA\Circles\Service\MiscService;
45
use OCA\FederatedFileSharing\Notifications;
46
use OCP\AppFramework\QueryException;
47
use OCP\Defaults;
48
use OCP\Federation\ICloudFederationFactory;
49
use OCP\Federation\ICloudFederationProviderManager;
50
use OCP\Federation\ICloudIdManager;
51
use OCP\Files\IRootFolder;
52
use OCP\Files\NotFoundException;
53
use OCP\IL10N;
54
use OCP\ILogger;
55
use OCP\IURLGenerator;
56
use OCP\IUser;
57
use OCP\IUserManager;
58
use OCP\Mail\IEMailTemplate;
59
use OCP\Mail\IMailer;
60
use OCP\Share\Exceptions\IllegalIDChangeException;
61
use OCP\Share\IShare;
62
use OCP\Util;
63
64
65
class FileSharingBroadcaster implements IBroadcaster {
66
67
68
	use TArrayTools;
69
70
71
	/** @var bool */
72
	private $initiated = false;
73
74
	/** @var IL10N */
75
	private $l10n = null;
76
77
	/** @var IMailer */
78
	private $mailer;
79
80
	/** @var IRootFolder */
81
	private $rootFolder;
82
83
	/** @var IUserManager */
84
	private $userManager;
85
86
	/** @var ICloudFederationFactory */
87
	private $federationFactory;
88
89
	/** @var ICloudFederationProviderManager */
90
	private $federationProviderManager;
91
92
	/** @var ICloudIdManager */
93
	private $federationCloudIdManager;
94
95
	/** @var Notifications */
96
	private $federationNotifications;
97
98
	/** @var ILogger */
99
	private $logger;
100
101
	/** @var Defaults */
102
	private $defaults;
103
104
	/** @var IURLGenerator */
105
	private $urlGenerator;
106
107
	/** @var SharesRequest */
108
	private $sharesRequest;
109
110
	/** @var TokensRequest */
111
	private $tokensRequest;
112
113
	/** @var ConfigService */
114
	private $configService;
115
116
	/** @var MiscService */
117
	private $miscService;
118
119
	/** @var bool */
120
	private $federatedEnabled = false;
121
122
	/**
123
	 * {@inheritdoc}
124
	 */
125
	public function init() {
126
		if ($this->initiated) {
127
			return;
128
		}
129
130
		$this->initiated = true;
131
		$this->l10n = OC::$server->getL10N(Application::APP_NAME);
132
		$this->mailer = OC::$server->getMailer();
133
		$this->rootFolder = OC::$server->getLazyRootFolder();
134
		$this->userManager = OC::$server->getUserManager();
135
		$this->federationFactory = OC::$server->getCloudFederationFactory();
136
		$this->federationProviderManager = OC::$server->getCloudFederationProviderManager();
137
		$this->federationCloudIdManager = OC::$server->getCloudIdManager();
138
		$this->logger = OC::$server->getLogger();
139
		$this->urlGenerator = OC::$server->getURLGenerator();
140
		try {
141
			$this->defaults = OC::$server->query(Defaults::class);
142
			$this->sharesRequest = OC::$server->query(SharesRequest::class);
143
			$this->tokensRequest = OC::$server->query(TokensRequest::class);
144
			$this->configService = OC::$server->query(ConfigService::class);
145
			$this->miscService = OC::$server->query(MiscService::class);
146
		} catch (QueryException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\AppFramework\QueryException 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...
147
			OC::$server->getLogger()
148
					   ->log(1, 'Circles: cannot init FileSharingBroadcaster - ' . $e->getMessage());
149
		}
150
151
		try {
152
			$this->federationNotifications =
153
				OC::$server->query(Notifications::class);
154
			$this->federatedEnabled = true;
155
		} catch (QueryException $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\AppFramework\QueryException 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...
156
		}
157
158
	}
159
160
161
	/**
162
	 * {@inheritdoc}
163
	 */
164
	public function end() {
165
	}
166
167
168
	/**
169
	 * {@inheritdoc}
170
	 */
171
	public function createShareToCircle(SharingFrame $frame, Circle $circle) {
172
		if ($frame->is0Circle()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !$frame->is0Circle();.
Loading history...
173
			return false;
174
		}
175
176
		return true;
177
	}
178
179
180
	/**
181
	 * {@inheritdoc}
182
	 */
183
	public function deleteShareToCircle(SharingFrame $frame, Circle $circle) {
184
		return true;
185
	}
186
187
188
	/**
189
	 * {@inheritdoc}
190
	 */
191
	public function editShareToCircle(SharingFrame $frame, Circle $circle) {
192
		return true;
193
	}
194
195
196
	/**
197
	 * {@inheritdoc}
198
	 * @throws IllegalIDChangeException
199
	 */
200
	public function createShareToMember(SharingFrame $frame, Member $member) {
201
		if (!$frame->is0Circle()) {
202
			return false;
203
		}
204
205
		$payload = $frame->getPayload();
206
		if (!key_exists('share', $payload)) {
207
			return false;
208
		}
209
210
		$share = $this->generateShare($payload['share']);
211
		if ($member->getType() === Member::TYPE_MAIL || $member->getType() === Member::TYPE_CONTACT) {
212
			try {
213
				$circle = $frame->getCircle();
214
215
				// federated shared in contact
216
				$clouds = $this->getCloudsFromContact($member->getUserId());
217
				if ($this->federatedEnabled && !empty($clouds)) {
218
					$sent = false;
219
					foreach ($clouds as $cloudId) {
220
						$sharesToken = $this->tokensRequest->generateTokenForMember($member, $share->getId());
221
						if ($this->sharedByFederated($circle, $share, $cloudId, $sharesToken)) {
222
							$sent = true;
223
						}
224
					}
225
226
					if ($sent) {
227
						return true;
228
					}
229
				}
230
231
				$password = '';
0 ignored issues
show
Unused Code introduced by
$password is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
232
				if ($this->configService->enforcePasswordProtection()) {
233
					$password = $this->miscService->token(15);
0 ignored issues
show
Unused Code introduced by
$password is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
234
				}
235
236
				$sharesToken = $this->tokensRequest->generateTokenForMember($member, $share->getId());
237
				$mails = [$member->getUserId()];
238
				if ($member->getType() === Member::TYPE_CONTACT) {
239
					$mails = $this->getMailsFromContact($member->getUserId());
240
				}
241
242
				foreach ($mails as $mail) {
243
					$this->sharedByMail($circle, $share, $mail, $sharesToken);
244
				}
245
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
246
			}
247
		}
248
249
		return true;
250
	}
251
252
253
	/**
254
	 * {@inheritdoc}
255
	 */
256
	public function deleteShareToMember(SharingFrame $frame, Member $member) {
257
		return true;
258
	}
259
260
261
	/**
262
	 * {@inheritdoc}
263
	 */
264
	public function editShareToMember(SharingFrame $frame, Member $member) {
265
		return true;
266
	}
267
268
269
	/**
270
	 * @param Circle $circle
271
	 * @param Member $member
272
	 */
273
	public function sendMailAboutExistingShares(Circle $circle, Member $member) {
274
		if ($member->getType() !== Member::TYPE_MAIL && $member->getType() !== Member::TYPE_CONTACT
275
			&& $member->getContactId() !== '') {
276
			return;
277
		}
278
279
		$this->init();
280
281
		$allShares = $this->sharesRequest->getSharesForCircle($member->getCircleId());
282
		$knownShares = array_map(
283
			function(SharesToken $shareToken) {
284
				return $shareToken->getShareId();
285
			},
286
			$this->tokensRequest->getTokensFromMember($member)
287
		);
288
289
		$unknownShares = [];
290
		foreach ($allShares as $share) {
291
			if (!in_array($share['id'], $knownShares)) {
292
				$unknownShares[] = $share;
293
			}
294
		}
295
296
		$author = $circle->getViewer()
297
						 ->getUserId();
298
299
		$recipient = $member->getUserId();
300
		if ($member->getType() === Member::TYPE_CONTACT) {
301
			$emails = $this->getArray('EMAIL', MiscService::getContactData($member->getUserId()));
302
			if (empty($emails)) {
303
				return;
304
			}
305
306
			$recipient = $emails[0];
307
		}
308
309
		$this->sendMailExitingShares(
310
			$unknownShares, MiscService::getDisplay($author, Member::TYPE_USER), $member, $recipient,
311
			$circle->getName()
312
		);
313
	}
314
315
316
	/**
317
	 * recreate the share from the JSON payload.
318
	 *
319
	 * @param array $data
320
	 *
321
	 * @return IShare
322
	 * @throws IllegalIDChangeException
323
	 */
324
	private function generateShare($data): IShare {
325
		$this->logger->log(0, 'Regenerate shares from payload: ' . json_encode($data));
326
327
		$share = new Share($this->rootFolder, $this->userManager);
328
		$share->setId($data['id']);
329
		$share->setSharedBy($data['sharedBy']);
330
		$share->setSharedWith($data['sharedWith']);
331
		$share->setNodeId($data['nodeId']);
332
		$share->setShareOwner($data['shareOwner']);
333
		$share->setPermissions($data['permissions']);
334
		$share->setToken($data['token']);
335
		$share->setPassword($data['password']);
336
337
		return $share;
338
	}
339
340
341
	/**
342
	 * @param Circle $circle
343
	 * @param IShare $share
344
	 * @param string $address
345
	 * @param string $token
0 ignored issues
show
Documentation introduced by
Should the type for parameter $token not be SharesToken?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
346
	 *
347
	 * @return bool
348
	 */
349
	public function sharedByFederated(Circle $circle, IShare $share, string $address, SharesToken $token
0 ignored issues
show
Unused Code introduced by
The parameter $circle is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
350
	): bool {
351
		try {
352
			$cloudId = $this->federationCloudIdManager->resolveCloudId($address);
353
354
			$localUrl = $this->urlGenerator->getAbsoluteURL('/');
355
			$sharedByFederatedId = $share->getSharedBy();
356
			if ($this->userManager->userExists($sharedByFederatedId)) {
357
				$cloudId = $this->federationCloudIdManager->getCloudId($sharedByFederatedId, $localUrl);
358
				$sharedByFederatedId = $cloudId->getId();
359
			}
360
			$ownerCloudId = $this->federationCloudIdManager->getCloudId($share->getShareOwner(), $localUrl);
361
362
			$send = $this->federationNotifications->sendRemoteShare(
363
				$token->getToken(),
364
				$address,
365
				$share->getNode()
366
					  ->getName(),
367
				$share->getId(),
368
				$share->getShareOwner(),
369
				$ownerCloudId->getId(),
370
				$share->getSharedBy(),
371
				$sharedByFederatedId,
372
				Share::TYPE_USER
373
			);
374
375
			return $send;
376
		} catch (\Exception $e) {
377
			$this->logger->logException(
378
				$e, [
379
					  'message' => 'Failed to notify remote server of circles-federated share',
380
					  'level'   => ILogger::ERROR,
381
					  'app'     => 'circles',
382
				  ]
383
			);
384
		}
385
386
		return false;
387
	}
388
389
390
	/**
391
	 * @param Circle $circle
392
	 * @param IShare $share
393
	 * @param string $email
394
	 * @param SharesToken $sharesToken
395
	 */
396
	private function sharedByMail(Circle $circle, IShare $share, $email, SharesToken $sharesToken) {
397
		// genelink
398
		$link = $this->urlGenerator->linkToRouteAbsolute(
399
			'files_sharing.sharecontroller.showShare',
400
			['token' => $sharesToken->getToken()]
401
		);
402
403
		try {
404
			$this->sendMail(
405
				$share->getNode()
406
					  ->getName(), $link,
407
				MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
408
				$circle->getName(), $email
409
			);
410
			$this->sendPasswordByMail(
411
				$share, MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
412
				$email, $sharesToken->getOrigPassword()
413
			);
414
		} catch (Exception $e) {
415
			OC::$server->getLogger()
416
					   ->log(1, 'Circles::sharedByMail - mail were not sent: ' . $e->getMessage());
417
		}
418
	}
419
420
421
	/**
422
	 * @param $fileName
423
	 * @param string $link
424
	 * @param string $author
425
	 * @param $circleName
426
	 * @param string $email
427
	 *
428
	 * @throws Exception
429
	 */
430
	protected function sendMail($fileName, $link, $author, $circleName, $email) {
431
		$message = $this->mailer->createMessage();
432
433
		$this->logger->log(
434
			0, "Sending mail to circle '" . $circleName . "': " . $email . ' file: ' . $fileName
435
			   . ' - link: ' . $link
436
		);
437
438
		$subject = $this->l10n->t('%s shared »%s« with you.', [$author, $fileName]);
439
		$text = $this->l10n->t('%s shared »%s« with \'%s\'.', [$author, $fileName, $circleName]);
440
441
		$emailTemplate =
442
			$this->generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName);
443
444
		$instanceName = $this->defaults->getName();
445
		$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
446
447
		$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
448
		$message->setSubject($subject);
449
		$message->setPlainBody($emailTemplate->renderText());
450
		$message->setHtmlBody($emailTemplate->renderHtml());
451
		$message->setTo([$email]);
452
453
		$this->mailer->send($message);
454
	}
455
456
457
	/**
458
	 * @param IShare $share
459
	 * @param string $circleName
460
	 * @param string $email
461
	 *
462
	 * @param $password
463
	 *
464
	 * @throws NotFoundException
465
	 * @throws Exception
466
	 */
467
	protected function sendPasswordByMail(IShare $share, $circleName, $email, $password) {
468
		if (!$this->configService->sendPasswordByMail() || $password === '') {
469
			return;
470
		}
471
472
		$message = $this->mailer->createMessage();
473
474
		$this->logger->log(0, "Sending password mail to circle '" . $circleName . "': " . $email);
475
476
		$filename = $share->getNode()
477
						  ->getName();
478
		$initiator = $share->getSharedBy();
479
		$shareWith = $share->getSharedWith();
480
481
		$initiatorUser = $this->userManager->get($initiator);
482
		$initiatorDisplayName =
483
			($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
484
		$initiatorEmailAddress =
485
			($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
486
487
		$plainBodyPart = $this->l10n->t(
488
			"%1\$s shared »%2\$s« with you.\nYou should have already received a separate mail with a link to access it.\n",
489
			[$initiatorDisplayName, $filename]
490
		);
491
		$htmlBodyPart = $this->l10n->t(
492
			'%1$s shared »%2$s« with you. You should have already received a separate mail with a link to access it.',
493
			[$initiatorDisplayName, $filename]
494
		);
495
496
		$emailTemplate = $this->mailer->createEMailTemplate(
497
			'sharebymail.RecipientPasswordNotification', [
498
														   'filename'       => $filename,
499
														   'password'       => $password,
500
														   'initiator'      => $initiatorDisplayName,
501
														   'initiatorEmail' => $initiatorEmailAddress,
502
														   'shareWith'      => $shareWith,
503
													   ]
504
		);
505
506
		$emailTemplate->setSubject(
507
			$this->l10n->t(
508
				'Password to access »%1$s« shared to you by %2$s', [$filename, $initiatorDisplayName]
509
			)
510
		);
511
		$emailTemplate->addHeader();
512
		$emailTemplate->addHeading($this->l10n->t('Password to access »%s«', [$filename]), false);
513
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
514
		$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
515
		$emailTemplate->addBodyText($password);
516
517
		// The "From" contains the sharers name
518
		$instanceName = $this->defaults->getName();
519
		$senderName = $this->l10n->t(
520
			'%1$s via %2$s',
521
			[
522
				$initiatorDisplayName,
523
				$instanceName
524
			]
525
		);
526
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
527 View Code Duplication
		if ($initiatorEmailAddress !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
528
			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
529
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
530
		} else {
531
			$emailTemplate->addFooter();
532
		}
533
534
		$message->setTo([$email]);
535
		$message->useTemplate($emailTemplate);
536
		$this->mailer->send($message);
537
	}
538
539
540
	/**
541
	 * @param $subject
542
	 * @param $text
543
	 * @param $fileName
544
	 * @param $link
545
	 * @param string $author
546
	 * @param string $circleName
547
	 *
548
	 * @return IEMailTemplate
549
	 */
550
	private function generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName
551
	) {
552
		$emailTemplate = $this->mailer->createEMailTemplate(
553
			'circles.ShareNotification', [
554
										   'fileName'   => $fileName,
555
										   'fileLink'   => $link,
556
										   'author'     => $author,
557
										   'circleName' => $circleName,
558
									   ]
559
		);
560
561
		$emailTemplate->addHeader();
562
		$emailTemplate->addHeading($subject, false);
563
		$emailTemplate->addBodyText(
564
			htmlspecialchars($text) . '<br>' . htmlspecialchars(
565
				$this->l10n->t('Click the button below to open it.')
566
			), $text
567
		);
568
		$emailTemplate->addBodyButton(
569
			$this->l10n->t('Open »%s«', [htmlspecialchars($fileName)]), $link
570
		);
571
572
		return $emailTemplate;
573
	}
574
575
576
	/**
577
	 * @param array $unknownShares
578
	 * @param string $author
579
	 * @param Member $member
580
	 * @param string $recipient
581
	 * @param string $circleName
582
	 * @param string $password
0 ignored issues
show
Bug introduced by
There is no parameter named $password. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
583
	 */
584
	public function sendMailExitingShares(
585
		array $unknownShares, $author, Member $member, $recipient, $circleName
586
	) {
587
		$data = [];
588
589
		$password = '';
590
		foreach ($unknownShares as $share) {
591
			try {
592
				$item = $this->getMailLinkFromShare($share, $member, $password);
593
				$password = $item['password'];
594
				$data[] = $item;
595
			} catch (TokenDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
596
			}
597
		}
598
599
		if (sizeof($data) === 0) {
600
			return;
601
		}
602
603
		try {
604
			$template = $this->generateMailExitingShares($author, $circleName);
605
			$this->fillMailExistingShares($template, $data);
606
			$this->sendMailExistingShares($template, $author, $recipient);
607
			$this->sendPasswordExistingShares($author, $recipient, $password);
608
		} catch (Exception $e) {
609
			$this->logger->log(2, 'Failed to send mail about existing share ' . $e->getMessage());
610
		}
611
	}
612
613
614
	/**
615
	 * @param $author
616
	 * @param string $email
617
	 *
618
	 * @param $password
619
	 *
620
	 * @throws Exception
621
	 */
622
	protected function sendPasswordExistingShares($author, $email, $password) {
623
		if (!$this->configService->sendPasswordByMail() || $password === '') {
624
			return;
625
		}
626
627
		$message = $this->mailer->createMessage();
628
629
		$authorUser = $this->userManager->get($author);
630
		$authorName = ($authorUser instanceof IUser) ? $authorUser->getDisplayName() : $author;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
631
		$authorEmail = ($authorUser instanceof IUser) ? $authorUser->getEMailAddress() : null;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
632
633
		$this->logger->log(0, "Sending password mail about existing files to '" . $email . "'");
634
635
		$plainBodyPart = $this->l10n->t(
636
			"%1\$s shared multiple files with you.\nYou should have already received a separate mail with a link to access them.\n",
637
			[$authorName]
638
		);
639
		$htmlBodyPart = $this->l10n->t(
640
			'%1$s shared multiple files with you. You should have already received a separate mail with a link to access them.',
641
			[$authorName]
642
		);
643
644
		$emailTemplate = $this->mailer->createEMailTemplate(
645
			'sharebymail.RecipientPasswordNotification', [
646
														   'password' => $password,
647
														   'author'   => $author
648
													   ]
649
		);
650
651
		$emailTemplate->setSubject(
652
			$this->l10n->t(
653
				'Password to access files shared to you by %1$s', [$authorName]
654
			)
655
		);
656
		$emailTemplate->addHeader();
657
		$emailTemplate->addHeading($this->l10n->t('Password to access files'), false);
658
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
659
		$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
660
		$emailTemplate->addBodyText($password);
661
662
		// The "From" contains the sharers name
663
		$instanceName = $this->defaults->getName();
664
		$senderName = $this->l10n->t(
665
			'%1$s via %2$s',
666
			[
667
				$authorName,
668
				$instanceName
669
			]
670
		);
671
672
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
673 View Code Duplication
		if ($authorEmail !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
674
			$message->setReplyTo([$authorEmail => $authorName]);
675
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
676
		} else {
677
			$emailTemplate->addFooter();
678
		}
679
680
		$message->setTo([$email]);
681
		$message->useTemplate($emailTemplate);
682
		$this->mailer->send($message);
683
	}
684
685
	/**
686
	 * @param array $share
687
	 * @param Member $member
688
	 * @param string $password
689
	 * @param string $token
0 ignored issues
show
Bug introduced by
There is no parameter named $token. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
690
	 *
691
	 * @return array
692
	 * @throws TokenDoesNotExistException
693
	 */
694
	private function getMailLinkFromShare(array $share, Member $member, string $password = '') {
695
		$sharesToken = $this->tokensRequest->generateTokenForMember($member, $share['id'], $password);
696
		$link = $this->urlGenerator->linkToRouteAbsolute(
697
			'files_sharing.sharecontroller.showShare',
698
			['token' => $sharesToken->getToken()]
699
		);
700
		$author = $share['uid_initiator'];
701
702
		$filename = basename($share['file_target']);
703
704
		return [
705
			'author'   => $author,
706
			'link'     => $link,
707
			'password' => $sharesToken->getOrigPassword(),
708
			'filename' => $filename
709
		];
710
	}
711
712
713
	/**
714
	 * @param $author
715
	 * @param string $circleName
716
	 *
717
	 * @return IEMailTemplate
718
	 * @throws Exception
719
	 */
720
	protected function generateMailExitingShares($author, $circleName) {
721
		$this->logger->log(
722
			0, "Generating mail about existing share mail from '" . $author . "' in "
723
			   . $circleName
724
		);
725
726
		$emailTemplate = $this->mailer->createEMailTemplate('circles.ExistingShareNotification', []);
727
		$emailTemplate->addHeader();
728
729
		$text = $this->l10n->t('%s shared multiple files with \'%s\'.', [$author, $circleName]);
730
		$emailTemplate->addBodyText(htmlspecialchars($text), $text);
731
732
		return $emailTemplate;
733
	}
734
735
736
	/**
737
	 * @param IEMailTemplate $emailTemplate
738
	 * @param array $data
739
	 */
740
	protected function fillMailExistingShares(IEMailTemplate $emailTemplate, array $data) {
741
		foreach ($data as $item) {
742
//			$text = $this->l10n->t('%s shared »%s« with you.', [$item['author'], $item['filename']]);
743
//			$emailTemplate->addBodyText(
744
//				htmlspecialchars($text) . '<br>' . htmlspecialchars(
745
//					$this->l10n->t('Click the button below to open it.')
746
//				), $text
747
//			);
748
			$emailTemplate->addBodyButton(
749
				$this->l10n->t('Open »%s«', [htmlspecialchars($item['filename'])]), $item['link']
750
			);
751
		}
752
	}
753
754
755
	/**
756
	 * @param IEMailTemplate $emailTemplate
757
	 * @param $author
758
	 * @param $recipient
759
	 *
760
	 * @throws Exception
761
	 */
762
	protected function sendMailExistingShares(IEMailTemplate $emailTemplate, $author, $recipient) {
763
		$subject = $this->l10n->t('%s shared multiple files with you.', [$author]);
764
//		$emailTemplate->addHeading($subject, false);
765
766
		$instanceName = $this->defaults->getName();
767
		$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
768
769
		$message = $this->mailer->createMessage();
770
771
		$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
772
		$message->setSubject($subject);
773
		$message->setPlainBody($emailTemplate->renderText());
774
		$message->setHtmlBody($emailTemplate->renderHtml());
775
		$message->setTo([$recipient]);
776
777
		$this->mailer->send($message);
778
	}
779
780
781
	/**
782
	 * @param string $contactId
783
	 *
784
	 * @return array
785
	 */
786 View Code Duplication
	private function getCloudsFromContact(string $contactId): array {
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...
787
		$contact = MiscService::getContactData($contactId);
788
		if (!key_exists('CLOUD', $contact)) {
789
			return [];
790
		}
791
792
		return $contact['CLOUD'];
793
	}
794
795
	/**
796
	 * @param string $contactId
797
	 *
798
	 * @return array
799
	 */
800 View Code Duplication
	private function getMailsFromContact(string $contactId): array {
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...
801
		$contact = MiscService::getContactData($contactId);
802
		if (!key_exists('EMAIL', $contact)) {
803
			return [];
804
		}
805
806
		return $contact['EMAIL'];
807
	}
808
809
}
810