Completed
Pull Request — master (#330)
by Maxence
01:30
created

FileSharingBroadcaster::sendPasswordByMail()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 66
rs 8.7418
c 0
b 0
f 0
cc 4
nc 8
nop 3

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
 * 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 Exception;
31
use OC;
32
use OC\Share20\Share;
33
use OCA\Circles\AppInfo\Application;
34
use OCA\Circles\Db\TokensRequest;
35
use OCA\Circles\Exceptions\TokenDoesNotExistException;
36
use OCA\Circles\IBroadcaster;
37
use OCA\Circles\Model\Circle;
38
use OCA\Circles\Model\Member;
39
use OCA\Circles\Model\SharingFrame;
40
use OCA\Circles\Service\ConfigService;
41
use OCA\Circles\Service\MiscService;
42
use OCP\AppFramework\QueryException;
43
use OCP\Defaults;
44
use OCP\Files\IRootFolder;
45
use OCP\Files\NotFoundException;
46
use OCP\IL10N;
47
use OCP\ILogger;
48
use OCP\IURLGenerator;
49
use OCP\IUser;
50
use OCP\IUserManager;
51
use OCP\Mail\IMailer;
52
use OCP\Share\Exceptions\IllegalIDChangeException;
53
use OCP\Share\IShare;
54
use OCP\Util;
55
56
57
class FileSharingBroadcaster implements IBroadcaster {
58
59
	/** @var IL10N */
60
	private $l10n = null;
61
62
	/** @var IMailer */
63
	private $mailer;
64
65
	/** @var IRootFolder */
66
	private $rootFolder;
67
68
	/** @var IUserManager */
69
	private $userManager;
70
71
	/** @var ILogger */
72
	private $logger;
73
74
	/** @var Defaults */
75
	private $defaults;
76
77
	/** @var IURLGenerator */
78
	private $urlGenerator;
79
80
	/** @var TokensRequest */
81
	private $tokensRequest;
82
83
	/** @var ConfigService */
84
	private $configService;
85
86
87
	/**
88
	 * {@inheritdoc}
89
	 */
90
	public function init() {
91
		$this->l10n = OC::$server->getL10N(Application::APP_NAME);
92
		$this->mailer = OC::$server->getMailer();
93
		$this->rootFolder = OC::$server->getLazyRootFolder();
94
		$this->userManager = OC::$server->getUserManager();
95
		$this->logger = OC::$server->getLogger();
96
		$this->urlGenerator = OC::$server->getURLGenerator();
97
		try {
98
			$this->defaults = OC::$server->query(Defaults::class);
99
			$this->tokensRequest = OC::$server->query(TokensRequest::class);
100
			$this->configService = OC::$server->query(ConfigService::class);
101
		} 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...
102
		}
103
	}
104
105
106
	/**
107
	 * {@inheritdoc}
108
	 */
109
	public function end() {
110
	}
111
112
113
	/**
114
	 * {@inheritdoc}
115
	 */
116
	public function createShareToCircle(SharingFrame $frame, Circle $circle) {
117
		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...
118
			return false;
119
		}
120
121
		return true;
122
	}
123
124
125
	/**
126
	 * {@inheritdoc}
127
	 */
128
	public function deleteShareToCircle(SharingFrame $frame, Circle $circle) {
129
		return true;
130
	}
131
132
133
	/**
134
	 * {@inheritdoc}
135
	 */
136
	public function editShareToCircle(SharingFrame $frame, Circle $circle) {
137
		return true;
138
	}
139
140
141
	/**
142
	 * {@inheritdoc}
143
	 */
144
	public function createShareToMember(SharingFrame $frame, Member $member) {
145
		if (!$frame->is0Circle()) {
146
			return false;
147
		}
148
149
		$payload = $frame->getPayload();
150
		if (!key_exists('share', $payload)) {
151
			return false;
152
		}
153
154
		$share = $this->generateShare($payload['share']);
155
		if ($member->getType() === Member::TYPE_MAIL) {
156
			try {
157
				$circle = $frame->getCircle();
158
				$token = $this->tokensRequest->generateTokenForMember($member, $share->getId());
159
				if ($token !== '') {
160
					$this->sharedByMail($circle, $share, $member->getUserId(), $token);
161
				}
162
			} catch (TokenDoesNotExistException $e) {
163
			} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException 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...
164
			}
165
		}
166
167
		return true;
168
	}
169
170
171
	/**
172
	 * {@inheritdoc}
173
	 */
174
	public function deleteShareToMember(SharingFrame $frame, Member $member) {
175
		return true;
176
	}
177
178
179
	/**
180
	 * {@inheritdoc}
181
	 */
182
	public function editShareToMember(SharingFrame $frame, Member $member) {
183
		return true;
184
	}
185
186
187
	/**
188
	 * recreate the share from the JSON payload.
189
	 *
190
	 * @param array $data
191
	 *
192
	 * @return IShare
0 ignored issues
show
Documentation introduced by
Should the return type not be Share?

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

Loading history...
193
	 * @throws IllegalIDChangeException
194
	 */
195
	private function generateShare($data) {
196
		$this->logger->log(0, 'Regenerate shares from payload: ' . json_encode($data));
197
198
		$share = new Share($this->rootFolder, $this->userManager);
199
		$share->setId($data['id']);
200
		$share->setSharedBy($data['sharedBy']);
201
		$share->setSharedWith($data['sharedWith']);
202
		$share->setNodeId($data['nodeId']);
203
		$share->setShareOwner($data['shareOwner']);
204
		$share->setPermissions($data['permissions']);
205
		$share->setToken($data['token']);
206
		$share->setPassword($data['password']);
207
208
		return $share;
209
	}
210
211
212
	/**
213
	 * @param Circle $circle
214
	 * @param IShare $share
215
	 * @param string $email
216
	 * @param string $token
217
	 *
218
	 * @throws NotFoundException
219
	 */
220
	private function sharedByMail(Circle $circle, IShare $share, $email, $token) {
221
		// genelink
222
		$link = $this->urlGenerator->linkToRouteAbsolute(
223
			'circles.Shares.public',
224
			['token' => $token]
225
		);
226
227
		try {
228
			$this->sendMail(
229
				$share->getNode()
230
					  ->getName(), $link,
231
				MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
232
				$circle->getName(), $email
233
			);
234
235
			if ($this->configService->sendPasswordByMail() && $share->getPassword() !== '') {
236
				$this->sendPasswordByMail(
237
					$share, MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
238
					$email
239
				);
240
			}
241
		} catch (Exception $e) {
242
			OC::$server->getLogger()
243
					   ->log(1, 'Circles::sharedByMail - mail were not sent: ' . $e->getMessage());
244
		}
245
	}
246
247
248
	/**
249
	 * @param $fileName
250
	 * @param string $link
251
	 * @param string $author
252
	 * @param $circleName
253
	 * @param string $email
254
	 *
255
	 * @throws Exception
256
	 */
257
	protected function sendMail($fileName, $link, $author, $circleName, $email) {
258
		$message = $this->mailer->createMessage();
259
260
		$this->logger->log(
261
			0, "Sending mail to circle '" . $circleName . "': " . $email . ' file: ' . $fileName
262
			   . ' - link: ' . $link
263
		);
264
265
		$subject = $this->l10n->t('%s shared »%s« with you.', [$author, $fileName]);
266
		$text = $this->l10n->t('%s shared »%s« with \'%s\'.', [$author, $fileName, $circleName]);
267
268
		$emailTemplate =
269
			$this->generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName);
270
271
		$instanceName = $this->defaults->getName();
272
		$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
273
274
		$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
275
		$message->setSubject($subject);
276
		$message->setPlainBody($emailTemplate->renderText());
277
		$message->setHtmlBody($emailTemplate->renderHtml());
278
		$message->setTo([$email]);
279
280
		$this->mailer->send($message);
281
	}
282
283
284
	/**
285
	 * @param IShare $share
286
	 * @param string $circleName
287
	 * @param string $email
288
	 *
289
	 * @param $password
290
	 *
291
	 * @throws NotFoundException
292
	 * @throws Exception
293
	 */
294
	protected function sendPasswordByMail(IShare $share, $circleName, $email) {
295
		$message = $this->mailer->createMessage();
296
297
		$this->logger->log(0, "Sending password mail to circle '" . $circleName . "': " . $email);
298
299
		$filename = $share->getNode()
300
						  ->getName();
301
		$initiator = $share->getSharedBy();
302
		$shareWith = $share->getSharedWith();
303
304
		$initiatorUser = $this->userManager->get($initiator);
305
		$initiatorDisplayName =
306
			($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...
307
		$initiatorEmailAddress = ($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...
308
309
		$plainBodyPart = $this->l10n->t(
310
			"%1\$s shared »%2\$s« with you.\nYou should have already received a separate mail with a link to access it.\n",
311
			[$initiatorDisplayName, $filename]
312
		);
313
		$htmlBodyPart = $this->l10n->t(
314
			'%1$s shared »%2$s« with you. You should have already received a separate mail with a link to access it.',
315
			[$initiatorDisplayName, $filename]
316
		);
317
318
		$emailTemplate = $this->mailer->createEMailTemplate(
319
			'sharebymail.RecipientPasswordNotification', [
320
														   'filename'       => $filename,
321
														   'password'       => $share->getPassword(),
322
														   'initiator'      => $initiatorDisplayName,
323
														   'initiatorEmail' => $initiatorEmailAddress,
324
														   'shareWith'      => $shareWith,
325
													   ]
326
		);
327
328
		$emailTemplate->setSubject(
329
			$this->l10n->t(
330
				'Password to access »%1$s« shared to you by %2$s', [$filename, $initiatorDisplayName]
331
			)
332
		);
333
		$emailTemplate->addHeader();
334
		$emailTemplate->addHeading($this->l10n->t('Password to access »%s«', [$filename]), false);
335
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
336
		$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
337
		$emailTemplate->addBodyText($share->getPassword());
338
339
		// The "From" contains the sharers name
340
		$instanceName = $this->defaults->getName();
341
		$senderName = $this->l10n->t(
342
			'%1$s via %2$s',
343
			[
344
				$initiatorDisplayName,
345
				$instanceName
346
			]
347
		);
348
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
349
		if ($initiatorEmailAddress !== null) {
350
			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
351
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
352
		} else {
353
			$emailTemplate->addFooter();
354
		}
355
356
		$message->setTo([$email]);
357
		$message->useTemplate($emailTemplate);
358
		$this->mailer->send($message);
359
	}
360
361
362
	/**
363
	 * @param $subject
364
	 * @param $text
365
	 * @param $fileName
366
	 * @param $link
367
	 * @param string $author
368
	 * @param string $circleName
369
	 *
370
	 * @return \OCP\Mail\IEMailTemplate
371
	 */
372
	private function generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName
373
	) {
374
375
		$emailTemplate = $this->mailer->createEMailTemplate(
376
			'circles.ShareNotification', [
377
										   'fileName'   => $fileName,
378
										   'fileLink'   => $link,
379
										   'author'     => $author,
380
										   'circleName' => $circleName,
381
									   ]
382
		);
383
384
		$emailTemplate->addHeader();
385
		$emailTemplate->addHeading($subject, false);
386
		$emailTemplate->addBodyText(
387
			htmlspecialchars($text) . '<br>' . htmlspecialchars(
388
				$this->l10n->t('Click the button below to open it.')
389
			), $text
390
		);
391
		$emailTemplate->addBodyButton(
392
			$this->l10n->t('Open »%s«', [htmlspecialchars($fileName)]), $link
393
		);
394
395
		return $emailTemplate;
396
	}
397
}
398