Completed
Pull Request — master (#331)
by Maxence
01:41
created

FileSharingBroadcaster::deleteShareToCircle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 2
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
	/** @var MiscService */
87
	private $miscService;
88
89
90
	/**
91
	 * {@inheritdoc}
92
	 */
93
	public function init() {
94
		$this->l10n = OC::$server->getL10N(Application::APP_NAME);
95
		$this->mailer = OC::$server->getMailer();
96
		$this->rootFolder = OC::$server->getLazyRootFolder();
97
		$this->userManager = OC::$server->getUserManager();
98
		$this->logger = OC::$server->getLogger();
99
		$this->urlGenerator = OC::$server->getURLGenerator();
100
		try {
101
			$this->defaults = OC::$server->query(Defaults::class);
102
			$this->tokensRequest = OC::$server->query(TokensRequest::class);
103
			$this->configService = OC::$server->query(ConfigService::class);
104
			$this->miscService = OC::$server->query(MiscService::class);
105
		} 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...
106
		}
107
	}
108
109
110
	/**
111
	 * {@inheritdoc}
112
	 */
113
	public function end() {
114
	}
115
116
117
	/**
118
	 * {@inheritdoc}
119
	 */
120
	public function createShareToCircle(SharingFrame $frame, Circle $circle) {
121
		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...
122
			return false;
123
		}
124
125
		return true;
126
	}
127
128
129
	/**
130
	 * {@inheritdoc}
131
	 */
132
	public function deleteShareToCircle(SharingFrame $frame, Circle $circle) {
133
		return true;
134
	}
135
136
137
	/**
138
	 * {@inheritdoc}
139
	 */
140
	public function editShareToCircle(SharingFrame $frame, Circle $circle) {
141
		return true;
142
	}
143
144
145
	/**
146
	 * {@inheritdoc}
147
	 * @throws IllegalIDChangeException
148
	 */
149
	public function createShareToMember(SharingFrame $frame, Member $member) {
150
		if (!$frame->is0Circle()) {
151
			return false;
152
		}
153
154
		$payload = $frame->getPayload();
155
		if (!key_exists('share', $payload)) {
156
			return false;
157
		}
158
159
		$share = $this->generateShare($payload['share']);
160
		if ($member->getType() === Member::TYPE_MAIL || $member->getType() === Member::TYPE_CONTACT) {
161
			try {
162
				$circle = $frame->getCircle();
163
				$password = '';
164
165
				if ($this->configService->enforcePasswordProtection()) {
166
					$password = $this->miscService->uuid(15);
167
				}
168
				$token = $this->tokensRequest->generateTokenForMember($member, $share->getId(), $password);
169
				if ($token !== '') {
170
					$this->sharedByMail($circle, $share, $member->getUserId(), $token, $password);
171
				}
172
			} catch (TokenDoesNotExistException $e) {
173
			} 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...
174
			}
175
		}
176
177
		return true;
178
	}
179
180
181
	/**
182
	 * {@inheritdoc}
183
	 */
184
	public function deleteShareToMember(SharingFrame $frame, Member $member) {
185
		return true;
186
	}
187
188
189
	/**
190
	 * {@inheritdoc}
191
	 */
192
	public function editShareToMember(SharingFrame $frame, Member $member) {
193
		return true;
194
	}
195
196
197
	/**
198
	 * recreate the share from the JSON payload.
199
	 *
200
	 * @param array $data
201
	 *
202
	 * @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...
203
	 * @throws IllegalIDChangeException
204
	 */
205
	private function generateShare($data) {
206
		$this->logger->log(0, 'Regenerate shares from payload: ' . json_encode($data));
207
208
		$share = new Share($this->rootFolder, $this->userManager);
209
		$share->setId($data['id']);
210
		$share->setSharedBy($data['sharedBy']);
211
		$share->setSharedWith($data['sharedWith']);
212
		$share->setNodeId($data['nodeId']);
213
		$share->setShareOwner($data['shareOwner']);
214
		$share->setPermissions($data['permissions']);
215
		$share->setToken($data['token']);
216
		$share->setPassword($data['password']);
217
218
		return $share;
219
	}
220
221
222
	/**
223
	 * @param Circle $circle
224
	 * @param IShare $share
225
	 * @param string $email
226
	 * @param string $token
227
	 * @param string $password
228
	 */
229
	private function sharedByMail(Circle $circle, IShare $share, $email, $token, $password) {
230
		// genelink
231
		$link = $this->urlGenerator->linkToRouteAbsolute(
232
			'files_sharing.sharecontroller.showShare',
233
			['token' => $token]
234
		);
235
236
		try {
237
			$this->sendMail(
238
				$share->getNode()
239
					  ->getName(), $link,
240
				MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
241
				$circle->getName(), $email
242
			);
243
			if ($this->configService->sendPasswordByMail() && $password !== '') {
244
				$this->sendPasswordByMail(
245
					$share, MiscService::getDisplay($share->getSharedBy(), Member::TYPE_USER),
246
					$email, $password
247
				);
248
			}
249
		} catch (Exception $e) {
250
			OC::$server->getLogger()
251
					   ->log(1, 'Circles::sharedByMail - mail were not sent: ' . $e->getMessage());
252
		}
253
	}
254
255
256
	/**
257
	 * @param $fileName
258
	 * @param string $link
259
	 * @param string $author
260
	 * @param $circleName
261
	 * @param string $email
262
	 *
263
	 * @throws Exception
264
	 */
265
	protected function sendMail($fileName, $link, $author, $circleName, $email) {
266
		$message = $this->mailer->createMessage();
267
268
		$this->logger->log(
269
			0, "Sending mail to circle '" . $circleName . "': " . $email . ' file: ' . $fileName
270
			   . ' - link: ' . $link
271
		);
272
273
		$subject = $this->l10n->t('%s shared »%s« with you.', [$author, $fileName]);
274
		$text = $this->l10n->t('%s shared »%s« with \'%s\'.', [$author, $fileName, $circleName]);
275
276
		$emailTemplate =
277
			$this->generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName);
278
279
		$instanceName = $this->defaults->getName();
280
		$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
281
282
		$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
283
		$message->setSubject($subject);
284
		$message->setPlainBody($emailTemplate->renderText());
285
		$message->setHtmlBody($emailTemplate->renderHtml());
286
		$message->setTo([$email]);
287
288
		$this->mailer->send($message);
289
	}
290
291
292
	/**
293
	 * @param IShare $share
294
	 * @param string $circleName
295
	 * @param string $email
296
	 *
297
	 * @param $password
298
	 *
299
	 * @throws NotFoundException
300
	 * @throws Exception
301
	 */
302
	protected function sendPasswordByMail(IShare $share, $circleName, $email, $password) {
303
		$message = $this->mailer->createMessage();
304
305
		$this->logger->log(0, "Sending password mail to circle '" . $circleName . "': " . $email);
306
307
		$filename = $share->getNode()
308
						  ->getName();
309
		$initiator = $share->getSharedBy();
310
		$shareWith = $share->getSharedWith();
311
312
		$initiatorUser = $this->userManager->get($initiator);
313
		$initiatorDisplayName =
314
			($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...
315
		$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...
316
317
		$plainBodyPart = $this->l10n->t(
318
			"%1\$s shared »%2\$s« with you.\nYou should have already received a separate mail with a link to access it.\n",
319
			[$initiatorDisplayName, $filename]
320
		);
321
		$htmlBodyPart = $this->l10n->t(
322
			'%1$s shared »%2$s« with you. You should have already received a separate mail with a link to access it.',
323
			[$initiatorDisplayName, $filename]
324
		);
325
326
		$emailTemplate = $this->mailer->createEMailTemplate(
327
			'sharebymail.RecipientPasswordNotification', [
328
														   'filename'       => $filename,
329
														   'password'       => $password,
330
														   'initiator'      => $initiatorDisplayName,
331
														   'initiatorEmail' => $initiatorEmailAddress,
332
														   'shareWith'      => $shareWith,
333
													   ]
334
		);
335
336
		$emailTemplate->setSubject(
337
			$this->l10n->t(
338
				'Password to access »%1$s« shared to you by %2$s', [$filename, $initiatorDisplayName]
339
			)
340
		);
341
		$emailTemplate->addHeader();
342
		$emailTemplate->addHeading($this->l10n->t('Password to access »%s«', [$filename]), false);
343
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
344
		$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
345
		$emailTemplate->addBodyText($password);
346
347
		// The "From" contains the sharers name
348
		$instanceName = $this->defaults->getName();
349
		$senderName = $this->l10n->t(
350
			'%1$s via %2$s',
351
			[
352
				$initiatorDisplayName,
353
				$instanceName
354
			]
355
		);
356
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
357
		if ($initiatorEmailAddress !== null) {
358
			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
359
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
360
		} else {
361
			$emailTemplate->addFooter();
362
		}
363
364
		$message->setTo([$email]);
365
		$message->useTemplate($emailTemplate);
366
		$this->mailer->send($message);
367
	}
368
369
370
	/**
371
	 * @param $subject
372
	 * @param $text
373
	 * @param $fileName
374
	 * @param $link
375
	 * @param string $author
376
	 * @param string $circleName
377
	 *
378
	 * @return \OCP\Mail\IEMailTemplate
379
	 */
380
	private function generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName
381
	) {
382
383
		$emailTemplate = $this->mailer->createEMailTemplate(
384
			'circles.ShareNotification', [
385
										   'fileName'   => $fileName,
386
										   'fileLink'   => $link,
387
										   'author'     => $author,
388
										   'circleName' => $circleName,
389
									   ]
390
		);
391
392
		$emailTemplate->addHeader();
393
		$emailTemplate->addHeading($subject, false);
394
		$emailTemplate->addBodyText(
395
			htmlspecialchars($text) . '<br>' . htmlspecialchars(
396
				$this->l10n->t('Click the button below to open it.')
397
			), $text
398
		);
399
		$emailTemplate->addBodyButton(
400
			$this->l10n->t('Open »%s«', [htmlspecialchars($fileName)]), $link
401
		);
402
403
		return $emailTemplate;
404
	}
405
}
406