Completed
Push — master ( 695e32...226e63 )
by Joas
26:45 queued 10:02
created
apps/sharebymail/lib/ShareByMailProvider.php 1 patch
Indentation   +1018 added lines, -1018 removed lines patch added patch discarded remove patch
@@ -53,1036 +53,1036 @@
 block discarded – undo
53 53
  */
54 54
 class ShareByMailProvider implements IShareProvider {
55 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
-		/*
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 163
 		 * Check if file is not already shared with the remote user
164 164
 		 */
165
-		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
-		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
-	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->logException($hintException, [
355
-				'message' => 'Failed to send share by mail.',
356
-				'level' => \OCP\Util::ERROR,
357
-				'app' => 'sharebymail',
358
-			]);
359
-			$this->removeShareFromTable($shareId);
360
-			throw $hintException;
361
-		} catch (\Exception $e) {
362
-			$this->logger->logException($e, [
363
-				'message' => 'Failed to send share by mail.',
364
-				'level' => \OCP\Util::ERROR,
365
-				'app' => 'sharebymail',
366
-			]);
367
-			$this->removeShareFromTable($shareId);
368
-			throw new HintException('Failed to send share by mail',
369
-				$this->l->t('Failed to send share by email'));
370
-		}
371
-
372
-		return $shareId;
373
-
374
-	}
375
-
376
-	/**
377
-	 * @param string $filename
378
-	 * @param string $link
379
-	 * @param string $initiator
380
-	 * @param string $shareWith
381
-	 * @param \DateTime|null $expiration
382
-	 * @throws \Exception If mail couldn't be sent
383
-	 */
384
-	protected function sendMailNotification($filename,
385
-											$link,
386
-											$initiator,
387
-											$shareWith,
388
-											\DateTime $expiration = null) {
389
-		$initiatorUser = $this->userManager->get($initiator);
390
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
391
-		$message = $this->mailer->createMessage();
392
-
393
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
394
-			'filename' => $filename,
395
-			'link' => $link,
396
-			'initiator' => $initiatorDisplayName,
397
-			'expiration' => $expiration,
398
-			'shareWith' => $shareWith,
399
-		]);
400
-
401
-		$emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
402
-		$emailTemplate->addHeader();
403
-		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
404
-		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
405
-
406
-		$emailTemplate->addBodyText(
407
-			htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')),
408
-			$text
409
-		);
410
-		$emailTemplate->addBodyButton(
411
-			$this->l->t('Open »%s«', [$filename]),
412
-			$link
413
-		);
414
-
415
-		$message->setTo([$shareWith]);
416
-
417
-		// The "From" contains the sharers name
418
-		$instanceName = $this->defaults->getName();
419
-		$senderName = $this->l->t(
420
-			'%s via %s',
421
-			[
422
-				$initiatorDisplayName,
423
-				$instanceName
424
-			]
425
-		);
426
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
427
-
428
-		// The "Reply-To" is set to the sharer if an mail address is configured
429
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
430
-		$initiatorEmail = $initiatorUser->getEMailAddress();
431
-		if($initiatorEmail !== null) {
432
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
433
-			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
434
-		} else {
435
-			$emailTemplate->addFooter();
436
-		}
437
-
438
-		$message->useTemplate($emailTemplate);
439
-		$this->mailer->send($message);
440
-	}
441
-
442
-	/**
443
-	 * send password to recipient of a mail share
444
-	 *
445
-	 * @param IShare $share
446
-	 * @param string $password
447
-	 * @return bool
448
-	 */
449
-	protected function sendPassword(IShare $share, $password) {
450
-
451
-		$filename = $share->getNode()->getName();
452
-		$initiator = $share->getSharedBy();
453
-		$shareWith = $share->getSharedWith();
454
-
455
-		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
456
-			return false;
457
-		}
458
-
459
-		$initiatorUser = $this->userManager->get($initiator);
460
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
461
-		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
462
-
463
-		$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]);
464
-		$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]);
465
-
466
-		$message = $this->mailer->createMessage();
467
-
468
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
469
-			'filename' => $filename,
470
-			'password' => $password,
471
-			'initiator' => $initiatorDisplayName,
472
-			'initiatorEmail' => $initiatorEmailAddress,
473
-			'shareWith' => $shareWith,
474
-		]);
475
-
476
-		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
477
-		$emailTemplate->addHeader();
478
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
479
-		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
480
-		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
481
-
482
-		// The "From" contains the sharers name
483
-		$instanceName = $this->defaults->getName();
484
-		$senderName = $this->l->t(
485
-			'%s via %s',
486
-			[
487
-				$initiatorDisplayName,
488
-				$instanceName
489
-			]
490
-		);
491
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
492
-		if ($initiatorEmailAddress !== null) {
493
-			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
494
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
495
-		} else {
496
-			$emailTemplate->addFooter();
497
-		}
498
-
499
-		$message->setTo([$shareWith]);
500
-		$message->useTemplate($emailTemplate);
501
-		$this->mailer->send($message);
502
-
503
-		$this->createPasswordSendActivity($share, $shareWith, false);
504
-
505
-		return true;
506
-	}
507
-
508
-	/**
509
-	 * send auto generated password to the owner. This happens if the admin enforces
510
-	 * a password for mail shares and forbid to send the password by mail to the recipient
511
-	 *
512
-	 * @param IShare $share
513
-	 * @param string $password
514
-	 * @return bool
515
-	 * @throws \Exception
516
-	 */
517
-	protected function sendPasswordToOwner(IShare $share, $password) {
518
-
519
-		$filename = $share->getNode()->getName();
520
-		$initiator = $this->userManager->get($share->getSharedBy());
521
-		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
522
-		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
523
-		$shareWith = $share->getSharedWith();
524
-
525
-		if ($initiatorEMailAddress === null) {
526
-			throw new \Exception(
527
-				$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.")
528
-			);
529
-		}
530
-
531
-		$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()]);
532
-
533
-		$message = $this->mailer->createMessage();
534
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
535
-			'filename' => $filename,
536
-			'password' => $password,
537
-			'initiator' => $initiatorDisplayName,
538
-			'initiatorEmail' => $initiatorEMailAddress,
539
-			'shareWith' => $shareWith,
540
-		]);
541
-
542
-		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
543
-		$emailTemplate->addHeader();
544
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
545
-		$emailTemplate->addBodyText($bodyPart);
546
-		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
547
-		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
548
-		$emailTemplate->addFooter();
549
-
550
-		if ($initiatorEMailAddress) {
551
-			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
552
-		}
553
-		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
554
-		$message->useTemplate($emailTemplate);
555
-		$this->mailer->send($message);
556
-
557
-		$this->createPasswordSendActivity($share, $shareWith, true);
558
-
559
-		return true;
560
-	}
561
-
562
-	/**
563
-	 * generate share token
564
-	 *
565
-	 * @return string
566
-	 */
567
-	protected function generateToken($size = 15) {
568
-		$token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
569
-		return $token;
570
-	}
571
-
572
-	/**
573
-	 * Get all children of this share
574
-	 *
575
-	 * @param IShare $parent
576
-	 * @return IShare[]
577
-	 */
578
-	public function getChildren(IShare $parent) {
579
-		$children = [];
580
-
581
-		$qb = $this->dbConnection->getQueryBuilder();
582
-		$qb->select('*')
583
-			->from('share')
584
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
585
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
586
-			->orderBy('id');
587
-
588
-		$cursor = $qb->execute();
589
-		while($data = $cursor->fetch()) {
590
-			$children[] = $this->createShareObject($data);
591
-		}
592
-		$cursor->closeCursor();
593
-
594
-		return $children;
595
-	}
596
-
597
-	/**
598
-	 * add share to the database and return the ID
599
-	 *
600
-	 * @param int $itemSource
601
-	 * @param string $itemType
602
-	 * @param string $shareWith
603
-	 * @param string $sharedBy
604
-	 * @param string $uidOwner
605
-	 * @param int $permissions
606
-	 * @param string $token
607
-	 * @return int
608
-	 */
609
-	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
610
-		$qb = $this->dbConnection->getQueryBuilder();
611
-		$qb->insert('share')
612
-			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
613
-			->setValue('item_type', $qb->createNamedParameter($itemType))
614
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
615
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
616
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
617
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
618
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
619
-			->setValue('permissions', $qb->createNamedParameter($permissions))
620
-			->setValue('token', $qb->createNamedParameter($token))
621
-			->setValue('password', $qb->createNamedParameter($password))
622
-			->setValue('stime', $qb->createNamedParameter(time()));
623
-
624
-		/*
165
+        $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
+        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
+    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->logException($hintException, [
355
+                'message' => 'Failed to send share by mail.',
356
+                'level' => \OCP\Util::ERROR,
357
+                'app' => 'sharebymail',
358
+            ]);
359
+            $this->removeShareFromTable($shareId);
360
+            throw $hintException;
361
+        } catch (\Exception $e) {
362
+            $this->logger->logException($e, [
363
+                'message' => 'Failed to send share by mail.',
364
+                'level' => \OCP\Util::ERROR,
365
+                'app' => 'sharebymail',
366
+            ]);
367
+            $this->removeShareFromTable($shareId);
368
+            throw new HintException('Failed to send share by mail',
369
+                $this->l->t('Failed to send share by email'));
370
+        }
371
+
372
+        return $shareId;
373
+
374
+    }
375
+
376
+    /**
377
+     * @param string $filename
378
+     * @param string $link
379
+     * @param string $initiator
380
+     * @param string $shareWith
381
+     * @param \DateTime|null $expiration
382
+     * @throws \Exception If mail couldn't be sent
383
+     */
384
+    protected function sendMailNotification($filename,
385
+                                            $link,
386
+                                            $initiator,
387
+                                            $shareWith,
388
+                                            \DateTime $expiration = null) {
389
+        $initiatorUser = $this->userManager->get($initiator);
390
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
391
+        $message = $this->mailer->createMessage();
392
+
393
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
394
+            'filename' => $filename,
395
+            'link' => $link,
396
+            'initiator' => $initiatorDisplayName,
397
+            'expiration' => $expiration,
398
+            'shareWith' => $shareWith,
399
+        ]);
400
+
401
+        $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
402
+        $emailTemplate->addHeader();
403
+        $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
404
+        $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
405
+
406
+        $emailTemplate->addBodyText(
407
+            htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')),
408
+            $text
409
+        );
410
+        $emailTemplate->addBodyButton(
411
+            $this->l->t('Open »%s«', [$filename]),
412
+            $link
413
+        );
414
+
415
+        $message->setTo([$shareWith]);
416
+
417
+        // The "From" contains the sharers name
418
+        $instanceName = $this->defaults->getName();
419
+        $senderName = $this->l->t(
420
+            '%s via %s',
421
+            [
422
+                $initiatorDisplayName,
423
+                $instanceName
424
+            ]
425
+        );
426
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
427
+
428
+        // The "Reply-To" is set to the sharer if an mail address is configured
429
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
430
+        $initiatorEmail = $initiatorUser->getEMailAddress();
431
+        if($initiatorEmail !== null) {
432
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
433
+            $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
434
+        } else {
435
+            $emailTemplate->addFooter();
436
+        }
437
+
438
+        $message->useTemplate($emailTemplate);
439
+        $this->mailer->send($message);
440
+    }
441
+
442
+    /**
443
+     * send password to recipient of a mail share
444
+     *
445
+     * @param IShare $share
446
+     * @param string $password
447
+     * @return bool
448
+     */
449
+    protected function sendPassword(IShare $share, $password) {
450
+
451
+        $filename = $share->getNode()->getName();
452
+        $initiator = $share->getSharedBy();
453
+        $shareWith = $share->getSharedWith();
454
+
455
+        if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
456
+            return false;
457
+        }
458
+
459
+        $initiatorUser = $this->userManager->get($initiator);
460
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
461
+        $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
462
+
463
+        $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]);
464
+        $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]);
465
+
466
+        $message = $this->mailer->createMessage();
467
+
468
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
469
+            'filename' => $filename,
470
+            'password' => $password,
471
+            'initiator' => $initiatorDisplayName,
472
+            'initiatorEmail' => $initiatorEmailAddress,
473
+            'shareWith' => $shareWith,
474
+        ]);
475
+
476
+        $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
477
+        $emailTemplate->addHeader();
478
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
479
+        $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
480
+        $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
481
+
482
+        // The "From" contains the sharers name
483
+        $instanceName = $this->defaults->getName();
484
+        $senderName = $this->l->t(
485
+            '%s via %s',
486
+            [
487
+                $initiatorDisplayName,
488
+                $instanceName
489
+            ]
490
+        );
491
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
492
+        if ($initiatorEmailAddress !== null) {
493
+            $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
494
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
495
+        } else {
496
+            $emailTemplate->addFooter();
497
+        }
498
+
499
+        $message->setTo([$shareWith]);
500
+        $message->useTemplate($emailTemplate);
501
+        $this->mailer->send($message);
502
+
503
+        $this->createPasswordSendActivity($share, $shareWith, false);
504
+
505
+        return true;
506
+    }
507
+
508
+    /**
509
+     * send auto generated password to the owner. This happens if the admin enforces
510
+     * a password for mail shares and forbid to send the password by mail to the recipient
511
+     *
512
+     * @param IShare $share
513
+     * @param string $password
514
+     * @return bool
515
+     * @throws \Exception
516
+     */
517
+    protected function sendPasswordToOwner(IShare $share, $password) {
518
+
519
+        $filename = $share->getNode()->getName();
520
+        $initiator = $this->userManager->get($share->getSharedBy());
521
+        $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
522
+        $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
523
+        $shareWith = $share->getSharedWith();
524
+
525
+        if ($initiatorEMailAddress === null) {
526
+            throw new \Exception(
527
+                $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.")
528
+            );
529
+        }
530
+
531
+        $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()]);
532
+
533
+        $message = $this->mailer->createMessage();
534
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
535
+            'filename' => $filename,
536
+            'password' => $password,
537
+            'initiator' => $initiatorDisplayName,
538
+            'initiatorEmail' => $initiatorEMailAddress,
539
+            'shareWith' => $shareWith,
540
+        ]);
541
+
542
+        $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
543
+        $emailTemplate->addHeader();
544
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
545
+        $emailTemplate->addBodyText($bodyPart);
546
+        $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
547
+        $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
548
+        $emailTemplate->addFooter();
549
+
550
+        if ($initiatorEMailAddress) {
551
+            $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
552
+        }
553
+        $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
554
+        $message->useTemplate($emailTemplate);
555
+        $this->mailer->send($message);
556
+
557
+        $this->createPasswordSendActivity($share, $shareWith, true);
558
+
559
+        return true;
560
+    }
561
+
562
+    /**
563
+     * generate share token
564
+     *
565
+     * @return string
566
+     */
567
+    protected function generateToken($size = 15) {
568
+        $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
569
+        return $token;
570
+    }
571
+
572
+    /**
573
+     * Get all children of this share
574
+     *
575
+     * @param IShare $parent
576
+     * @return IShare[]
577
+     */
578
+    public function getChildren(IShare $parent) {
579
+        $children = [];
580
+
581
+        $qb = $this->dbConnection->getQueryBuilder();
582
+        $qb->select('*')
583
+            ->from('share')
584
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
585
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
586
+            ->orderBy('id');
587
+
588
+        $cursor = $qb->execute();
589
+        while($data = $cursor->fetch()) {
590
+            $children[] = $this->createShareObject($data);
591
+        }
592
+        $cursor->closeCursor();
593
+
594
+        return $children;
595
+    }
596
+
597
+    /**
598
+     * add share to the database and return the ID
599
+     *
600
+     * @param int $itemSource
601
+     * @param string $itemType
602
+     * @param string $shareWith
603
+     * @param string $sharedBy
604
+     * @param string $uidOwner
605
+     * @param int $permissions
606
+     * @param string $token
607
+     * @return int
608
+     */
609
+    protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
610
+        $qb = $this->dbConnection->getQueryBuilder();
611
+        $qb->insert('share')
612
+            ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
613
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
614
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
615
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
616
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
617
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
618
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
619
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
620
+            ->setValue('token', $qb->createNamedParameter($token))
621
+            ->setValue('password', $qb->createNamedParameter($password))
622
+            ->setValue('stime', $qb->createNamedParameter(time()));
623
+
624
+        /*
625 625
 		 * Added to fix https://github.com/owncloud/core/issues/22215
626 626
 		 * Can be removed once we get rid of ajax/share.php
627 627
 		 */
628
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
628
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
629 629
 
630
-		$qb->execute();
631
-		$id = $qb->getLastInsertId();
630
+        $qb->execute();
631
+        $id = $qb->getLastInsertId();
632 632
 
633
-		return (int)$id;
634
-	}
633
+        return (int)$id;
634
+    }
635 635
 
636
-	/**
637
-	 * Update a share
638
-	 *
639
-	 * @param IShare $share
640
-	 * @param string|null $plainTextPassword
641
-	 * @return IShare The share object
642
-	 */
643
-	public function update(IShare $share, $plainTextPassword = null) {
636
+    /**
637
+     * Update a share
638
+     *
639
+     * @param IShare $share
640
+     * @param string|null $plainTextPassword
641
+     * @return IShare The share object
642
+     */
643
+    public function update(IShare $share, $plainTextPassword = null) {
644 644
 
645
-		$originalShare = $this->getShareById($share->getId());
645
+        $originalShare = $this->getShareById($share->getId());
646 646
 
647
-		// a real password was given
648
-		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
647
+        // a real password was given
648
+        $validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
649 649
 
650
-		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
651
-			$this->sendPassword($share, $plainTextPassword);
652
-		}
653
-		/*
650
+        if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
651
+            $this->sendPassword($share, $plainTextPassword);
652
+        }
653
+        /*
654 654
 		 * We allow updating the permissions and password of mail shares
655 655
 		 */
656
-		$qb = $this->dbConnection->getQueryBuilder();
657
-		$qb->update('share')
658
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
659
-			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
660
-			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
661
-			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
662
-			->set('password', $qb->createNamedParameter($share->getPassword()))
663
-			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
664
-			->execute();
665
-
666
-		return $share;
667
-	}
668
-
669
-	/**
670
-	 * @inheritdoc
671
-	 */
672
-	public function move(IShare $share, $recipient) {
673
-		/**
674
-		 * nothing to do here, mail shares are only outgoing shares
675
-		 */
676
-		return $share;
677
-	}
678
-
679
-	/**
680
-	 * Delete a share (owner unShares the file)
681
-	 *
682
-	 * @param IShare $share
683
-	 */
684
-	public function delete(IShare $share) {
685
-		$this->removeShareFromTable($share->getId());
686
-	}
687
-
688
-	/**
689
-	 * @inheritdoc
690
-	 */
691
-	public function deleteFromSelf(IShare $share, $recipient) {
692
-		// nothing to do here, mail shares are only outgoing shares
693
-	}
694
-
695
-	/**
696
-	 * @inheritdoc
697
-	 */
698
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
699
-		$qb = $this->dbConnection->getQueryBuilder();
700
-		$qb->select('*')
701
-			->from('share');
702
-
703
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
704
-
705
-		/**
706
-		 * Reshares for this user are shares where they are the owner.
707
-		 */
708
-		if ($reshares === false) {
709
-			//Special case for old shares created via the web UI
710
-			$or1 = $qb->expr()->andX(
711
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
712
-				$qb->expr()->isNull('uid_initiator')
713
-			);
714
-
715
-			$qb->andWhere(
716
-				$qb->expr()->orX(
717
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
718
-					$or1
719
-				)
720
-			);
721
-		} else {
722
-			$qb->andWhere(
723
-				$qb->expr()->orX(
724
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
725
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
726
-				)
727
-			);
728
-		}
729
-
730
-		if ($node !== null) {
731
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
732
-		}
733
-
734
-		if ($limit !== -1) {
735
-			$qb->setMaxResults($limit);
736
-		}
737
-
738
-		$qb->setFirstResult($offset);
739
-		$qb->orderBy('id');
740
-
741
-		$cursor = $qb->execute();
742
-		$shares = [];
743
-		while($data = $cursor->fetch()) {
744
-			$shares[] = $this->createShareObject($data);
745
-		}
746
-		$cursor->closeCursor();
747
-
748
-		return $shares;
749
-	}
750
-
751
-	/**
752
-	 * @inheritdoc
753
-	 */
754
-	public function getShareById($id, $recipientId = null) {
755
-		$qb = $this->dbConnection->getQueryBuilder();
756
-
757
-		$qb->select('*')
758
-			->from('share')
759
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
760
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
761
-
762
-		$cursor = $qb->execute();
763
-		$data = $cursor->fetch();
764
-		$cursor->closeCursor();
765
-
766
-		if ($data === false) {
767
-			throw new ShareNotFound();
768
-		}
769
-
770
-		try {
771
-			$share = $this->createShareObject($data);
772
-		} catch (InvalidShare $e) {
773
-			throw new ShareNotFound();
774
-		}
775
-
776
-		return $share;
777
-	}
778
-
779
-	/**
780
-	 * Get shares for a given path
781
-	 *
782
-	 * @param \OCP\Files\Node $path
783
-	 * @return IShare[]
784
-	 */
785
-	public function getSharesByPath(Node $path) {
786
-		$qb = $this->dbConnection->getQueryBuilder();
787
-
788
-		$cursor = $qb->select('*')
789
-			->from('share')
790
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
791
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
792
-			->execute();
793
-
794
-		$shares = [];
795
-		while($data = $cursor->fetch()) {
796
-			$shares[] = $this->createShareObject($data);
797
-		}
798
-		$cursor->closeCursor();
799
-
800
-		return $shares;
801
-	}
802
-
803
-	/**
804
-	 * @inheritdoc
805
-	 */
806
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
807
-		/** @var IShare[] $shares */
808
-		$shares = [];
809
-
810
-		//Get shares directly with this user
811
-		$qb = $this->dbConnection->getQueryBuilder();
812
-		$qb->select('*')
813
-			->from('share');
814
-
815
-		// Order by id
816
-		$qb->orderBy('id');
817
-
818
-		// Set limit and offset
819
-		if ($limit !== -1) {
820
-			$qb->setMaxResults($limit);
821
-		}
822
-		$qb->setFirstResult($offset);
823
-
824
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
825
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
826
-
827
-		// Filter by node if provided
828
-		if ($node !== null) {
829
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
830
-		}
831
-
832
-		$cursor = $qb->execute();
833
-
834
-		while($data = $cursor->fetch()) {
835
-			$shares[] = $this->createShareObject($data);
836
-		}
837
-		$cursor->closeCursor();
838
-
839
-
840
-		return $shares;
841
-	}
842
-
843
-	/**
844
-	 * Get a share by token
845
-	 *
846
-	 * @param string $token
847
-	 * @return IShare
848
-	 * @throws ShareNotFound
849
-	 */
850
-	public function getShareByToken($token) {
851
-		$qb = $this->dbConnection->getQueryBuilder();
852
-
853
-		$cursor = $qb->select('*')
854
-			->from('share')
855
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
856
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
857
-			->execute();
858
-
859
-		$data = $cursor->fetch();
860
-
861
-		if ($data === false) {
862
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
863
-		}
864
-
865
-		try {
866
-			$share = $this->createShareObject($data);
867
-		} catch (InvalidShare $e) {
868
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
869
-		}
870
-
871
-		return $share;
872
-	}
873
-
874
-	/**
875
-	 * remove share from table
876
-	 *
877
-	 * @param string $shareId
878
-	 */
879
-	protected function removeShareFromTable($shareId) {
880
-		$qb = $this->dbConnection->getQueryBuilder();
881
-		$qb->delete('share')
882
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
883
-		$qb->execute();
884
-	}
885
-
886
-	/**
887
-	 * Create a share object from an database row
888
-	 *
889
-	 * @param array $data
890
-	 * @return IShare
891
-	 * @throws InvalidShare
892
-	 * @throws ShareNotFound
893
-	 */
894
-	protected function createShareObject($data) {
895
-
896
-		$share = new Share($this->rootFolder, $this->userManager);
897
-		$share->setId((int)$data['id'])
898
-			->setShareType((int)$data['share_type'])
899
-			->setPermissions((int)$data['permissions'])
900
-			->setTarget($data['file_target'])
901
-			->setMailSend((bool)$data['mail_send'])
902
-			->setToken($data['token']);
903
-
904
-		$shareTime = new \DateTime();
905
-		$shareTime->setTimestamp((int)$data['stime']);
906
-		$share->setShareTime($shareTime);
907
-		$share->setSharedWith($data['share_with']);
908
-		$share->setPassword($data['password']);
909
-
910
-		if ($data['uid_initiator'] !== null) {
911
-			$share->setShareOwner($data['uid_owner']);
912
-			$share->setSharedBy($data['uid_initiator']);
913
-		} else {
914
-			//OLD SHARE
915
-			$share->setSharedBy($data['uid_owner']);
916
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
917
-
918
-			$owner = $path->getOwner();
919
-			$share->setShareOwner($owner->getUID());
920
-		}
921
-
922
-		if ($data['expiration'] !== null) {
923
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
924
-			if ($expiration !== false) {
925
-				$share->setExpirationDate($expiration);
926
-			}
927
-		}
928
-
929
-		$share->setNodeId((int)$data['file_source']);
930
-		$share->setNodeType($data['item_type']);
931
-
932
-		$share->setProviderId($this->identifier());
933
-
934
-		return $share;
935
-	}
936
-
937
-	/**
938
-	 * Get the node with file $id for $user
939
-	 *
940
-	 * @param string $userId
941
-	 * @param int $id
942
-	 * @return \OCP\Files\File|\OCP\Files\Folder
943
-	 * @throws InvalidShare
944
-	 */
945
-	private function getNode($userId, $id) {
946
-		try {
947
-			$userFolder = $this->rootFolder->getUserFolder($userId);
948
-		} catch (NoUserException $e) {
949
-			throw new InvalidShare();
950
-		}
951
-
952
-		$nodes = $userFolder->getById($id);
953
-
954
-		if (empty($nodes)) {
955
-			throw new InvalidShare();
956
-		}
957
-
958
-		return $nodes[0];
959
-	}
960
-
961
-	/**
962
-	 * A user is deleted from the system
963
-	 * So clean up the relevant shares.
964
-	 *
965
-	 * @param string $uid
966
-	 * @param int $shareType
967
-	 */
968
-	public function userDeleted($uid, $shareType) {
969
-		$qb = $this->dbConnection->getQueryBuilder();
970
-
971
-		$qb->delete('share')
972
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
973
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
974
-			->execute();
975
-	}
976
-
977
-	/**
978
-	 * This provider does not support group shares
979
-	 *
980
-	 * @param string $gid
981
-	 */
982
-	public function groupDeleted($gid) {
983
-	}
984
-
985
-	/**
986
-	 * This provider does not support group shares
987
-	 *
988
-	 * @param string $uid
989
-	 * @param string $gid
990
-	 */
991
-	public function userDeletedFromGroup($uid, $gid) {
992
-	}
993
-
994
-	/**
995
-	 * get database row of a give share
996
-	 *
997
-	 * @param $id
998
-	 * @return array
999
-	 * @throws ShareNotFound
1000
-	 */
1001
-	protected function getRawShare($id) {
1002
-
1003
-		// Now fetch the inserted share and create a complete share object
1004
-		$qb = $this->dbConnection->getQueryBuilder();
1005
-		$qb->select('*')
1006
-			->from('share')
1007
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1008
-
1009
-		$cursor = $qb->execute();
1010
-		$data = $cursor->fetch();
1011
-		$cursor->closeCursor();
1012
-
1013
-		if ($data === false) {
1014
-			throw new ShareNotFound;
1015
-		}
1016
-
1017
-		return $data;
1018
-	}
1019
-
1020
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
1021
-		$qb = $this->dbConnection->getQueryBuilder();
1022
-		$qb->select('*')
1023
-			->from('share', 's')
1024
-			->andWhere($qb->expr()->orX(
1025
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1026
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1027
-			))
1028
-			->andWhere(
1029
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1030
-			);
1031
-
1032
-		/**
1033
-		 * Reshares for this user are shares where they are the owner.
1034
-		 */
1035
-		if ($reshares === false) {
1036
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1037
-		} else {
1038
-			$qb->andWhere(
1039
-				$qb->expr()->orX(
1040
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1041
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1042
-				)
1043
-			);
1044
-		}
1045
-
1046
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1047
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1048
-
1049
-		$qb->orderBy('id');
1050
-
1051
-		$cursor = $qb->execute();
1052
-		$shares = [];
1053
-		while ($data = $cursor->fetch()) {
1054
-			$shares[$data['fileid']][] = $this->createShareObject($data);
1055
-		}
1056
-		$cursor->closeCursor();
1057
-
1058
-		return $shares;
1059
-	}
1060
-
1061
-	/**
1062
-	 * @inheritdoc
1063
-	 */
1064
-	public function getAccessList($nodes, $currentAccess) {
1065
-		$ids = [];
1066
-		foreach ($nodes as $node) {
1067
-			$ids[] = $node->getId();
1068
-		}
1069
-
1070
-		$qb = $this->dbConnection->getQueryBuilder();
1071
-		$qb->select('share_with')
1072
-			->from('share')
1073
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1074
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1075
-			->andWhere($qb->expr()->orX(
1076
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1077
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1078
-			))
1079
-			->setMaxResults(1);
1080
-		$cursor = $qb->execute();
1081
-
1082
-		$mail = $cursor->fetch() !== false;
1083
-		$cursor->closeCursor();
1084
-
1085
-		return ['public' => $mail];
1086
-	}
656
+        $qb = $this->dbConnection->getQueryBuilder();
657
+        $qb->update('share')
658
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
659
+            ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
660
+            ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
661
+            ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
662
+            ->set('password', $qb->createNamedParameter($share->getPassword()))
663
+            ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
664
+            ->execute();
665
+
666
+        return $share;
667
+    }
668
+
669
+    /**
670
+     * @inheritdoc
671
+     */
672
+    public function move(IShare $share, $recipient) {
673
+        /**
674
+         * nothing to do here, mail shares are only outgoing shares
675
+         */
676
+        return $share;
677
+    }
678
+
679
+    /**
680
+     * Delete a share (owner unShares the file)
681
+     *
682
+     * @param IShare $share
683
+     */
684
+    public function delete(IShare $share) {
685
+        $this->removeShareFromTable($share->getId());
686
+    }
687
+
688
+    /**
689
+     * @inheritdoc
690
+     */
691
+    public function deleteFromSelf(IShare $share, $recipient) {
692
+        // nothing to do here, mail shares are only outgoing shares
693
+    }
694
+
695
+    /**
696
+     * @inheritdoc
697
+     */
698
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
699
+        $qb = $this->dbConnection->getQueryBuilder();
700
+        $qb->select('*')
701
+            ->from('share');
702
+
703
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
704
+
705
+        /**
706
+         * Reshares for this user are shares where they are the owner.
707
+         */
708
+        if ($reshares === false) {
709
+            //Special case for old shares created via the web UI
710
+            $or1 = $qb->expr()->andX(
711
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
712
+                $qb->expr()->isNull('uid_initiator')
713
+            );
714
+
715
+            $qb->andWhere(
716
+                $qb->expr()->orX(
717
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
718
+                    $or1
719
+                )
720
+            );
721
+        } else {
722
+            $qb->andWhere(
723
+                $qb->expr()->orX(
724
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
725
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
726
+                )
727
+            );
728
+        }
729
+
730
+        if ($node !== null) {
731
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
732
+        }
733
+
734
+        if ($limit !== -1) {
735
+            $qb->setMaxResults($limit);
736
+        }
737
+
738
+        $qb->setFirstResult($offset);
739
+        $qb->orderBy('id');
740
+
741
+        $cursor = $qb->execute();
742
+        $shares = [];
743
+        while($data = $cursor->fetch()) {
744
+            $shares[] = $this->createShareObject($data);
745
+        }
746
+        $cursor->closeCursor();
747
+
748
+        return $shares;
749
+    }
750
+
751
+    /**
752
+     * @inheritdoc
753
+     */
754
+    public function getShareById($id, $recipientId = null) {
755
+        $qb = $this->dbConnection->getQueryBuilder();
756
+
757
+        $qb->select('*')
758
+            ->from('share')
759
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
760
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
761
+
762
+        $cursor = $qb->execute();
763
+        $data = $cursor->fetch();
764
+        $cursor->closeCursor();
765
+
766
+        if ($data === false) {
767
+            throw new ShareNotFound();
768
+        }
769
+
770
+        try {
771
+            $share = $this->createShareObject($data);
772
+        } catch (InvalidShare $e) {
773
+            throw new ShareNotFound();
774
+        }
775
+
776
+        return $share;
777
+    }
778
+
779
+    /**
780
+     * Get shares for a given path
781
+     *
782
+     * @param \OCP\Files\Node $path
783
+     * @return IShare[]
784
+     */
785
+    public function getSharesByPath(Node $path) {
786
+        $qb = $this->dbConnection->getQueryBuilder();
787
+
788
+        $cursor = $qb->select('*')
789
+            ->from('share')
790
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
791
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
792
+            ->execute();
793
+
794
+        $shares = [];
795
+        while($data = $cursor->fetch()) {
796
+            $shares[] = $this->createShareObject($data);
797
+        }
798
+        $cursor->closeCursor();
799
+
800
+        return $shares;
801
+    }
802
+
803
+    /**
804
+     * @inheritdoc
805
+     */
806
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
807
+        /** @var IShare[] $shares */
808
+        $shares = [];
809
+
810
+        //Get shares directly with this user
811
+        $qb = $this->dbConnection->getQueryBuilder();
812
+        $qb->select('*')
813
+            ->from('share');
814
+
815
+        // Order by id
816
+        $qb->orderBy('id');
817
+
818
+        // Set limit and offset
819
+        if ($limit !== -1) {
820
+            $qb->setMaxResults($limit);
821
+        }
822
+        $qb->setFirstResult($offset);
823
+
824
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
825
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
826
+
827
+        // Filter by node if provided
828
+        if ($node !== null) {
829
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
830
+        }
831
+
832
+        $cursor = $qb->execute();
833
+
834
+        while($data = $cursor->fetch()) {
835
+            $shares[] = $this->createShareObject($data);
836
+        }
837
+        $cursor->closeCursor();
838
+
839
+
840
+        return $shares;
841
+    }
842
+
843
+    /**
844
+     * Get a share by token
845
+     *
846
+     * @param string $token
847
+     * @return IShare
848
+     * @throws ShareNotFound
849
+     */
850
+    public function getShareByToken($token) {
851
+        $qb = $this->dbConnection->getQueryBuilder();
852
+
853
+        $cursor = $qb->select('*')
854
+            ->from('share')
855
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
856
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
857
+            ->execute();
858
+
859
+        $data = $cursor->fetch();
860
+
861
+        if ($data === false) {
862
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
863
+        }
864
+
865
+        try {
866
+            $share = $this->createShareObject($data);
867
+        } catch (InvalidShare $e) {
868
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
869
+        }
870
+
871
+        return $share;
872
+    }
873
+
874
+    /**
875
+     * remove share from table
876
+     *
877
+     * @param string $shareId
878
+     */
879
+    protected function removeShareFromTable($shareId) {
880
+        $qb = $this->dbConnection->getQueryBuilder();
881
+        $qb->delete('share')
882
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
883
+        $qb->execute();
884
+    }
885
+
886
+    /**
887
+     * Create a share object from an database row
888
+     *
889
+     * @param array $data
890
+     * @return IShare
891
+     * @throws InvalidShare
892
+     * @throws ShareNotFound
893
+     */
894
+    protected function createShareObject($data) {
895
+
896
+        $share = new Share($this->rootFolder, $this->userManager);
897
+        $share->setId((int)$data['id'])
898
+            ->setShareType((int)$data['share_type'])
899
+            ->setPermissions((int)$data['permissions'])
900
+            ->setTarget($data['file_target'])
901
+            ->setMailSend((bool)$data['mail_send'])
902
+            ->setToken($data['token']);
903
+
904
+        $shareTime = new \DateTime();
905
+        $shareTime->setTimestamp((int)$data['stime']);
906
+        $share->setShareTime($shareTime);
907
+        $share->setSharedWith($data['share_with']);
908
+        $share->setPassword($data['password']);
909
+
910
+        if ($data['uid_initiator'] !== null) {
911
+            $share->setShareOwner($data['uid_owner']);
912
+            $share->setSharedBy($data['uid_initiator']);
913
+        } else {
914
+            //OLD SHARE
915
+            $share->setSharedBy($data['uid_owner']);
916
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
917
+
918
+            $owner = $path->getOwner();
919
+            $share->setShareOwner($owner->getUID());
920
+        }
921
+
922
+        if ($data['expiration'] !== null) {
923
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
924
+            if ($expiration !== false) {
925
+                $share->setExpirationDate($expiration);
926
+            }
927
+        }
928
+
929
+        $share->setNodeId((int)$data['file_source']);
930
+        $share->setNodeType($data['item_type']);
931
+
932
+        $share->setProviderId($this->identifier());
933
+
934
+        return $share;
935
+    }
936
+
937
+    /**
938
+     * Get the node with file $id for $user
939
+     *
940
+     * @param string $userId
941
+     * @param int $id
942
+     * @return \OCP\Files\File|\OCP\Files\Folder
943
+     * @throws InvalidShare
944
+     */
945
+    private function getNode($userId, $id) {
946
+        try {
947
+            $userFolder = $this->rootFolder->getUserFolder($userId);
948
+        } catch (NoUserException $e) {
949
+            throw new InvalidShare();
950
+        }
951
+
952
+        $nodes = $userFolder->getById($id);
953
+
954
+        if (empty($nodes)) {
955
+            throw new InvalidShare();
956
+        }
957
+
958
+        return $nodes[0];
959
+    }
960
+
961
+    /**
962
+     * A user is deleted from the system
963
+     * So clean up the relevant shares.
964
+     *
965
+     * @param string $uid
966
+     * @param int $shareType
967
+     */
968
+    public function userDeleted($uid, $shareType) {
969
+        $qb = $this->dbConnection->getQueryBuilder();
970
+
971
+        $qb->delete('share')
972
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
973
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
974
+            ->execute();
975
+    }
976
+
977
+    /**
978
+     * This provider does not support group shares
979
+     *
980
+     * @param string $gid
981
+     */
982
+    public function groupDeleted($gid) {
983
+    }
984
+
985
+    /**
986
+     * This provider does not support group shares
987
+     *
988
+     * @param string $uid
989
+     * @param string $gid
990
+     */
991
+    public function userDeletedFromGroup($uid, $gid) {
992
+    }
993
+
994
+    /**
995
+     * get database row of a give share
996
+     *
997
+     * @param $id
998
+     * @return array
999
+     * @throws ShareNotFound
1000
+     */
1001
+    protected function getRawShare($id) {
1002
+
1003
+        // Now fetch the inserted share and create a complete share object
1004
+        $qb = $this->dbConnection->getQueryBuilder();
1005
+        $qb->select('*')
1006
+            ->from('share')
1007
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1008
+
1009
+        $cursor = $qb->execute();
1010
+        $data = $cursor->fetch();
1011
+        $cursor->closeCursor();
1012
+
1013
+        if ($data === false) {
1014
+            throw new ShareNotFound;
1015
+        }
1016
+
1017
+        return $data;
1018
+    }
1019
+
1020
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
1021
+        $qb = $this->dbConnection->getQueryBuilder();
1022
+        $qb->select('*')
1023
+            ->from('share', 's')
1024
+            ->andWhere($qb->expr()->orX(
1025
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1026
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1027
+            ))
1028
+            ->andWhere(
1029
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1030
+            );
1031
+
1032
+        /**
1033
+         * Reshares for this user are shares where they are the owner.
1034
+         */
1035
+        if ($reshares === false) {
1036
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1037
+        } else {
1038
+            $qb->andWhere(
1039
+                $qb->expr()->orX(
1040
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1041
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1042
+                )
1043
+            );
1044
+        }
1045
+
1046
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1047
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1048
+
1049
+        $qb->orderBy('id');
1050
+
1051
+        $cursor = $qb->execute();
1052
+        $shares = [];
1053
+        while ($data = $cursor->fetch()) {
1054
+            $shares[$data['fileid']][] = $this->createShareObject($data);
1055
+        }
1056
+        $cursor->closeCursor();
1057
+
1058
+        return $shares;
1059
+    }
1060
+
1061
+    /**
1062
+     * @inheritdoc
1063
+     */
1064
+    public function getAccessList($nodes, $currentAccess) {
1065
+        $ids = [];
1066
+        foreach ($nodes as $node) {
1067
+            $ids[] = $node->getId();
1068
+        }
1069
+
1070
+        $qb = $this->dbConnection->getQueryBuilder();
1071
+        $qb->select('share_with')
1072
+            ->from('share')
1073
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1074
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1075
+            ->andWhere($qb->expr()->orX(
1076
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1077
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1078
+            ))
1079
+            ->setMaxResults(1);
1080
+        $cursor = $qb->execute();
1081
+
1082
+        $mail = $cursor->fetch() !== false;
1083
+        $cursor->closeCursor();
1084
+
1085
+        return ['public' => $mail];
1086
+    }
1087 1087
 
1088 1088
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +1470 added lines, -1470 removed lines patch added patch discarded remove patch
@@ -71,1498 +71,1498 @@
 block discarded – undo
71 71
  */
72 72
 class Manager implements IManager {
73 73
 
74
-	/** @var IProviderFactory */
75
-	private $factory;
76
-	/** @var ILogger */
77
-	private $logger;
78
-	/** @var IConfig */
79
-	private $config;
80
-	/** @var ISecureRandom */
81
-	private $secureRandom;
82
-	/** @var IHasher */
83
-	private $hasher;
84
-	/** @var IMountManager */
85
-	private $mountManager;
86
-	/** @var IGroupManager */
87
-	private $groupManager;
88
-	/** @var IL10N */
89
-	private $l;
90
-	/** @var IFactory */
91
-	private $l10nFactory;
92
-	/** @var IUserManager */
93
-	private $userManager;
94
-	/** @var IRootFolder */
95
-	private $rootFolder;
96
-	/** @var CappedMemoryCache */
97
-	private $sharingDisabledForUsersCache;
98
-	/** @var EventDispatcher */
99
-	private $eventDispatcher;
100
-	/** @var LegacyHooks */
101
-	private $legacyHooks;
102
-	/** @var IMailer */
103
-	private $mailer;
104
-	/** @var IURLGenerator */
105
-	private $urlGenerator;
106
-	/** @var \OC_Defaults */
107
-	private $defaults;
108
-
109
-
110
-	/**
111
-	 * Manager constructor.
112
-	 *
113
-	 * @param ILogger $logger
114
-	 * @param IConfig $config
115
-	 * @param ISecureRandom $secureRandom
116
-	 * @param IHasher $hasher
117
-	 * @param IMountManager $mountManager
118
-	 * @param IGroupManager $groupManager
119
-	 * @param IL10N $l
120
-	 * @param IFactory $l10nFactory
121
-	 * @param IProviderFactory $factory
122
-	 * @param IUserManager $userManager
123
-	 * @param IRootFolder $rootFolder
124
-	 * @param EventDispatcher $eventDispatcher
125
-	 * @param IMailer $mailer
126
-	 * @param IURLGenerator $urlGenerator
127
-	 * @param \OC_Defaults $defaults
128
-	 */
129
-	public function __construct(
130
-			ILogger $logger,
131
-			IConfig $config,
132
-			ISecureRandom $secureRandom,
133
-			IHasher $hasher,
134
-			IMountManager $mountManager,
135
-			IGroupManager $groupManager,
136
-			IL10N $l,
137
-			IFactory $l10nFactory,
138
-			IProviderFactory $factory,
139
-			IUserManager $userManager,
140
-			IRootFolder $rootFolder,
141
-			EventDispatcher $eventDispatcher,
142
-			IMailer $mailer,
143
-			IURLGenerator $urlGenerator,
144
-			\OC_Defaults $defaults
145
-	) {
146
-		$this->logger = $logger;
147
-		$this->config = $config;
148
-		$this->secureRandom = $secureRandom;
149
-		$this->hasher = $hasher;
150
-		$this->mountManager = $mountManager;
151
-		$this->groupManager = $groupManager;
152
-		$this->l = $l;
153
-		$this->l10nFactory = $l10nFactory;
154
-		$this->factory = $factory;
155
-		$this->userManager = $userManager;
156
-		$this->rootFolder = $rootFolder;
157
-		$this->eventDispatcher = $eventDispatcher;
158
-		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
159
-		$this->legacyHooks = new LegacyHooks($this->eventDispatcher);
160
-		$this->mailer = $mailer;
161
-		$this->urlGenerator = $urlGenerator;
162
-		$this->defaults = $defaults;
163
-	}
164
-
165
-	/**
166
-	 * Convert from a full share id to a tuple (providerId, shareId)
167
-	 *
168
-	 * @param string $id
169
-	 * @return string[]
170
-	 */
171
-	private function splitFullId($id) {
172
-		return explode(':', $id, 2);
173
-	}
174
-
175
-	/**
176
-	 * Verify if a password meets all requirements
177
-	 *
178
-	 * @param string $password
179
-	 * @throws \Exception
180
-	 */
181
-	protected function verifyPassword($password) {
182
-		if ($password === null) {
183
-			// No password is set, check if this is allowed.
184
-			if ($this->shareApiLinkEnforcePassword()) {
185
-				throw new \InvalidArgumentException('Passwords are enforced for link shares');
186
-			}
187
-
188
-			return;
189
-		}
190
-
191
-		// Let others verify the password
192
-		try {
193
-			$event = new GenericEvent($password);
194
-			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
195
-		} catch (HintException $e) {
196
-			throw new \Exception($e->getHint());
197
-		}
198
-	}
199
-
200
-	/**
201
-	 * Check for generic requirements before creating a share
202
-	 *
203
-	 * @param \OCP\Share\IShare $share
204
-	 * @throws \InvalidArgumentException
205
-	 * @throws GenericShareException
206
-	 *
207
-	 * @suppress PhanUndeclaredClassMethod
208
-	 */
209
-	protected function generalCreateChecks(\OCP\Share\IShare $share) {
210
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
211
-			// We expect a valid user as sharedWith for user shares
212
-			if (!$this->userManager->userExists($share->getSharedWith())) {
213
-				throw new \InvalidArgumentException('SharedWith is not a valid user');
214
-			}
215
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
216
-			// We expect a valid group as sharedWith for group shares
217
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
218
-				throw new \InvalidArgumentException('SharedWith is not a valid group');
219
-			}
220
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
221
-			if ($share->getSharedWith() !== null) {
222
-				throw new \InvalidArgumentException('SharedWith should be empty');
223
-			}
224
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
225
-			if ($share->getSharedWith() === null) {
226
-				throw new \InvalidArgumentException('SharedWith should not be empty');
227
-			}
228
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
229
-			if ($share->getSharedWith() === null) {
230
-				throw new \InvalidArgumentException('SharedWith should not be empty');
231
-			}
232
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
233
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
234
-			if ($circle === null) {
235
-				throw new \InvalidArgumentException('SharedWith is not a valid circle');
236
-			}
237
-		} else {
238
-			// We can't handle other types yet
239
-			throw new \InvalidArgumentException('unknown share type');
240
-		}
241
-
242
-		// Verify the initiator of the share is set
243
-		if ($share->getSharedBy() === null) {
244
-			throw new \InvalidArgumentException('SharedBy should be set');
245
-		}
246
-
247
-		// Cannot share with yourself
248
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
249
-			$share->getSharedWith() === $share->getSharedBy()) {
250
-			throw new \InvalidArgumentException('Can’t share with yourself');
251
-		}
252
-
253
-		// The path should be set
254
-		if ($share->getNode() === null) {
255
-			throw new \InvalidArgumentException('Path should be set');
256
-		}
257
-
258
-		// And it should be a file or a folder
259
-		if (!($share->getNode() instanceof \OCP\Files\File) &&
260
-				!($share->getNode() instanceof \OCP\Files\Folder)) {
261
-			throw new \InvalidArgumentException('Path should be either a file or a folder');
262
-		}
263
-
264
-		// And you can't share your rootfolder
265
-		if ($this->userManager->userExists($share->getSharedBy())) {
266
-			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
267
-		} else {
268
-			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
269
-		}
270
-		if ($sharedPath === $share->getNode()->getPath()) {
271
-			throw new \InvalidArgumentException('You can’t share your root folder');
272
-		}
273
-
274
-		// Check if we actually have share permissions
275
-		if (!$share->getNode()->isShareable()) {
276
-			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
277
-			throw new GenericShareException($message_t, $message_t, 404);
278
-		}
279
-
280
-		// Permissions should be set
281
-		if ($share->getPermissions() === null) {
282
-			throw new \InvalidArgumentException('A share requires permissions');
283
-		}
284
-
285
-		/*
74
+    /** @var IProviderFactory */
75
+    private $factory;
76
+    /** @var ILogger */
77
+    private $logger;
78
+    /** @var IConfig */
79
+    private $config;
80
+    /** @var ISecureRandom */
81
+    private $secureRandom;
82
+    /** @var IHasher */
83
+    private $hasher;
84
+    /** @var IMountManager */
85
+    private $mountManager;
86
+    /** @var IGroupManager */
87
+    private $groupManager;
88
+    /** @var IL10N */
89
+    private $l;
90
+    /** @var IFactory */
91
+    private $l10nFactory;
92
+    /** @var IUserManager */
93
+    private $userManager;
94
+    /** @var IRootFolder */
95
+    private $rootFolder;
96
+    /** @var CappedMemoryCache */
97
+    private $sharingDisabledForUsersCache;
98
+    /** @var EventDispatcher */
99
+    private $eventDispatcher;
100
+    /** @var LegacyHooks */
101
+    private $legacyHooks;
102
+    /** @var IMailer */
103
+    private $mailer;
104
+    /** @var IURLGenerator */
105
+    private $urlGenerator;
106
+    /** @var \OC_Defaults */
107
+    private $defaults;
108
+
109
+
110
+    /**
111
+     * Manager constructor.
112
+     *
113
+     * @param ILogger $logger
114
+     * @param IConfig $config
115
+     * @param ISecureRandom $secureRandom
116
+     * @param IHasher $hasher
117
+     * @param IMountManager $mountManager
118
+     * @param IGroupManager $groupManager
119
+     * @param IL10N $l
120
+     * @param IFactory $l10nFactory
121
+     * @param IProviderFactory $factory
122
+     * @param IUserManager $userManager
123
+     * @param IRootFolder $rootFolder
124
+     * @param EventDispatcher $eventDispatcher
125
+     * @param IMailer $mailer
126
+     * @param IURLGenerator $urlGenerator
127
+     * @param \OC_Defaults $defaults
128
+     */
129
+    public function __construct(
130
+            ILogger $logger,
131
+            IConfig $config,
132
+            ISecureRandom $secureRandom,
133
+            IHasher $hasher,
134
+            IMountManager $mountManager,
135
+            IGroupManager $groupManager,
136
+            IL10N $l,
137
+            IFactory $l10nFactory,
138
+            IProviderFactory $factory,
139
+            IUserManager $userManager,
140
+            IRootFolder $rootFolder,
141
+            EventDispatcher $eventDispatcher,
142
+            IMailer $mailer,
143
+            IURLGenerator $urlGenerator,
144
+            \OC_Defaults $defaults
145
+    ) {
146
+        $this->logger = $logger;
147
+        $this->config = $config;
148
+        $this->secureRandom = $secureRandom;
149
+        $this->hasher = $hasher;
150
+        $this->mountManager = $mountManager;
151
+        $this->groupManager = $groupManager;
152
+        $this->l = $l;
153
+        $this->l10nFactory = $l10nFactory;
154
+        $this->factory = $factory;
155
+        $this->userManager = $userManager;
156
+        $this->rootFolder = $rootFolder;
157
+        $this->eventDispatcher = $eventDispatcher;
158
+        $this->sharingDisabledForUsersCache = new CappedMemoryCache();
159
+        $this->legacyHooks = new LegacyHooks($this->eventDispatcher);
160
+        $this->mailer = $mailer;
161
+        $this->urlGenerator = $urlGenerator;
162
+        $this->defaults = $defaults;
163
+    }
164
+
165
+    /**
166
+     * Convert from a full share id to a tuple (providerId, shareId)
167
+     *
168
+     * @param string $id
169
+     * @return string[]
170
+     */
171
+    private function splitFullId($id) {
172
+        return explode(':', $id, 2);
173
+    }
174
+
175
+    /**
176
+     * Verify if a password meets all requirements
177
+     *
178
+     * @param string $password
179
+     * @throws \Exception
180
+     */
181
+    protected function verifyPassword($password) {
182
+        if ($password === null) {
183
+            // No password is set, check if this is allowed.
184
+            if ($this->shareApiLinkEnforcePassword()) {
185
+                throw new \InvalidArgumentException('Passwords are enforced for link shares');
186
+            }
187
+
188
+            return;
189
+        }
190
+
191
+        // Let others verify the password
192
+        try {
193
+            $event = new GenericEvent($password);
194
+            $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
195
+        } catch (HintException $e) {
196
+            throw new \Exception($e->getHint());
197
+        }
198
+    }
199
+
200
+    /**
201
+     * Check for generic requirements before creating a share
202
+     *
203
+     * @param \OCP\Share\IShare $share
204
+     * @throws \InvalidArgumentException
205
+     * @throws GenericShareException
206
+     *
207
+     * @suppress PhanUndeclaredClassMethod
208
+     */
209
+    protected function generalCreateChecks(\OCP\Share\IShare $share) {
210
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
211
+            // We expect a valid user as sharedWith for user shares
212
+            if (!$this->userManager->userExists($share->getSharedWith())) {
213
+                throw new \InvalidArgumentException('SharedWith is not a valid user');
214
+            }
215
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
216
+            // We expect a valid group as sharedWith for group shares
217
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
218
+                throw new \InvalidArgumentException('SharedWith is not a valid group');
219
+            }
220
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
221
+            if ($share->getSharedWith() !== null) {
222
+                throw new \InvalidArgumentException('SharedWith should be empty');
223
+            }
224
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
225
+            if ($share->getSharedWith() === null) {
226
+                throw new \InvalidArgumentException('SharedWith should not be empty');
227
+            }
228
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
229
+            if ($share->getSharedWith() === null) {
230
+                throw new \InvalidArgumentException('SharedWith should not be empty');
231
+            }
232
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
233
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
234
+            if ($circle === null) {
235
+                throw new \InvalidArgumentException('SharedWith is not a valid circle');
236
+            }
237
+        } else {
238
+            // We can't handle other types yet
239
+            throw new \InvalidArgumentException('unknown share type');
240
+        }
241
+
242
+        // Verify the initiator of the share is set
243
+        if ($share->getSharedBy() === null) {
244
+            throw new \InvalidArgumentException('SharedBy should be set');
245
+        }
246
+
247
+        // Cannot share with yourself
248
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
249
+            $share->getSharedWith() === $share->getSharedBy()) {
250
+            throw new \InvalidArgumentException('Can’t share with yourself');
251
+        }
252
+
253
+        // The path should be set
254
+        if ($share->getNode() === null) {
255
+            throw new \InvalidArgumentException('Path should be set');
256
+        }
257
+
258
+        // And it should be a file or a folder
259
+        if (!($share->getNode() instanceof \OCP\Files\File) &&
260
+                !($share->getNode() instanceof \OCP\Files\Folder)) {
261
+            throw new \InvalidArgumentException('Path should be either a file or a folder');
262
+        }
263
+
264
+        // And you can't share your rootfolder
265
+        if ($this->userManager->userExists($share->getSharedBy())) {
266
+            $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
267
+        } else {
268
+            $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
269
+        }
270
+        if ($sharedPath === $share->getNode()->getPath()) {
271
+            throw new \InvalidArgumentException('You can’t share your root folder');
272
+        }
273
+
274
+        // Check if we actually have share permissions
275
+        if (!$share->getNode()->isShareable()) {
276
+            $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
277
+            throw new GenericShareException($message_t, $message_t, 404);
278
+        }
279
+
280
+        // Permissions should be set
281
+        if ($share->getPermissions() === null) {
282
+            throw new \InvalidArgumentException('A share requires permissions');
283
+        }
284
+
285
+        /*
286 286
 		 * Quick fix for #23536
287 287
 		 * Non moveable mount points do not have update and delete permissions
288 288
 		 * while we 'most likely' do have that on the storage.
289 289
 		 */
290
-		$permissions = $share->getNode()->getPermissions();
291
-		$mount = $share->getNode()->getMountPoint();
292
-		if (!($mount instanceof MoveableMount)) {
293
-			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
294
-		}
295
-
296
-		// Check that we do not share with more permissions than we have
297
-		if ($share->getPermissions() & ~$permissions) {
298
-			$message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
299
-			throw new GenericShareException($message_t, $message_t, 404);
300
-		}
301
-
302
-
303
-		// Check that read permissions are always set
304
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
305
-		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
306
-			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
307
-		if (!$noReadPermissionRequired &&
308
-			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
309
-			throw new \InvalidArgumentException('Shares need at least read permissions');
310
-		}
311
-
312
-		if ($share->getNode() instanceof \OCP\Files\File) {
313
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
314
-				$message_t = $this->l->t('Files can’t be shared with delete permissions');
315
-				throw new GenericShareException($message_t);
316
-			}
317
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
318
-				$message_t = $this->l->t('Files can’t be shared with create permissions');
319
-				throw new GenericShareException($message_t);
320
-			}
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * Validate if the expiration date fits the system settings
326
-	 *
327
-	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
328
-	 * @return \OCP\Share\IShare The modified share object
329
-	 * @throws GenericShareException
330
-	 * @throws \InvalidArgumentException
331
-	 * @throws \Exception
332
-	 */
333
-	protected function validateExpirationDate(\OCP\Share\IShare $share) {
334
-
335
-		$expirationDate = $share->getExpirationDate();
336
-
337
-		if ($expirationDate !== null) {
338
-			//Make sure the expiration date is a date
339
-			$expirationDate->setTime(0, 0, 0);
340
-
341
-			$date = new \DateTime();
342
-			$date->setTime(0, 0, 0);
343
-			if ($date >= $expirationDate) {
344
-				$message = $this->l->t('Expiration date is in the past');
345
-				throw new GenericShareException($message, $message, 404);
346
-			}
347
-		}
348
-
349
-		// If expiredate is empty set a default one if there is a default
350
-		$fullId = null;
351
-		try {
352
-			$fullId = $share->getFullId();
353
-		} catch (\UnexpectedValueException $e) {
354
-			// This is a new share
355
-		}
356
-
357
-		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
358
-			$expirationDate = new \DateTime();
359
-			$expirationDate->setTime(0,0,0);
360
-			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
361
-		}
362
-
363
-		// If we enforce the expiration date check that is does not exceed
364
-		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
365
-			if ($expirationDate === null) {
366
-				throw new \InvalidArgumentException('Expiration date is enforced');
367
-			}
368
-
369
-			$date = new \DateTime();
370
-			$date->setTime(0, 0, 0);
371
-			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
372
-			if ($date < $expirationDate) {
373
-				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
374
-				throw new GenericShareException($message, $message, 404);
375
-			}
376
-		}
377
-
378
-		$accepted = true;
379
-		$message = '';
380
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
381
-			'expirationDate' => &$expirationDate,
382
-			'accepted' => &$accepted,
383
-			'message' => &$message,
384
-			'passwordSet' => $share->getPassword() !== null,
385
-		]);
386
-
387
-		if (!$accepted) {
388
-			throw new \Exception($message);
389
-		}
390
-
391
-		$share->setExpirationDate($expirationDate);
392
-
393
-		return $share;
394
-	}
395
-
396
-	/**
397
-	 * Check for pre share requirements for user shares
398
-	 *
399
-	 * @param \OCP\Share\IShare $share
400
-	 * @throws \Exception
401
-	 */
402
-	protected function userCreateChecks(\OCP\Share\IShare $share) {
403
-		// Check if we can share with group members only
404
-		if ($this->shareWithGroupMembersOnly()) {
405
-			$sharedBy = $this->userManager->get($share->getSharedBy());
406
-			$sharedWith = $this->userManager->get($share->getSharedWith());
407
-			// Verify we can share with this user
408
-			$groups = array_intersect(
409
-					$this->groupManager->getUserGroupIds($sharedBy),
410
-					$this->groupManager->getUserGroupIds($sharedWith)
411
-			);
412
-			if (empty($groups)) {
413
-				throw new \Exception('Sharing is only allowed with group members');
414
-			}
415
-		}
416
-
417
-		/*
290
+        $permissions = $share->getNode()->getPermissions();
291
+        $mount = $share->getNode()->getMountPoint();
292
+        if (!($mount instanceof MoveableMount)) {
293
+            $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
294
+        }
295
+
296
+        // Check that we do not share with more permissions than we have
297
+        if ($share->getPermissions() & ~$permissions) {
298
+            $message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
299
+            throw new GenericShareException($message_t, $message_t, 404);
300
+        }
301
+
302
+
303
+        // Check that read permissions are always set
304
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
305
+        $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
306
+            || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
307
+        if (!$noReadPermissionRequired &&
308
+            ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
309
+            throw new \InvalidArgumentException('Shares need at least read permissions');
310
+        }
311
+
312
+        if ($share->getNode() instanceof \OCP\Files\File) {
313
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
314
+                $message_t = $this->l->t('Files can’t be shared with delete permissions');
315
+                throw new GenericShareException($message_t);
316
+            }
317
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
318
+                $message_t = $this->l->t('Files can’t be shared with create permissions');
319
+                throw new GenericShareException($message_t);
320
+            }
321
+        }
322
+    }
323
+
324
+    /**
325
+     * Validate if the expiration date fits the system settings
326
+     *
327
+     * @param \OCP\Share\IShare $share The share to validate the expiration date of
328
+     * @return \OCP\Share\IShare The modified share object
329
+     * @throws GenericShareException
330
+     * @throws \InvalidArgumentException
331
+     * @throws \Exception
332
+     */
333
+    protected function validateExpirationDate(\OCP\Share\IShare $share) {
334
+
335
+        $expirationDate = $share->getExpirationDate();
336
+
337
+        if ($expirationDate !== null) {
338
+            //Make sure the expiration date is a date
339
+            $expirationDate->setTime(0, 0, 0);
340
+
341
+            $date = new \DateTime();
342
+            $date->setTime(0, 0, 0);
343
+            if ($date >= $expirationDate) {
344
+                $message = $this->l->t('Expiration date is in the past');
345
+                throw new GenericShareException($message, $message, 404);
346
+            }
347
+        }
348
+
349
+        // If expiredate is empty set a default one if there is a default
350
+        $fullId = null;
351
+        try {
352
+            $fullId = $share->getFullId();
353
+        } catch (\UnexpectedValueException $e) {
354
+            // This is a new share
355
+        }
356
+
357
+        if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
358
+            $expirationDate = new \DateTime();
359
+            $expirationDate->setTime(0,0,0);
360
+            $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
361
+        }
362
+
363
+        // If we enforce the expiration date check that is does not exceed
364
+        if ($this->shareApiLinkDefaultExpireDateEnforced()) {
365
+            if ($expirationDate === null) {
366
+                throw new \InvalidArgumentException('Expiration date is enforced');
367
+            }
368
+
369
+            $date = new \DateTime();
370
+            $date->setTime(0, 0, 0);
371
+            $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
372
+            if ($date < $expirationDate) {
373
+                $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
374
+                throw new GenericShareException($message, $message, 404);
375
+            }
376
+        }
377
+
378
+        $accepted = true;
379
+        $message = '';
380
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
381
+            'expirationDate' => &$expirationDate,
382
+            'accepted' => &$accepted,
383
+            'message' => &$message,
384
+            'passwordSet' => $share->getPassword() !== null,
385
+        ]);
386
+
387
+        if (!$accepted) {
388
+            throw new \Exception($message);
389
+        }
390
+
391
+        $share->setExpirationDate($expirationDate);
392
+
393
+        return $share;
394
+    }
395
+
396
+    /**
397
+     * Check for pre share requirements for user shares
398
+     *
399
+     * @param \OCP\Share\IShare $share
400
+     * @throws \Exception
401
+     */
402
+    protected function userCreateChecks(\OCP\Share\IShare $share) {
403
+        // Check if we can share with group members only
404
+        if ($this->shareWithGroupMembersOnly()) {
405
+            $sharedBy = $this->userManager->get($share->getSharedBy());
406
+            $sharedWith = $this->userManager->get($share->getSharedWith());
407
+            // Verify we can share with this user
408
+            $groups = array_intersect(
409
+                    $this->groupManager->getUserGroupIds($sharedBy),
410
+                    $this->groupManager->getUserGroupIds($sharedWith)
411
+            );
412
+            if (empty($groups)) {
413
+                throw new \Exception('Sharing is only allowed with group members');
414
+            }
415
+        }
416
+
417
+        /*
418 418
 		 * TODO: Could be costly, fix
419 419
 		 *
420 420
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
421 421
 		 */
422
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
423
-		$existingShares = $provider->getSharesByPath($share->getNode());
424
-		foreach($existingShares as $existingShare) {
425
-			// Ignore if it is the same share
426
-			try {
427
-				if ($existingShare->getFullId() === $share->getFullId()) {
428
-					continue;
429
-				}
430
-			} catch (\UnexpectedValueException $e) {
431
-				//Shares are not identical
432
-			}
433
-
434
-			// Identical share already existst
435
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
436
-				throw new \Exception('Path is already shared with this user');
437
-			}
438
-
439
-			// The share is already shared with this user via a group share
440
-			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
441
-				$group = $this->groupManager->get($existingShare->getSharedWith());
442
-				if (!is_null($group)) {
443
-					$user = $this->userManager->get($share->getSharedWith());
444
-
445
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
446
-						throw new \Exception('Path is already shared with this user');
447
-					}
448
-				}
449
-			}
450
-		}
451
-	}
452
-
453
-	/**
454
-	 * Check for pre share requirements for group shares
455
-	 *
456
-	 * @param \OCP\Share\IShare $share
457
-	 * @throws \Exception
458
-	 */
459
-	protected function groupCreateChecks(\OCP\Share\IShare $share) {
460
-		// Verify group shares are allowed
461
-		if (!$this->allowGroupSharing()) {
462
-			throw new \Exception('Group sharing is now allowed');
463
-		}
464
-
465
-		// Verify if the user can share with this group
466
-		if ($this->shareWithGroupMembersOnly()) {
467
-			$sharedBy = $this->userManager->get($share->getSharedBy());
468
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
469
-			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
470
-				throw new \Exception('Sharing is only allowed within your own groups');
471
-			}
472
-		}
473
-
474
-		/*
422
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
423
+        $existingShares = $provider->getSharesByPath($share->getNode());
424
+        foreach($existingShares as $existingShare) {
425
+            // Ignore if it is the same share
426
+            try {
427
+                if ($existingShare->getFullId() === $share->getFullId()) {
428
+                    continue;
429
+                }
430
+            } catch (\UnexpectedValueException $e) {
431
+                //Shares are not identical
432
+            }
433
+
434
+            // Identical share already existst
435
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
436
+                throw new \Exception('Path is already shared with this user');
437
+            }
438
+
439
+            // The share is already shared with this user via a group share
440
+            if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
441
+                $group = $this->groupManager->get($existingShare->getSharedWith());
442
+                if (!is_null($group)) {
443
+                    $user = $this->userManager->get($share->getSharedWith());
444
+
445
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
446
+                        throw new \Exception('Path is already shared with this user');
447
+                    }
448
+                }
449
+            }
450
+        }
451
+    }
452
+
453
+    /**
454
+     * Check for pre share requirements for group shares
455
+     *
456
+     * @param \OCP\Share\IShare $share
457
+     * @throws \Exception
458
+     */
459
+    protected function groupCreateChecks(\OCP\Share\IShare $share) {
460
+        // Verify group shares are allowed
461
+        if (!$this->allowGroupSharing()) {
462
+            throw new \Exception('Group sharing is now allowed');
463
+        }
464
+
465
+        // Verify if the user can share with this group
466
+        if ($this->shareWithGroupMembersOnly()) {
467
+            $sharedBy = $this->userManager->get($share->getSharedBy());
468
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
469
+            if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
470
+                throw new \Exception('Sharing is only allowed within your own groups');
471
+            }
472
+        }
473
+
474
+        /*
475 475
 		 * TODO: Could be costly, fix
476 476
 		 *
477 477
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
478 478
 		 */
479
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
480
-		$existingShares = $provider->getSharesByPath($share->getNode());
481
-		foreach($existingShares as $existingShare) {
482
-			try {
483
-				if ($existingShare->getFullId() === $share->getFullId()) {
484
-					continue;
485
-				}
486
-			} catch (\UnexpectedValueException $e) {
487
-				//It is a new share so just continue
488
-			}
489
-
490
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
491
-				throw new \Exception('Path is already shared with this group');
492
-			}
493
-		}
494
-	}
495
-
496
-	/**
497
-	 * Check for pre share requirements for link shares
498
-	 *
499
-	 * @param \OCP\Share\IShare $share
500
-	 * @throws \Exception
501
-	 */
502
-	protected function linkCreateChecks(\OCP\Share\IShare $share) {
503
-		// Are link shares allowed?
504
-		if (!$this->shareApiAllowLinks()) {
505
-			throw new \Exception('Link sharing is not allowed');
506
-		}
507
-
508
-		// Link shares by definition can't have share permissions
509
-		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
510
-			throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
511
-		}
512
-
513
-		// Check if public upload is allowed
514
-		if (!$this->shareApiLinkAllowPublicUpload() &&
515
-			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
516
-			throw new \InvalidArgumentException('Public upload is not allowed');
517
-		}
518
-	}
519
-
520
-	/**
521
-	 * To make sure we don't get invisible link shares we set the parent
522
-	 * of a link if it is a reshare. This is a quick word around
523
-	 * until we can properly display multiple link shares in the UI
524
-	 *
525
-	 * See: https://github.com/owncloud/core/issues/22295
526
-	 *
527
-	 * FIXME: Remove once multiple link shares can be properly displayed
528
-	 *
529
-	 * @param \OCP\Share\IShare $share
530
-	 */
531
-	protected function setLinkParent(\OCP\Share\IShare $share) {
532
-
533
-		// No sense in checking if the method is not there.
534
-		if (method_exists($share, 'setParent')) {
535
-			$storage = $share->getNode()->getStorage();
536
-			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
537
-				/** @var \OCA\Files_Sharing\SharedStorage $storage */
538
-				$share->setParent($storage->getShareId());
539
-			}
540
-		}
541
-	}
542
-
543
-	/**
544
-	 * @param File|Folder $path
545
-	 */
546
-	protected function pathCreateChecks($path) {
547
-		// Make sure that we do not share a path that contains a shared mountpoint
548
-		if ($path instanceof \OCP\Files\Folder) {
549
-			$mounts = $this->mountManager->findIn($path->getPath());
550
-			foreach($mounts as $mount) {
551
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
552
-					throw new \InvalidArgumentException('Path contains files shared with you');
553
-				}
554
-			}
555
-		}
556
-	}
557
-
558
-	/**
559
-	 * Check if the user that is sharing can actually share
560
-	 *
561
-	 * @param \OCP\Share\IShare $share
562
-	 * @throws \Exception
563
-	 */
564
-	protected function canShare(\OCP\Share\IShare $share) {
565
-		if (!$this->shareApiEnabled()) {
566
-			throw new \Exception('Sharing is disabled');
567
-		}
568
-
569
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
570
-			throw new \Exception('Sharing is disabled for you');
571
-		}
572
-	}
573
-
574
-	/**
575
-	 * Share a path
576
-	 *
577
-	 * @param \OCP\Share\IShare $share
578
-	 * @return Share The share object
579
-	 * @throws \Exception
580
-	 *
581
-	 * TODO: handle link share permissions or check them
582
-	 */
583
-	public function createShare(\OCP\Share\IShare $share) {
584
-		$this->canShare($share);
585
-
586
-		$this->generalCreateChecks($share);
587
-
588
-		// Verify if there are any issues with the path
589
-		$this->pathCreateChecks($share->getNode());
590
-
591
-		/*
479
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
480
+        $existingShares = $provider->getSharesByPath($share->getNode());
481
+        foreach($existingShares as $existingShare) {
482
+            try {
483
+                if ($existingShare->getFullId() === $share->getFullId()) {
484
+                    continue;
485
+                }
486
+            } catch (\UnexpectedValueException $e) {
487
+                //It is a new share so just continue
488
+            }
489
+
490
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
491
+                throw new \Exception('Path is already shared with this group');
492
+            }
493
+        }
494
+    }
495
+
496
+    /**
497
+     * Check for pre share requirements for link shares
498
+     *
499
+     * @param \OCP\Share\IShare $share
500
+     * @throws \Exception
501
+     */
502
+    protected function linkCreateChecks(\OCP\Share\IShare $share) {
503
+        // Are link shares allowed?
504
+        if (!$this->shareApiAllowLinks()) {
505
+            throw new \Exception('Link sharing is not allowed');
506
+        }
507
+
508
+        // Link shares by definition can't have share permissions
509
+        if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
510
+            throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
511
+        }
512
+
513
+        // Check if public upload is allowed
514
+        if (!$this->shareApiLinkAllowPublicUpload() &&
515
+            ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
516
+            throw new \InvalidArgumentException('Public upload is not allowed');
517
+        }
518
+    }
519
+
520
+    /**
521
+     * To make sure we don't get invisible link shares we set the parent
522
+     * of a link if it is a reshare. This is a quick word around
523
+     * until we can properly display multiple link shares in the UI
524
+     *
525
+     * See: https://github.com/owncloud/core/issues/22295
526
+     *
527
+     * FIXME: Remove once multiple link shares can be properly displayed
528
+     *
529
+     * @param \OCP\Share\IShare $share
530
+     */
531
+    protected function setLinkParent(\OCP\Share\IShare $share) {
532
+
533
+        // No sense in checking if the method is not there.
534
+        if (method_exists($share, 'setParent')) {
535
+            $storage = $share->getNode()->getStorage();
536
+            if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
537
+                /** @var \OCA\Files_Sharing\SharedStorage $storage */
538
+                $share->setParent($storage->getShareId());
539
+            }
540
+        }
541
+    }
542
+
543
+    /**
544
+     * @param File|Folder $path
545
+     */
546
+    protected function pathCreateChecks($path) {
547
+        // Make sure that we do not share a path that contains a shared mountpoint
548
+        if ($path instanceof \OCP\Files\Folder) {
549
+            $mounts = $this->mountManager->findIn($path->getPath());
550
+            foreach($mounts as $mount) {
551
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
552
+                    throw new \InvalidArgumentException('Path contains files shared with you');
553
+                }
554
+            }
555
+        }
556
+    }
557
+
558
+    /**
559
+     * Check if the user that is sharing can actually share
560
+     *
561
+     * @param \OCP\Share\IShare $share
562
+     * @throws \Exception
563
+     */
564
+    protected function canShare(\OCP\Share\IShare $share) {
565
+        if (!$this->shareApiEnabled()) {
566
+            throw new \Exception('Sharing is disabled');
567
+        }
568
+
569
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
570
+            throw new \Exception('Sharing is disabled for you');
571
+        }
572
+    }
573
+
574
+    /**
575
+     * Share a path
576
+     *
577
+     * @param \OCP\Share\IShare $share
578
+     * @return Share The share object
579
+     * @throws \Exception
580
+     *
581
+     * TODO: handle link share permissions or check them
582
+     */
583
+    public function createShare(\OCP\Share\IShare $share) {
584
+        $this->canShare($share);
585
+
586
+        $this->generalCreateChecks($share);
587
+
588
+        // Verify if there are any issues with the path
589
+        $this->pathCreateChecks($share->getNode());
590
+
591
+        /*
592 592
 		 * On creation of a share the owner is always the owner of the path
593 593
 		 * Except for mounted federated shares.
594 594
 		 */
595
-		$storage = $share->getNode()->getStorage();
596
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
597
-			$parent = $share->getNode()->getParent();
598
-			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
599
-				$parent = $parent->getParent();
600
-			}
601
-			$share->setShareOwner($parent->getOwner()->getUID());
602
-		} else {
603
-			$share->setShareOwner($share->getNode()->getOwner()->getUID());
604
-		}
605
-
606
-		//Verify share type
607
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
608
-			$this->userCreateChecks($share);
609
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
610
-			$this->groupCreateChecks($share);
611
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
612
-			$this->linkCreateChecks($share);
613
-			$this->setLinkParent($share);
614
-
615
-			/*
595
+        $storage = $share->getNode()->getStorage();
596
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
597
+            $parent = $share->getNode()->getParent();
598
+            while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
599
+                $parent = $parent->getParent();
600
+            }
601
+            $share->setShareOwner($parent->getOwner()->getUID());
602
+        } else {
603
+            $share->setShareOwner($share->getNode()->getOwner()->getUID());
604
+        }
605
+
606
+        //Verify share type
607
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
608
+            $this->userCreateChecks($share);
609
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
610
+            $this->groupCreateChecks($share);
611
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
612
+            $this->linkCreateChecks($share);
613
+            $this->setLinkParent($share);
614
+
615
+            /*
616 616
 			 * For now ignore a set token.
617 617
 			 */
618
-			$share->setToken(
619
-				$this->secureRandom->generate(
620
-					\OC\Share\Constants::TOKEN_LENGTH,
621
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
622
-				)
623
-			);
624
-
625
-			//Verify the expiration date
626
-			$this->validateExpirationDate($share);
627
-
628
-			//Verify the password
629
-			$this->verifyPassword($share->getPassword());
630
-
631
-			// If a password is set. Hash it!
632
-			if ($share->getPassword() !== null) {
633
-				$share->setPassword($this->hasher->hash($share->getPassword()));
634
-			}
635
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
636
-			$share->setToken(
637
-				$this->secureRandom->generate(
638
-					\OC\Share\Constants::TOKEN_LENGTH,
639
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
640
-				)
641
-			);
642
-		}
643
-
644
-		// Cannot share with the owner
645
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
646
-			$share->getSharedWith() === $share->getShareOwner()) {
647
-			throw new \InvalidArgumentException('Can’t share with the share owner');
648
-		}
649
-
650
-		// Generate the target
651
-		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
652
-		$target = \OC\Files\Filesystem::normalizePath($target);
653
-		$share->setTarget($target);
654
-
655
-		// Pre share event
656
-		$event = new GenericEvent($share);
657
-		$a = $this->eventDispatcher->dispatch('OCP\Share::preShare', $event);
658
-		if ($event->isPropagationStopped() && $event->hasArgument('error')) {
659
-			throw new \Exception($event->getArgument('error'));
660
-		}
661
-
662
-		$oldShare = $share;
663
-		$provider = $this->factory->getProviderForType($share->getShareType());
664
-		$share = $provider->create($share);
665
-		//reuse the node we already have
666
-		$share->setNode($oldShare->getNode());
667
-
668
-		// Post share event
669
-		$event = new GenericEvent($share);
670
-		$this->eventDispatcher->dispatch('OCP\Share::postShare', $event);
671
-
672
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
673
-			$mailSend = $share->getMailSend();
674
-			if($mailSend === true) {
675
-				$user = $this->userManager->get($share->getSharedWith());
676
-				if ($user !== null) {
677
-					$emailAddress = $user->getEMailAddress();
678
-					if ($emailAddress !== null && $emailAddress !== '') {
679
-						$userLang = $this->config->getUserValue($share->getSharedWith(), 'core', 'lang', null);
680
-						$l = $this->l10nFactory->get('lib', $userLang);
681
-						$this->sendMailNotification(
682
-							$l,
683
-							$share->getNode()->getName(),
684
-							$this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]),
685
-							$share->getSharedBy(),
686
-							$emailAddress,
687
-							$share->getExpirationDate()
688
-						);
689
-						$this->logger->debug('Send share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
690
-					} else {
691
-						$this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
692
-					}
693
-				} else {
694
-					$this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
695
-				}
696
-			} else {
697
-				$this->logger->debug('Share notification not send because mailsend is false.', ['app' => 'share']);
698
-			}
699
-		}
700
-
701
-		return $share;
702
-	}
703
-
704
-	/**
705
-	 * @param IL10N $l Language of the recipient
706
-	 * @param string $filename file/folder name
707
-	 * @param string $link link to the file/folder
708
-	 * @param string $initiator user ID of share sender
709
-	 * @param string $shareWith email address of share receiver
710
-	 * @param \DateTime|null $expiration
711
-	 * @throws \Exception If mail couldn't be sent
712
-	 */
713
-	protected function sendMailNotification(IL10N $l,
714
-											$filename,
715
-											$link,
716
-											$initiator,
717
-											$shareWith,
718
-											\DateTime $expiration = null) {
719
-		$initiatorUser = $this->userManager->get($initiator);
720
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
721
-
722
-		$message = $this->mailer->createMessage();
723
-
724
-		$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
725
-			'filename' => $filename,
726
-			'link' => $link,
727
-			'initiator' => $initiatorDisplayName,
728
-			'expiration' => $expiration,
729
-			'shareWith' => $shareWith,
730
-		]);
731
-
732
-		$emailTemplate->setSubject($l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
733
-		$emailTemplate->addHeader();
734
-		$emailTemplate->addHeading($l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
735
-		$text = $l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
736
-
737
-		$emailTemplate->addBodyText(
738
-			htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
739
-			$text
740
-		);
741
-		$emailTemplate->addBodyButton(
742
-			$l->t('Open »%s«', [$filename]),
743
-			$link
744
-		);
745
-
746
-		$message->setTo([$shareWith]);
747
-
748
-		// The "From" contains the sharers name
749
-		$instanceName = $this->defaults->getName();
750
-		$senderName = $l->t(
751
-			'%s via %s',
752
-			[
753
-				$initiatorDisplayName,
754
-				$instanceName
755
-			]
756
-		);
757
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
758
-
759
-		// The "Reply-To" is set to the sharer if an mail address is configured
760
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
761
-		$initiatorEmail = $initiatorUser->getEMailAddress();
762
-		if($initiatorEmail !== null) {
763
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
764
-			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
765
-		} else {
766
-			$emailTemplate->addFooter();
767
-		}
768
-
769
-		$message->useTemplate($emailTemplate);
770
-		$this->mailer->send($message);
771
-	}
772
-
773
-	/**
774
-	 * Update a share
775
-	 *
776
-	 * @param \OCP\Share\IShare $share
777
-	 * @return \OCP\Share\IShare The share object
778
-	 * @throws \InvalidArgumentException
779
-	 */
780
-	public function updateShare(\OCP\Share\IShare $share) {
781
-		$expirationDateUpdated = false;
782
-
783
-		$this->canShare($share);
784
-
785
-		try {
786
-			$originalShare = $this->getShareById($share->getFullId());
787
-		} catch (\UnexpectedValueException $e) {
788
-			throw new \InvalidArgumentException('Share does not have a full id');
789
-		}
790
-
791
-		// We can't change the share type!
792
-		if ($share->getShareType() !== $originalShare->getShareType()) {
793
-			throw new \InvalidArgumentException('Can’t change share type');
794
-		}
795
-
796
-		// We can only change the recipient on user shares
797
-		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
798
-		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
799
-			throw new \InvalidArgumentException('Can only update recipient on user shares');
800
-		}
801
-
802
-		// Cannot share with the owner
803
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
804
-			$share->getSharedWith() === $share->getShareOwner()) {
805
-			throw new \InvalidArgumentException('Can’t share with the share owner');
806
-		}
807
-
808
-		$this->generalCreateChecks($share);
809
-
810
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
811
-			$this->userCreateChecks($share);
812
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
813
-			$this->groupCreateChecks($share);
814
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
815
-			$this->linkCreateChecks($share);
816
-
817
-			$this->updateSharePasswordIfNeeded($share, $originalShare);
818
-
819
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
820
-				//Verify the expiration date
821
-				$this->validateExpirationDate($share);
822
-				$expirationDateUpdated = true;
823
-			}
824
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
825
-			$plainTextPassword = $share->getPassword();
826
-			if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
827
-				$plainTextPassword = null;
828
-			}
829
-		}
830
-
831
-		$this->pathCreateChecks($share->getNode());
832
-
833
-		// Now update the share!
834
-		$provider = $this->factory->getProviderForType($share->getShareType());
835
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
836
-			$share = $provider->update($share, $plainTextPassword);
837
-		} else {
838
-			$share = $provider->update($share);
839
-		}
840
-
841
-		if ($expirationDateUpdated === true) {
842
-			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
843
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
844
-				'itemSource' => $share->getNode()->getId(),
845
-				'date' => $share->getExpirationDate(),
846
-				'uidOwner' => $share->getSharedBy(),
847
-			]);
848
-		}
849
-
850
-		if ($share->getPassword() !== $originalShare->getPassword()) {
851
-			\OC_Hook::emit(Share::class, 'post_update_password', [
852
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
853
-				'itemSource' => $share->getNode()->getId(),
854
-				'uidOwner' => $share->getSharedBy(),
855
-				'token' => $share->getToken(),
856
-				'disabled' => is_null($share->getPassword()),
857
-			]);
858
-		}
859
-
860
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
861
-			if ($this->userManager->userExists($share->getShareOwner())) {
862
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
863
-			} else {
864
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
865
-			}
866
-			\OC_Hook::emit(Share::class, 'post_update_permissions', array(
867
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
868
-				'itemSource' => $share->getNode()->getId(),
869
-				'shareType' => $share->getShareType(),
870
-				'shareWith' => $share->getSharedWith(),
871
-				'uidOwner' => $share->getSharedBy(),
872
-				'permissions' => $share->getPermissions(),
873
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
874
-			));
875
-		}
876
-
877
-		return $share;
878
-	}
879
-
880
-	/**
881
-	 * Updates the password of the given share if it is not the same as the
882
-	 * password of the original share.
883
-	 *
884
-	 * @param \OCP\Share\IShare $share the share to update its password.
885
-	 * @param \OCP\Share\IShare $originalShare the original share to compare its
886
-	 *        password with.
887
-	 * @return boolean whether the password was updated or not.
888
-	 */
889
-	private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
890
-		// Password updated.
891
-		if ($share->getPassword() !== $originalShare->getPassword()) {
892
-			//Verify the password
893
-			$this->verifyPassword($share->getPassword());
894
-
895
-			// If a password is set. Hash it!
896
-			if ($share->getPassword() !== null) {
897
-				$share->setPassword($this->hasher->hash($share->getPassword()));
898
-
899
-				return true;
900
-			}
901
-		}
902
-
903
-		return false;
904
-	}
905
-
906
-	/**
907
-	 * Delete all the children of this share
908
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
909
-	 *
910
-	 * @param \OCP\Share\IShare $share
911
-	 * @return \OCP\Share\IShare[] List of deleted shares
912
-	 */
913
-	protected function deleteChildren(\OCP\Share\IShare $share) {
914
-		$deletedShares = [];
915
-
916
-		$provider = $this->factory->getProviderForType($share->getShareType());
917
-
918
-		foreach ($provider->getChildren($share) as $child) {
919
-			$deletedChildren = $this->deleteChildren($child);
920
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
921
-
922
-			$provider->delete($child);
923
-			$deletedShares[] = $child;
924
-		}
925
-
926
-		return $deletedShares;
927
-	}
928
-
929
-	/**
930
-	 * Delete a share
931
-	 *
932
-	 * @param \OCP\Share\IShare $share
933
-	 * @throws ShareNotFound
934
-	 * @throws \InvalidArgumentException
935
-	 */
936
-	public function deleteShare(\OCP\Share\IShare $share) {
937
-
938
-		try {
939
-			$share->getFullId();
940
-		} catch (\UnexpectedValueException $e) {
941
-			throw new \InvalidArgumentException('Share does not have a full id');
942
-		}
943
-
944
-		$event = new GenericEvent($share);
945
-		$this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
946
-
947
-		// Get all children and delete them as well
948
-		$deletedShares = $this->deleteChildren($share);
949
-
950
-		// Do the actual delete
951
-		$provider = $this->factory->getProviderForType($share->getShareType());
952
-		$provider->delete($share);
953
-
954
-		// All the deleted shares caused by this delete
955
-		$deletedShares[] = $share;
956
-
957
-		// Emit post hook
958
-		$event->setArgument('deletedShares', $deletedShares);
959
-		$this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
960
-	}
961
-
962
-
963
-	/**
964
-	 * Unshare a file as the recipient.
965
-	 * This can be different from a regular delete for example when one of
966
-	 * the users in a groups deletes that share. But the provider should
967
-	 * handle this.
968
-	 *
969
-	 * @param \OCP\Share\IShare $share
970
-	 * @param string $recipientId
971
-	 */
972
-	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
973
-		list($providerId, ) = $this->splitFullId($share->getFullId());
974
-		$provider = $this->factory->getProvider($providerId);
975
-
976
-		$provider->deleteFromSelf($share, $recipientId);
977
-		$event = new GenericEvent($share);
978
-		$this->eventDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
979
-	}
980
-
981
-	/**
982
-	 * @inheritdoc
983
-	 */
984
-	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
985
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
986
-			throw new \InvalidArgumentException('Can’t change target of link share');
987
-		}
988
-
989
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
990
-			throw new \InvalidArgumentException('Invalid recipient');
991
-		}
992
-
993
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
994
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
995
-			if (is_null($sharedWith)) {
996
-				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
997
-			}
998
-			$recipient = $this->userManager->get($recipientId);
999
-			if (!$sharedWith->inGroup($recipient)) {
1000
-				throw new \InvalidArgumentException('Invalid recipient');
1001
-			}
1002
-		}
1003
-
1004
-		list($providerId, ) = $this->splitFullId($share->getFullId());
1005
-		$provider = $this->factory->getProvider($providerId);
1006
-
1007
-		$provider->move($share, $recipientId);
1008
-	}
1009
-
1010
-	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
1011
-		$providers = $this->factory->getAllProviders();
1012
-
1013
-		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
1014
-			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
1015
-			foreach ($newShares as $fid => $data) {
1016
-				if (!isset($shares[$fid])) {
1017
-					$shares[$fid] = [];
1018
-				}
1019
-
1020
-				$shares[$fid] = array_merge($shares[$fid], $data);
1021
-			}
1022
-			return $shares;
1023
-		}, []);
1024
-	}
1025
-
1026
-	/**
1027
-	 * @inheritdoc
1028
-	 */
1029
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1030
-		if ($path !== null &&
1031
-				!($path instanceof \OCP\Files\File) &&
1032
-				!($path instanceof \OCP\Files\Folder)) {
1033
-			throw new \InvalidArgumentException('invalid path');
1034
-		}
1035
-
1036
-		try {
1037
-			$provider = $this->factory->getProviderForType($shareType);
1038
-		} catch (ProviderException $e) {
1039
-			return [];
1040
-		}
1041
-
1042
-		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1043
-
1044
-		/*
618
+            $share->setToken(
619
+                $this->secureRandom->generate(
620
+                    \OC\Share\Constants::TOKEN_LENGTH,
621
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
622
+                )
623
+            );
624
+
625
+            //Verify the expiration date
626
+            $this->validateExpirationDate($share);
627
+
628
+            //Verify the password
629
+            $this->verifyPassword($share->getPassword());
630
+
631
+            // If a password is set. Hash it!
632
+            if ($share->getPassword() !== null) {
633
+                $share->setPassword($this->hasher->hash($share->getPassword()));
634
+            }
635
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
636
+            $share->setToken(
637
+                $this->secureRandom->generate(
638
+                    \OC\Share\Constants::TOKEN_LENGTH,
639
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
640
+                )
641
+            );
642
+        }
643
+
644
+        // Cannot share with the owner
645
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
646
+            $share->getSharedWith() === $share->getShareOwner()) {
647
+            throw new \InvalidArgumentException('Can’t share with the share owner');
648
+        }
649
+
650
+        // Generate the target
651
+        $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
652
+        $target = \OC\Files\Filesystem::normalizePath($target);
653
+        $share->setTarget($target);
654
+
655
+        // Pre share event
656
+        $event = new GenericEvent($share);
657
+        $a = $this->eventDispatcher->dispatch('OCP\Share::preShare', $event);
658
+        if ($event->isPropagationStopped() && $event->hasArgument('error')) {
659
+            throw new \Exception($event->getArgument('error'));
660
+        }
661
+
662
+        $oldShare = $share;
663
+        $provider = $this->factory->getProviderForType($share->getShareType());
664
+        $share = $provider->create($share);
665
+        //reuse the node we already have
666
+        $share->setNode($oldShare->getNode());
667
+
668
+        // Post share event
669
+        $event = new GenericEvent($share);
670
+        $this->eventDispatcher->dispatch('OCP\Share::postShare', $event);
671
+
672
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
673
+            $mailSend = $share->getMailSend();
674
+            if($mailSend === true) {
675
+                $user = $this->userManager->get($share->getSharedWith());
676
+                if ($user !== null) {
677
+                    $emailAddress = $user->getEMailAddress();
678
+                    if ($emailAddress !== null && $emailAddress !== '') {
679
+                        $userLang = $this->config->getUserValue($share->getSharedWith(), 'core', 'lang', null);
680
+                        $l = $this->l10nFactory->get('lib', $userLang);
681
+                        $this->sendMailNotification(
682
+                            $l,
683
+                            $share->getNode()->getName(),
684
+                            $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]),
685
+                            $share->getSharedBy(),
686
+                            $emailAddress,
687
+                            $share->getExpirationDate()
688
+                        );
689
+                        $this->logger->debug('Send share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
690
+                    } else {
691
+                        $this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
692
+                    }
693
+                } else {
694
+                    $this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
695
+                }
696
+            } else {
697
+                $this->logger->debug('Share notification not send because mailsend is false.', ['app' => 'share']);
698
+            }
699
+        }
700
+
701
+        return $share;
702
+    }
703
+
704
+    /**
705
+     * @param IL10N $l Language of the recipient
706
+     * @param string $filename file/folder name
707
+     * @param string $link link to the file/folder
708
+     * @param string $initiator user ID of share sender
709
+     * @param string $shareWith email address of share receiver
710
+     * @param \DateTime|null $expiration
711
+     * @throws \Exception If mail couldn't be sent
712
+     */
713
+    protected function sendMailNotification(IL10N $l,
714
+                                            $filename,
715
+                                            $link,
716
+                                            $initiator,
717
+                                            $shareWith,
718
+                                            \DateTime $expiration = null) {
719
+        $initiatorUser = $this->userManager->get($initiator);
720
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
721
+
722
+        $message = $this->mailer->createMessage();
723
+
724
+        $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
725
+            'filename' => $filename,
726
+            'link' => $link,
727
+            'initiator' => $initiatorDisplayName,
728
+            'expiration' => $expiration,
729
+            'shareWith' => $shareWith,
730
+        ]);
731
+
732
+        $emailTemplate->setSubject($l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
733
+        $emailTemplate->addHeader();
734
+        $emailTemplate->addHeading($l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
735
+        $text = $l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
736
+
737
+        $emailTemplate->addBodyText(
738
+            htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
739
+            $text
740
+        );
741
+        $emailTemplate->addBodyButton(
742
+            $l->t('Open »%s«', [$filename]),
743
+            $link
744
+        );
745
+
746
+        $message->setTo([$shareWith]);
747
+
748
+        // The "From" contains the sharers name
749
+        $instanceName = $this->defaults->getName();
750
+        $senderName = $l->t(
751
+            '%s via %s',
752
+            [
753
+                $initiatorDisplayName,
754
+                $instanceName
755
+            ]
756
+        );
757
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
758
+
759
+        // The "Reply-To" is set to the sharer if an mail address is configured
760
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
761
+        $initiatorEmail = $initiatorUser->getEMailAddress();
762
+        if($initiatorEmail !== null) {
763
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
764
+            $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
765
+        } else {
766
+            $emailTemplate->addFooter();
767
+        }
768
+
769
+        $message->useTemplate($emailTemplate);
770
+        $this->mailer->send($message);
771
+    }
772
+
773
+    /**
774
+     * Update a share
775
+     *
776
+     * @param \OCP\Share\IShare $share
777
+     * @return \OCP\Share\IShare The share object
778
+     * @throws \InvalidArgumentException
779
+     */
780
+    public function updateShare(\OCP\Share\IShare $share) {
781
+        $expirationDateUpdated = false;
782
+
783
+        $this->canShare($share);
784
+
785
+        try {
786
+            $originalShare = $this->getShareById($share->getFullId());
787
+        } catch (\UnexpectedValueException $e) {
788
+            throw new \InvalidArgumentException('Share does not have a full id');
789
+        }
790
+
791
+        // We can't change the share type!
792
+        if ($share->getShareType() !== $originalShare->getShareType()) {
793
+            throw new \InvalidArgumentException('Can’t change share type');
794
+        }
795
+
796
+        // We can only change the recipient on user shares
797
+        if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
798
+            $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
799
+            throw new \InvalidArgumentException('Can only update recipient on user shares');
800
+        }
801
+
802
+        // Cannot share with the owner
803
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
804
+            $share->getSharedWith() === $share->getShareOwner()) {
805
+            throw new \InvalidArgumentException('Can’t share with the share owner');
806
+        }
807
+
808
+        $this->generalCreateChecks($share);
809
+
810
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
811
+            $this->userCreateChecks($share);
812
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
813
+            $this->groupCreateChecks($share);
814
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
815
+            $this->linkCreateChecks($share);
816
+
817
+            $this->updateSharePasswordIfNeeded($share, $originalShare);
818
+
819
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
820
+                //Verify the expiration date
821
+                $this->validateExpirationDate($share);
822
+                $expirationDateUpdated = true;
823
+            }
824
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
825
+            $plainTextPassword = $share->getPassword();
826
+            if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
827
+                $plainTextPassword = null;
828
+            }
829
+        }
830
+
831
+        $this->pathCreateChecks($share->getNode());
832
+
833
+        // Now update the share!
834
+        $provider = $this->factory->getProviderForType($share->getShareType());
835
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
836
+            $share = $provider->update($share, $plainTextPassword);
837
+        } else {
838
+            $share = $provider->update($share);
839
+        }
840
+
841
+        if ($expirationDateUpdated === true) {
842
+            \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
843
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
844
+                'itemSource' => $share->getNode()->getId(),
845
+                'date' => $share->getExpirationDate(),
846
+                'uidOwner' => $share->getSharedBy(),
847
+            ]);
848
+        }
849
+
850
+        if ($share->getPassword() !== $originalShare->getPassword()) {
851
+            \OC_Hook::emit(Share::class, 'post_update_password', [
852
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
853
+                'itemSource' => $share->getNode()->getId(),
854
+                'uidOwner' => $share->getSharedBy(),
855
+                'token' => $share->getToken(),
856
+                'disabled' => is_null($share->getPassword()),
857
+            ]);
858
+        }
859
+
860
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
861
+            if ($this->userManager->userExists($share->getShareOwner())) {
862
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
863
+            } else {
864
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
865
+            }
866
+            \OC_Hook::emit(Share::class, 'post_update_permissions', array(
867
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
868
+                'itemSource' => $share->getNode()->getId(),
869
+                'shareType' => $share->getShareType(),
870
+                'shareWith' => $share->getSharedWith(),
871
+                'uidOwner' => $share->getSharedBy(),
872
+                'permissions' => $share->getPermissions(),
873
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
874
+            ));
875
+        }
876
+
877
+        return $share;
878
+    }
879
+
880
+    /**
881
+     * Updates the password of the given share if it is not the same as the
882
+     * password of the original share.
883
+     *
884
+     * @param \OCP\Share\IShare $share the share to update its password.
885
+     * @param \OCP\Share\IShare $originalShare the original share to compare its
886
+     *        password with.
887
+     * @return boolean whether the password was updated or not.
888
+     */
889
+    private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
890
+        // Password updated.
891
+        if ($share->getPassword() !== $originalShare->getPassword()) {
892
+            //Verify the password
893
+            $this->verifyPassword($share->getPassword());
894
+
895
+            // If a password is set. Hash it!
896
+            if ($share->getPassword() !== null) {
897
+                $share->setPassword($this->hasher->hash($share->getPassword()));
898
+
899
+                return true;
900
+            }
901
+        }
902
+
903
+        return false;
904
+    }
905
+
906
+    /**
907
+     * Delete all the children of this share
908
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
909
+     *
910
+     * @param \OCP\Share\IShare $share
911
+     * @return \OCP\Share\IShare[] List of deleted shares
912
+     */
913
+    protected function deleteChildren(\OCP\Share\IShare $share) {
914
+        $deletedShares = [];
915
+
916
+        $provider = $this->factory->getProviderForType($share->getShareType());
917
+
918
+        foreach ($provider->getChildren($share) as $child) {
919
+            $deletedChildren = $this->deleteChildren($child);
920
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
921
+
922
+            $provider->delete($child);
923
+            $deletedShares[] = $child;
924
+        }
925
+
926
+        return $deletedShares;
927
+    }
928
+
929
+    /**
930
+     * Delete a share
931
+     *
932
+     * @param \OCP\Share\IShare $share
933
+     * @throws ShareNotFound
934
+     * @throws \InvalidArgumentException
935
+     */
936
+    public function deleteShare(\OCP\Share\IShare $share) {
937
+
938
+        try {
939
+            $share->getFullId();
940
+        } catch (\UnexpectedValueException $e) {
941
+            throw new \InvalidArgumentException('Share does not have a full id');
942
+        }
943
+
944
+        $event = new GenericEvent($share);
945
+        $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
946
+
947
+        // Get all children and delete them as well
948
+        $deletedShares = $this->deleteChildren($share);
949
+
950
+        // Do the actual delete
951
+        $provider = $this->factory->getProviderForType($share->getShareType());
952
+        $provider->delete($share);
953
+
954
+        // All the deleted shares caused by this delete
955
+        $deletedShares[] = $share;
956
+
957
+        // Emit post hook
958
+        $event->setArgument('deletedShares', $deletedShares);
959
+        $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
960
+    }
961
+
962
+
963
+    /**
964
+     * Unshare a file as the recipient.
965
+     * This can be different from a regular delete for example when one of
966
+     * the users in a groups deletes that share. But the provider should
967
+     * handle this.
968
+     *
969
+     * @param \OCP\Share\IShare $share
970
+     * @param string $recipientId
971
+     */
972
+    public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
973
+        list($providerId, ) = $this->splitFullId($share->getFullId());
974
+        $provider = $this->factory->getProvider($providerId);
975
+
976
+        $provider->deleteFromSelf($share, $recipientId);
977
+        $event = new GenericEvent($share);
978
+        $this->eventDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
979
+    }
980
+
981
+    /**
982
+     * @inheritdoc
983
+     */
984
+    public function moveShare(\OCP\Share\IShare $share, $recipientId) {
985
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
986
+            throw new \InvalidArgumentException('Can’t change target of link share');
987
+        }
988
+
989
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
990
+            throw new \InvalidArgumentException('Invalid recipient');
991
+        }
992
+
993
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
994
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
995
+            if (is_null($sharedWith)) {
996
+                throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
997
+            }
998
+            $recipient = $this->userManager->get($recipientId);
999
+            if (!$sharedWith->inGroup($recipient)) {
1000
+                throw new \InvalidArgumentException('Invalid recipient');
1001
+            }
1002
+        }
1003
+
1004
+        list($providerId, ) = $this->splitFullId($share->getFullId());
1005
+        $provider = $this->factory->getProvider($providerId);
1006
+
1007
+        $provider->move($share, $recipientId);
1008
+    }
1009
+
1010
+    public function getSharesInFolder($userId, Folder $node, $reshares = false) {
1011
+        $providers = $this->factory->getAllProviders();
1012
+
1013
+        return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
1014
+            $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
1015
+            foreach ($newShares as $fid => $data) {
1016
+                if (!isset($shares[$fid])) {
1017
+                    $shares[$fid] = [];
1018
+                }
1019
+
1020
+                $shares[$fid] = array_merge($shares[$fid], $data);
1021
+            }
1022
+            return $shares;
1023
+        }, []);
1024
+    }
1025
+
1026
+    /**
1027
+     * @inheritdoc
1028
+     */
1029
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1030
+        if ($path !== null &&
1031
+                !($path instanceof \OCP\Files\File) &&
1032
+                !($path instanceof \OCP\Files\Folder)) {
1033
+            throw new \InvalidArgumentException('invalid path');
1034
+        }
1035
+
1036
+        try {
1037
+            $provider = $this->factory->getProviderForType($shareType);
1038
+        } catch (ProviderException $e) {
1039
+            return [];
1040
+        }
1041
+
1042
+        $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1043
+
1044
+        /*
1045 1045
 		 * Work around so we don't return expired shares but still follow
1046 1046
 		 * proper pagination.
1047 1047
 		 */
1048 1048
 
1049
-		$shares2 = [];
1050
-
1051
-		while(true) {
1052
-			$added = 0;
1053
-			foreach ($shares as $share) {
1054
-
1055
-				try {
1056
-					$this->checkExpireDate($share);
1057
-				} catch (ShareNotFound $e) {
1058
-					//Ignore since this basically means the share is deleted
1059
-					continue;
1060
-				}
1061
-
1062
-				$added++;
1063
-				$shares2[] = $share;
1064
-
1065
-				if (count($shares2) === $limit) {
1066
-					break;
1067
-				}
1068
-			}
1069
-
1070
-			// If we did not fetch more shares than the limit then there are no more shares
1071
-			if (count($shares) < $limit) {
1072
-				break;
1073
-			}
1074
-
1075
-			if (count($shares2) === $limit) {
1076
-				break;
1077
-			}
1078
-
1079
-			// If there was no limit on the select we are done
1080
-			if ($limit === -1) {
1081
-				break;
1082
-			}
1083
-
1084
-			$offset += $added;
1085
-
1086
-			// Fetch again $limit shares
1087
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1088
-
1089
-			// No more shares means we are done
1090
-			if (empty($shares)) {
1091
-				break;
1092
-			}
1093
-		}
1094
-
1095
-		$shares = $shares2;
1096
-
1097
-		return $shares;
1098
-	}
1099
-
1100
-	/**
1101
-	 * @inheritdoc
1102
-	 */
1103
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1104
-		try {
1105
-			$provider = $this->factory->getProviderForType($shareType);
1106
-		} catch (ProviderException $e) {
1107
-			return [];
1108
-		}
1109
-
1110
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1111
-
1112
-		// remove all shares which are already expired
1113
-		foreach ($shares as $key => $share) {
1114
-			try {
1115
-				$this->checkExpireDate($share);
1116
-			} catch (ShareNotFound $e) {
1117
-				unset($shares[$key]);
1118
-			}
1119
-		}
1120
-
1121
-		return $shares;
1122
-	}
1123
-
1124
-	/**
1125
-	 * @inheritdoc
1126
-	 */
1127
-	public function getShareById($id, $recipient = null) {
1128
-		if ($id === null) {
1129
-			throw new ShareNotFound();
1130
-		}
1131
-
1132
-		list($providerId, $id) = $this->splitFullId($id);
1133
-
1134
-		try {
1135
-			$provider = $this->factory->getProvider($providerId);
1136
-		} catch (ProviderException $e) {
1137
-			throw new ShareNotFound();
1138
-		}
1139
-
1140
-		$share = $provider->getShareById($id, $recipient);
1141
-
1142
-		$this->checkExpireDate($share);
1143
-
1144
-		return $share;
1145
-	}
1146
-
1147
-	/**
1148
-	 * Get all the shares for a given path
1149
-	 *
1150
-	 * @param \OCP\Files\Node $path
1151
-	 * @param int $page
1152
-	 * @param int $perPage
1153
-	 *
1154
-	 * @return Share[]
1155
-	 */
1156
-	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1157
-		return [];
1158
-	}
1159
-
1160
-	/**
1161
-	 * Get the share by token possible with password
1162
-	 *
1163
-	 * @param string $token
1164
-	 * @return Share
1165
-	 *
1166
-	 * @throws ShareNotFound
1167
-	 */
1168
-	public function getShareByToken($token) {
1169
-		$share = null;
1170
-		try {
1171
-			if($this->shareApiAllowLinks()) {
1172
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1173
-				$share = $provider->getShareByToken($token);
1174
-			}
1175
-		} catch (ProviderException $e) {
1176
-		} catch (ShareNotFound $e) {
1177
-		}
1178
-
1179
-
1180
-		// If it is not a link share try to fetch a federated share by token
1181
-		if ($share === null) {
1182
-			try {
1183
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1184
-				$share = $provider->getShareByToken($token);
1185
-			} catch (ProviderException $e) {
1186
-			} catch (ShareNotFound $e) {
1187
-			}
1188
-		}
1189
-
1190
-		// If it is not a link share try to fetch a mail share by token
1191
-		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1192
-			try {
1193
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1194
-				$share = $provider->getShareByToken($token);
1195
-			} catch (ProviderException $e) {
1196
-			} catch (ShareNotFound $e) {
1197
-			}
1198
-		}
1199
-
1200
-		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
1201
-			try {
1202
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_CIRCLE);
1203
-				$share = $provider->getShareByToken($token);
1204
-			} catch (ProviderException $e) {
1205
-			} catch (ShareNotFound $e) {
1206
-			}
1207
-		}
1208
-
1209
-		if ($share === null) {
1210
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1211
-		}
1212
-
1213
-		$this->checkExpireDate($share);
1214
-
1215
-		/*
1049
+        $shares2 = [];
1050
+
1051
+        while(true) {
1052
+            $added = 0;
1053
+            foreach ($shares as $share) {
1054
+
1055
+                try {
1056
+                    $this->checkExpireDate($share);
1057
+                } catch (ShareNotFound $e) {
1058
+                    //Ignore since this basically means the share is deleted
1059
+                    continue;
1060
+                }
1061
+
1062
+                $added++;
1063
+                $shares2[] = $share;
1064
+
1065
+                if (count($shares2) === $limit) {
1066
+                    break;
1067
+                }
1068
+            }
1069
+
1070
+            // If we did not fetch more shares than the limit then there are no more shares
1071
+            if (count($shares) < $limit) {
1072
+                break;
1073
+            }
1074
+
1075
+            if (count($shares2) === $limit) {
1076
+                break;
1077
+            }
1078
+
1079
+            // If there was no limit on the select we are done
1080
+            if ($limit === -1) {
1081
+                break;
1082
+            }
1083
+
1084
+            $offset += $added;
1085
+
1086
+            // Fetch again $limit shares
1087
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1088
+
1089
+            // No more shares means we are done
1090
+            if (empty($shares)) {
1091
+                break;
1092
+            }
1093
+        }
1094
+
1095
+        $shares = $shares2;
1096
+
1097
+        return $shares;
1098
+    }
1099
+
1100
+    /**
1101
+     * @inheritdoc
1102
+     */
1103
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1104
+        try {
1105
+            $provider = $this->factory->getProviderForType($shareType);
1106
+        } catch (ProviderException $e) {
1107
+            return [];
1108
+        }
1109
+
1110
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1111
+
1112
+        // remove all shares which are already expired
1113
+        foreach ($shares as $key => $share) {
1114
+            try {
1115
+                $this->checkExpireDate($share);
1116
+            } catch (ShareNotFound $e) {
1117
+                unset($shares[$key]);
1118
+            }
1119
+        }
1120
+
1121
+        return $shares;
1122
+    }
1123
+
1124
+    /**
1125
+     * @inheritdoc
1126
+     */
1127
+    public function getShareById($id, $recipient = null) {
1128
+        if ($id === null) {
1129
+            throw new ShareNotFound();
1130
+        }
1131
+
1132
+        list($providerId, $id) = $this->splitFullId($id);
1133
+
1134
+        try {
1135
+            $provider = $this->factory->getProvider($providerId);
1136
+        } catch (ProviderException $e) {
1137
+            throw new ShareNotFound();
1138
+        }
1139
+
1140
+        $share = $provider->getShareById($id, $recipient);
1141
+
1142
+        $this->checkExpireDate($share);
1143
+
1144
+        return $share;
1145
+    }
1146
+
1147
+    /**
1148
+     * Get all the shares for a given path
1149
+     *
1150
+     * @param \OCP\Files\Node $path
1151
+     * @param int $page
1152
+     * @param int $perPage
1153
+     *
1154
+     * @return Share[]
1155
+     */
1156
+    public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1157
+        return [];
1158
+    }
1159
+
1160
+    /**
1161
+     * Get the share by token possible with password
1162
+     *
1163
+     * @param string $token
1164
+     * @return Share
1165
+     *
1166
+     * @throws ShareNotFound
1167
+     */
1168
+    public function getShareByToken($token) {
1169
+        $share = null;
1170
+        try {
1171
+            if($this->shareApiAllowLinks()) {
1172
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1173
+                $share = $provider->getShareByToken($token);
1174
+            }
1175
+        } catch (ProviderException $e) {
1176
+        } catch (ShareNotFound $e) {
1177
+        }
1178
+
1179
+
1180
+        // If it is not a link share try to fetch a federated share by token
1181
+        if ($share === null) {
1182
+            try {
1183
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1184
+                $share = $provider->getShareByToken($token);
1185
+            } catch (ProviderException $e) {
1186
+            } catch (ShareNotFound $e) {
1187
+            }
1188
+        }
1189
+
1190
+        // If it is not a link share try to fetch a mail share by token
1191
+        if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1192
+            try {
1193
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1194
+                $share = $provider->getShareByToken($token);
1195
+            } catch (ProviderException $e) {
1196
+            } catch (ShareNotFound $e) {
1197
+            }
1198
+        }
1199
+
1200
+        if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
1201
+            try {
1202
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_CIRCLE);
1203
+                $share = $provider->getShareByToken($token);
1204
+            } catch (ProviderException $e) {
1205
+            } catch (ShareNotFound $e) {
1206
+            }
1207
+        }
1208
+
1209
+        if ($share === null) {
1210
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1211
+        }
1212
+
1213
+        $this->checkExpireDate($share);
1214
+
1215
+        /*
1216 1216
 		 * Reduce the permissions for link shares if public upload is not enabled
1217 1217
 		 */
1218
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1219
-			!$this->shareApiLinkAllowPublicUpload()) {
1220
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1221
-		}
1222
-
1223
-		return $share;
1224
-	}
1225
-
1226
-	protected function checkExpireDate($share) {
1227
-		if ($share->getExpirationDate() !== null &&
1228
-			$share->getExpirationDate() <= new \DateTime()) {
1229
-			$this->deleteShare($share);
1230
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1231
-		}
1232
-
1233
-	}
1234
-
1235
-	/**
1236
-	 * Verify the password of a public share
1237
-	 *
1238
-	 * @param \OCP\Share\IShare $share
1239
-	 * @param string $password
1240
-	 * @return bool
1241
-	 */
1242
-	public function checkPassword(\OCP\Share\IShare $share, $password) {
1243
-		$passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1244
-			|| $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1245
-		if (!$passwordProtected) {
1246
-			//TODO maybe exception?
1247
-			return false;
1248
-		}
1249
-
1250
-		if ($password === null || $share->getPassword() === null) {
1251
-			return false;
1252
-		}
1253
-
1254
-		$newHash = '';
1255
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1256
-			return false;
1257
-		}
1258
-
1259
-		if (!empty($newHash)) {
1260
-			$share->setPassword($newHash);
1261
-			$provider = $this->factory->getProviderForType($share->getShareType());
1262
-			$provider->update($share);
1263
-		}
1264
-
1265
-		return true;
1266
-	}
1267
-
1268
-	/**
1269
-	 * @inheritdoc
1270
-	 */
1271
-	public function userDeleted($uid) {
1272
-		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1273
-
1274
-		foreach ($types as $type) {
1275
-			try {
1276
-				$provider = $this->factory->getProviderForType($type);
1277
-			} catch (ProviderException $e) {
1278
-				continue;
1279
-			}
1280
-			$provider->userDeleted($uid, $type);
1281
-		}
1282
-	}
1283
-
1284
-	/**
1285
-	 * @inheritdoc
1286
-	 */
1287
-	public function groupDeleted($gid) {
1288
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1289
-		$provider->groupDeleted($gid);
1290
-	}
1291
-
1292
-	/**
1293
-	 * @inheritdoc
1294
-	 */
1295
-	public function userDeletedFromGroup($uid, $gid) {
1296
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1297
-		$provider->userDeletedFromGroup($uid, $gid);
1298
-	}
1299
-
1300
-	/**
1301
-	 * Get access list to a path. This means
1302
-	 * all the users that can access a given path.
1303
-	 *
1304
-	 * Consider:
1305
-	 * -root
1306
-	 * |-folder1 (23)
1307
-	 *  |-folder2 (32)
1308
-	 *   |-fileA (42)
1309
-	 *
1310
-	 * fileA is shared with user1 and user1@server1
1311
-	 * folder2 is shared with group2 (user4 is a member of group2)
1312
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1313
-	 *
1314
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1315
-	 * [
1316
-	 *  users  => [
1317
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1318
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1319
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1320
-	 *  ],
1321
-	 *  remote => [
1322
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1323
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1324
-	 *  ],
1325
-	 *  public => bool
1326
-	 *  mail => bool
1327
-	 * ]
1328
-	 *
1329
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1330
-	 * [
1331
-	 *  users  => ['user1', 'user2', 'user4'],
1332
-	 *  remote => bool,
1333
-	 *  public => bool
1334
-	 *  mail => bool
1335
-	 * ]
1336
-	 *
1337
-	 * This is required for encryption/activity
1338
-	 *
1339
-	 * @param \OCP\Files\Node $path
1340
-	 * @param bool $recursive Should we check all parent folders as well
1341
-	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1342
-	 * @return array
1343
-	 */
1344
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1345
-		$owner = $path->getOwner()->getUID();
1346
-
1347
-		if ($currentAccess) {
1348
-			$al = ['users' => [], 'remote' => [], 'public' => false];
1349
-		} else {
1350
-			$al = ['users' => [], 'remote' => false, 'public' => false];
1351
-		}
1352
-		if (!$this->userManager->userExists($owner)) {
1353
-			return $al;
1354
-		}
1355
-
1356
-		//Get node for the owner
1357
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1358
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1359
-			$path = $userFolder->getById($path->getId())[0];
1360
-		}
1361
-
1362
-		$providers = $this->factory->getAllProviders();
1363
-
1364
-		/** @var Node[] $nodes */
1365
-		$nodes = [];
1366
-
1367
-
1368
-		if ($currentAccess) {
1369
-			$ownerPath = $path->getPath();
1370
-			$ownerPath = explode('/', $ownerPath, 4);
1371
-			if (count($ownerPath) < 4) {
1372
-				$ownerPath = '';
1373
-			} else {
1374
-				$ownerPath = $ownerPath[3];
1375
-			}
1376
-			$al['users'][$owner] = [
1377
-				'node_id' => $path->getId(),
1378
-				'node_path' => '/' . $ownerPath,
1379
-			];
1380
-		} else {
1381
-			$al['users'][] = $owner;
1382
-		}
1383
-
1384
-		// Collect all the shares
1385
-		while ($path->getPath() !== $userFolder->getPath()) {
1386
-			$nodes[] = $path;
1387
-			if (!$recursive) {
1388
-				break;
1389
-			}
1390
-			$path = $path->getParent();
1391
-		}
1392
-
1393
-		foreach ($providers as $provider) {
1394
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1395
-
1396
-			foreach ($tmp as $k => $v) {
1397
-				if (isset($al[$k])) {
1398
-					if (is_array($al[$k])) {
1399
-						if ($currentAccess) {
1400
-							$al[$k] += $v;
1401
-						} else {
1402
-							$al[$k] = array_merge($al[$k], $v);
1403
-							$al[$k] = array_unique($al[$k]);
1404
-							$al[$k] = array_values($al[$k]);
1405
-						}
1406
-					} else {
1407
-						$al[$k] = $al[$k] || $v;
1408
-					}
1409
-				} else {
1410
-					$al[$k] = $v;
1411
-				}
1412
-			}
1413
-		}
1414
-
1415
-		return $al;
1416
-	}
1417
-
1418
-	/**
1419
-	 * Create a new share
1420
-	 * @return \OCP\Share\IShare
1421
-	 */
1422
-	public function newShare() {
1423
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1424
-	}
1425
-
1426
-	/**
1427
-	 * Is the share API enabled
1428
-	 *
1429
-	 * @return bool
1430
-	 */
1431
-	public function shareApiEnabled() {
1432
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1433
-	}
1434
-
1435
-	/**
1436
-	 * Is public link sharing enabled
1437
-	 *
1438
-	 * @return bool
1439
-	 */
1440
-	public function shareApiAllowLinks() {
1441
-		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1442
-	}
1443
-
1444
-	/**
1445
-	 * Is password on public link requires
1446
-	 *
1447
-	 * @return bool
1448
-	 */
1449
-	public function shareApiLinkEnforcePassword() {
1450
-		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1451
-	}
1452
-
1453
-	/**
1454
-	 * Is default expire date enabled
1455
-	 *
1456
-	 * @return bool
1457
-	 */
1458
-	public function shareApiLinkDefaultExpireDate() {
1459
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1460
-	}
1461
-
1462
-	/**
1463
-	 * Is default expire date enforced
1464
-	 *`
1465
-	 * @return bool
1466
-	 */
1467
-	public function shareApiLinkDefaultExpireDateEnforced() {
1468
-		return $this->shareApiLinkDefaultExpireDate() &&
1469
-			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1470
-	}
1471
-
1472
-	/**
1473
-	 * Number of default expire days
1474
-	 *shareApiLinkAllowPublicUpload
1475
-	 * @return int
1476
-	 */
1477
-	public function shareApiLinkDefaultExpireDays() {
1478
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1479
-	}
1480
-
1481
-	/**
1482
-	 * Allow public upload on link shares
1483
-	 *
1484
-	 * @return bool
1485
-	 */
1486
-	public function shareApiLinkAllowPublicUpload() {
1487
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1488
-	}
1489
-
1490
-	/**
1491
-	 * check if user can only share with group members
1492
-	 * @return bool
1493
-	 */
1494
-	public function shareWithGroupMembersOnly() {
1495
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1496
-	}
1497
-
1498
-	/**
1499
-	 * Check if users can share with groups
1500
-	 * @return bool
1501
-	 */
1502
-	public function allowGroupSharing() {
1503
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1504
-	}
1505
-
1506
-	/**
1507
-	 * Copied from \OC_Util::isSharingDisabledForUser
1508
-	 *
1509
-	 * TODO: Deprecate fuction from OC_Util
1510
-	 *
1511
-	 * @param string $userId
1512
-	 * @return bool
1513
-	 */
1514
-	public function sharingDisabledForUser($userId) {
1515
-		if ($userId === null) {
1516
-			return false;
1517
-		}
1518
-
1519
-		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1520
-			return $this->sharingDisabledForUsersCache[$userId];
1521
-		}
1522
-
1523
-		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1524
-			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1525
-			$excludedGroups = json_decode($groupsList);
1526
-			if (is_null($excludedGroups)) {
1527
-				$excludedGroups = explode(',', $groupsList);
1528
-				$newValue = json_encode($excludedGroups);
1529
-				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1530
-			}
1531
-			$user = $this->userManager->get($userId);
1532
-			$usersGroups = $this->groupManager->getUserGroupIds($user);
1533
-			if (!empty($usersGroups)) {
1534
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1535
-				// if the user is only in groups which are disabled for sharing then
1536
-				// sharing is also disabled for the user
1537
-				if (empty($remainingGroups)) {
1538
-					$this->sharingDisabledForUsersCache[$userId] = true;
1539
-					return true;
1540
-				}
1541
-			}
1542
-		}
1543
-
1544
-		$this->sharingDisabledForUsersCache[$userId] = false;
1545
-		return false;
1546
-	}
1547
-
1548
-	/**
1549
-	 * @inheritdoc
1550
-	 */
1551
-	public function outgoingServer2ServerSharesAllowed() {
1552
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1553
-	}
1554
-
1555
-	/**
1556
-	 * @inheritdoc
1557
-	 */
1558
-	public function shareProviderExists($shareType) {
1559
-		try {
1560
-			$this->factory->getProviderForType($shareType);
1561
-		} catch (ProviderException $e) {
1562
-			return false;
1563
-		}
1564
-
1565
-		return true;
1566
-	}
1218
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1219
+            !$this->shareApiLinkAllowPublicUpload()) {
1220
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1221
+        }
1222
+
1223
+        return $share;
1224
+    }
1225
+
1226
+    protected function checkExpireDate($share) {
1227
+        if ($share->getExpirationDate() !== null &&
1228
+            $share->getExpirationDate() <= new \DateTime()) {
1229
+            $this->deleteShare($share);
1230
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1231
+        }
1232
+
1233
+    }
1234
+
1235
+    /**
1236
+     * Verify the password of a public share
1237
+     *
1238
+     * @param \OCP\Share\IShare $share
1239
+     * @param string $password
1240
+     * @return bool
1241
+     */
1242
+    public function checkPassword(\OCP\Share\IShare $share, $password) {
1243
+        $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1244
+            || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1245
+        if (!$passwordProtected) {
1246
+            //TODO maybe exception?
1247
+            return false;
1248
+        }
1249
+
1250
+        if ($password === null || $share->getPassword() === null) {
1251
+            return false;
1252
+        }
1253
+
1254
+        $newHash = '';
1255
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1256
+            return false;
1257
+        }
1258
+
1259
+        if (!empty($newHash)) {
1260
+            $share->setPassword($newHash);
1261
+            $provider = $this->factory->getProviderForType($share->getShareType());
1262
+            $provider->update($share);
1263
+        }
1264
+
1265
+        return true;
1266
+    }
1267
+
1268
+    /**
1269
+     * @inheritdoc
1270
+     */
1271
+    public function userDeleted($uid) {
1272
+        $types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1273
+
1274
+        foreach ($types as $type) {
1275
+            try {
1276
+                $provider = $this->factory->getProviderForType($type);
1277
+            } catch (ProviderException $e) {
1278
+                continue;
1279
+            }
1280
+            $provider->userDeleted($uid, $type);
1281
+        }
1282
+    }
1283
+
1284
+    /**
1285
+     * @inheritdoc
1286
+     */
1287
+    public function groupDeleted($gid) {
1288
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1289
+        $provider->groupDeleted($gid);
1290
+    }
1291
+
1292
+    /**
1293
+     * @inheritdoc
1294
+     */
1295
+    public function userDeletedFromGroup($uid, $gid) {
1296
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1297
+        $provider->userDeletedFromGroup($uid, $gid);
1298
+    }
1299
+
1300
+    /**
1301
+     * Get access list to a path. This means
1302
+     * all the users that can access a given path.
1303
+     *
1304
+     * Consider:
1305
+     * -root
1306
+     * |-folder1 (23)
1307
+     *  |-folder2 (32)
1308
+     *   |-fileA (42)
1309
+     *
1310
+     * fileA is shared with user1 and user1@server1
1311
+     * folder2 is shared with group2 (user4 is a member of group2)
1312
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1313
+     *
1314
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1315
+     * [
1316
+     *  users  => [
1317
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1318
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1319
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1320
+     *  ],
1321
+     *  remote => [
1322
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1323
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1324
+     *  ],
1325
+     *  public => bool
1326
+     *  mail => bool
1327
+     * ]
1328
+     *
1329
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1330
+     * [
1331
+     *  users  => ['user1', 'user2', 'user4'],
1332
+     *  remote => bool,
1333
+     *  public => bool
1334
+     *  mail => bool
1335
+     * ]
1336
+     *
1337
+     * This is required for encryption/activity
1338
+     *
1339
+     * @param \OCP\Files\Node $path
1340
+     * @param bool $recursive Should we check all parent folders as well
1341
+     * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1342
+     * @return array
1343
+     */
1344
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1345
+        $owner = $path->getOwner()->getUID();
1346
+
1347
+        if ($currentAccess) {
1348
+            $al = ['users' => [], 'remote' => [], 'public' => false];
1349
+        } else {
1350
+            $al = ['users' => [], 'remote' => false, 'public' => false];
1351
+        }
1352
+        if (!$this->userManager->userExists($owner)) {
1353
+            return $al;
1354
+        }
1355
+
1356
+        //Get node for the owner
1357
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1358
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1359
+            $path = $userFolder->getById($path->getId())[0];
1360
+        }
1361
+
1362
+        $providers = $this->factory->getAllProviders();
1363
+
1364
+        /** @var Node[] $nodes */
1365
+        $nodes = [];
1366
+
1367
+
1368
+        if ($currentAccess) {
1369
+            $ownerPath = $path->getPath();
1370
+            $ownerPath = explode('/', $ownerPath, 4);
1371
+            if (count($ownerPath) < 4) {
1372
+                $ownerPath = '';
1373
+            } else {
1374
+                $ownerPath = $ownerPath[3];
1375
+            }
1376
+            $al['users'][$owner] = [
1377
+                'node_id' => $path->getId(),
1378
+                'node_path' => '/' . $ownerPath,
1379
+            ];
1380
+        } else {
1381
+            $al['users'][] = $owner;
1382
+        }
1383
+
1384
+        // Collect all the shares
1385
+        while ($path->getPath() !== $userFolder->getPath()) {
1386
+            $nodes[] = $path;
1387
+            if (!$recursive) {
1388
+                break;
1389
+            }
1390
+            $path = $path->getParent();
1391
+        }
1392
+
1393
+        foreach ($providers as $provider) {
1394
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1395
+
1396
+            foreach ($tmp as $k => $v) {
1397
+                if (isset($al[$k])) {
1398
+                    if (is_array($al[$k])) {
1399
+                        if ($currentAccess) {
1400
+                            $al[$k] += $v;
1401
+                        } else {
1402
+                            $al[$k] = array_merge($al[$k], $v);
1403
+                            $al[$k] = array_unique($al[$k]);
1404
+                            $al[$k] = array_values($al[$k]);
1405
+                        }
1406
+                    } else {
1407
+                        $al[$k] = $al[$k] || $v;
1408
+                    }
1409
+                } else {
1410
+                    $al[$k] = $v;
1411
+                }
1412
+            }
1413
+        }
1414
+
1415
+        return $al;
1416
+    }
1417
+
1418
+    /**
1419
+     * Create a new share
1420
+     * @return \OCP\Share\IShare
1421
+     */
1422
+    public function newShare() {
1423
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1424
+    }
1425
+
1426
+    /**
1427
+     * Is the share API enabled
1428
+     *
1429
+     * @return bool
1430
+     */
1431
+    public function shareApiEnabled() {
1432
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1433
+    }
1434
+
1435
+    /**
1436
+     * Is public link sharing enabled
1437
+     *
1438
+     * @return bool
1439
+     */
1440
+    public function shareApiAllowLinks() {
1441
+        return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1442
+    }
1443
+
1444
+    /**
1445
+     * Is password on public link requires
1446
+     *
1447
+     * @return bool
1448
+     */
1449
+    public function shareApiLinkEnforcePassword() {
1450
+        return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1451
+    }
1452
+
1453
+    /**
1454
+     * Is default expire date enabled
1455
+     *
1456
+     * @return bool
1457
+     */
1458
+    public function shareApiLinkDefaultExpireDate() {
1459
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1460
+    }
1461
+
1462
+    /**
1463
+     * Is default expire date enforced
1464
+     *`
1465
+     * @return bool
1466
+     */
1467
+    public function shareApiLinkDefaultExpireDateEnforced() {
1468
+        return $this->shareApiLinkDefaultExpireDate() &&
1469
+            $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1470
+    }
1471
+
1472
+    /**
1473
+     * Number of default expire days
1474
+     *shareApiLinkAllowPublicUpload
1475
+     * @return int
1476
+     */
1477
+    public function shareApiLinkDefaultExpireDays() {
1478
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1479
+    }
1480
+
1481
+    /**
1482
+     * Allow public upload on link shares
1483
+     *
1484
+     * @return bool
1485
+     */
1486
+    public function shareApiLinkAllowPublicUpload() {
1487
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1488
+    }
1489
+
1490
+    /**
1491
+     * check if user can only share with group members
1492
+     * @return bool
1493
+     */
1494
+    public function shareWithGroupMembersOnly() {
1495
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1496
+    }
1497
+
1498
+    /**
1499
+     * Check if users can share with groups
1500
+     * @return bool
1501
+     */
1502
+    public function allowGroupSharing() {
1503
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1504
+    }
1505
+
1506
+    /**
1507
+     * Copied from \OC_Util::isSharingDisabledForUser
1508
+     *
1509
+     * TODO: Deprecate fuction from OC_Util
1510
+     *
1511
+     * @param string $userId
1512
+     * @return bool
1513
+     */
1514
+    public function sharingDisabledForUser($userId) {
1515
+        if ($userId === null) {
1516
+            return false;
1517
+        }
1518
+
1519
+        if (isset($this->sharingDisabledForUsersCache[$userId])) {
1520
+            return $this->sharingDisabledForUsersCache[$userId];
1521
+        }
1522
+
1523
+        if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1524
+            $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1525
+            $excludedGroups = json_decode($groupsList);
1526
+            if (is_null($excludedGroups)) {
1527
+                $excludedGroups = explode(',', $groupsList);
1528
+                $newValue = json_encode($excludedGroups);
1529
+                $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1530
+            }
1531
+            $user = $this->userManager->get($userId);
1532
+            $usersGroups = $this->groupManager->getUserGroupIds($user);
1533
+            if (!empty($usersGroups)) {
1534
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
1535
+                // if the user is only in groups which are disabled for sharing then
1536
+                // sharing is also disabled for the user
1537
+                if (empty($remainingGroups)) {
1538
+                    $this->sharingDisabledForUsersCache[$userId] = true;
1539
+                    return true;
1540
+                }
1541
+            }
1542
+        }
1543
+
1544
+        $this->sharingDisabledForUsersCache[$userId] = false;
1545
+        return false;
1546
+    }
1547
+
1548
+    /**
1549
+     * @inheritdoc
1550
+     */
1551
+    public function outgoingServer2ServerSharesAllowed() {
1552
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1553
+    }
1554
+
1555
+    /**
1556
+     * @inheritdoc
1557
+     */
1558
+    public function shareProviderExists($shareType) {
1559
+        try {
1560
+            $this->factory->getProviderForType($shareType);
1561
+        } catch (ProviderException $e) {
1562
+            return false;
1563
+        }
1564
+
1565
+        return true;
1566
+    }
1567 1567
 
1568 1568
 }
Please login to merge, or discard this patch.
lib/private/Mail/EMailTemplate.php 1 patch
Indentation   +349 added lines, -349 removed lines patch added patch discarded remove patch
@@ -41,33 +41,33 @@  discard block
 block discarded – undo
41 41
  * @package OC\Mail
42 42
  */
43 43
 class EMailTemplate implements IEMailTemplate {
44
-	/** @var Defaults */
45
-	protected $themingDefaults;
46
-	/** @var IURLGenerator */
47
-	protected $urlGenerator;
48
-	/** @var IL10N */
49
-	protected $l10n;
50
-	/** @var string */
51
-	protected $emailId;
52
-	/** @var array */
53
-	protected $data;
54
-
55
-	/** @var string */
56
-	protected $subject = '';
57
-	/** @var string */
58
-	protected $htmlBody = '';
59
-	/** @var string */
60
-	protected $plainBody = '';
61
-	/** @var bool indicated if the footer is added */
62
-	protected $headerAdded = false;
63
-	/** @var bool indicated if the body is already opened */
64
-	protected $bodyOpened = false;
65
-	/** @var bool indicated if there is a list open in the body */
66
-	protected $bodyListOpened = false;
67
-	/** @var bool indicated if the footer is added */
68
-	protected $footerAdded = false;
69
-
70
-	protected $head = <<<EOF
44
+    /** @var Defaults */
45
+    protected $themingDefaults;
46
+    /** @var IURLGenerator */
47
+    protected $urlGenerator;
48
+    /** @var IL10N */
49
+    protected $l10n;
50
+    /** @var string */
51
+    protected $emailId;
52
+    /** @var array */
53
+    protected $data;
54
+
55
+    /** @var string */
56
+    protected $subject = '';
57
+    /** @var string */
58
+    protected $htmlBody = '';
59
+    /** @var string */
60
+    protected $plainBody = '';
61
+    /** @var bool indicated if the footer is added */
62
+    protected $headerAdded = false;
63
+    /** @var bool indicated if the body is already opened */
64
+    protected $bodyOpened = false;
65
+    /** @var bool indicated if there is a list open in the body */
66
+    protected $bodyListOpened = false;
67
+    /** @var bool indicated if the footer is added */
68
+    protected $footerAdded = false;
69
+
70
+    protected $head = <<<EOF
71 71
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
72 72
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="-webkit-font-smoothing:antialiased;background:#f3f3f3!important">
73 73
 <head>
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
 				<center data-parsed="" style="min-width:580px;width:100%">
86 86
 EOF;
87 87
 
88
-	protected $tail = <<<EOF
88
+    protected $tail = <<<EOF
89 89
 					</center>
90 90
 				</td>
91 91
 			</tr>
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
 </html>
97 97
 EOF;
98 98
 
99
-	protected $header = <<<EOF
99
+    protected $header = <<<EOF
100 100
 <table align="center" class="wrapper header float-center" style="Margin:0 auto;background:#8a8a8a;background-color:%s;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
101 101
 	<tr style="padding:0;text-align:left;vertical-align:top">
102 102
 		<td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:20px;text-align:left;vertical-align:top;word-wrap:break-word">
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
 </table>
130 130
 EOF;
131 131
 
132
-	protected $heading = <<<EOF
132
+    protected $heading = <<<EOF
133 133
 <table align="center" class="container main-heading float-center" style="Margin:0 auto;background:0 0!important;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:580px">
134 134
 	<tbody>
135 135
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 </table>
149 149
 EOF;
150 150
 
151
-	protected $bodyBegin = <<<EOF
151
+    protected $bodyBegin = <<<EOF
152 152
 <table align="center" class="wrapper content float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%">
153 153
 	<tr style="padding:0;text-align:left;vertical-align:top">
154 154
 		<td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
@@ -165,7 +165,7 @@  discard block
 block discarded – undo
165 165
 						</table>
166 166
 EOF;
167 167
 
168
-	protected $bodyText = <<<EOF
168
+    protected $bodyText = <<<EOF
169 169
 <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
170 170
 	<tbody>
171 171
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -184,7 +184,7 @@  discard block
 block discarded – undo
184 184
 </table>
185 185
 EOF;
186 186
 
187
-	protected $listBegin = <<<EOF
187
+    protected $listBegin = <<<EOF
188 188
 <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
189 189
 	<tbody>
190 190
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -192,7 +192,7 @@  discard block
 block discarded – undo
192 192
 			<table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
193 193
 EOF;
194 194
 
195
-	protected $listItem = <<<EOF
195
+    protected $listItem = <<<EOF
196 196
 				<tr style="padding:0;text-align:left;vertical-align:top">
197 197
 					<td style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;width:15px;">
198 198
 						<p class="text-left" style="Margin:0;Margin-bottom:10px;color:#777;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;padding-left:10px;text-align:left">%s</p>
@@ -204,7 +204,7 @@  discard block
 block discarded – undo
204 204
 				</tr>
205 205
 EOF;
206 206
 
207
-	protected $listEnd = <<<EOF
207
+    protected $listEnd = <<<EOF
208 208
 			</table>
209 209
 		</th>
210 210
 	</tr>
@@ -212,7 +212,7 @@  discard block
 block discarded – undo
212 212
 </table>
213 213
 EOF;
214 214
 
215
-	protected $buttonGroup = <<<EOF
215
+    protected $buttonGroup = <<<EOF
216 216
 <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
217 217
 	<tbody>
218 218
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 </table>
266 266
 EOF;
267 267
 
268
-	protected $button = <<<EOF
268
+    protected $button = <<<EOF
269 269
 <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
270 270
 	<tbody>
271 271
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -305,7 +305,7 @@  discard block
 block discarded – undo
305 305
 </table>
306 306
 EOF;
307 307
 
308
-	protected $bodyEnd = <<<EOF
308
+    protected $bodyEnd = <<<EOF
309 309
 
310 310
 					</td>
311 311
 				</tr>
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
 </table>
317 317
 EOF;
318 318
 
319
-	protected $footer = <<<EOF
319
+    protected $footer = <<<EOF
320 320
 <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
321 321
 	<tbody>
322 322
 	<tr style="padding:0;text-align:left;vertical-align:top">
@@ -342,314 +342,314 @@  discard block
 block discarded – undo
342 342
 </table>
343 343
 EOF;
344 344
 
345
-	/**
346
-	 * @param Defaults $themingDefaults
347
-	 * @param IURLGenerator $urlGenerator
348
-	 * @param IL10N $l10n
349
-	 * @param string $emailId
350
-	 * @param array $data
351
-	 */
352
-	public function __construct(Defaults $themingDefaults,
353
-								IURLGenerator $urlGenerator,
354
-								IL10N $l10n,
355
-								$emailId,
356
-								array $data) {
357
-		$this->themingDefaults = $themingDefaults;
358
-		$this->urlGenerator = $urlGenerator;
359
-		$this->l10n = $l10n;
360
-		$this->htmlBody .= $this->head;
361
-		$this->emailId = $emailId;
362
-		$this->data = $data;
363
-	}
364
-
365
-	/**
366
-	 * Sets the subject of the email
367
-	 *
368
-	 * @param string $subject
369
-	 */
370
-	public function setSubject($subject) {
371
-		$this->subject = $subject;
372
-	}
373
-
374
-	/**
375
-	 * Adds a header to the email
376
-	 */
377
-	public function addHeader() {
378
-		if ($this->headerAdded) {
379
-			return;
380
-		}
381
-		$this->headerAdded = true;
382
-
383
-		$logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false));
384
-		$this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getColorPrimary(), $logoUrl, $this->themingDefaults->getName()]);
385
-	}
386
-
387
-	/**
388
-	 * Adds a heading to the email
389
-	 *
390
-	 * @param string $title
391
-	 * @param string|bool $plainTitle Title that is used in the plain text email
392
-	 *   if empty the $title is used, if false none will be used
393
-	 */
394
-	public function addHeading($title, $plainTitle = '') {
395
-		if ($this->footerAdded) {
396
-			return;
397
-		}
398
-		if ($plainTitle === '') {
399
-			$plainTitle = $title;
400
-		}
401
-
402
-		$this->htmlBody .= vsprintf($this->heading, [htmlspecialchars($title)]);
403
-		if ($plainTitle !== false) {
404
-			$this->plainBody .= $plainTitle . PHP_EOL . PHP_EOL;
405
-		}
406
-	}
407
-
408
-	/**
409
-	 * Open the HTML body when it is not already
410
-	 */
411
-	protected function ensureBodyIsOpened() {
412
-		if ($this->bodyOpened) {
413
-			return;
414
-		}
415
-
416
-		$this->htmlBody .= $this->bodyBegin;
417
-		$this->bodyOpened = true;
418
-	}
419
-
420
-	/**
421
-	 * Adds a paragraph to the body of the email
422
-	 *
423
-	 * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
424
-	 * @param string|bool $plainText Text that is used in the plain text email
425
-	 *   if empty the $text is used, if false none will be used
426
-	 */
427
-	public function addBodyText($text, $plainText = '') {
428
-		if ($this->footerAdded) {
429
-			return;
430
-		}
431
-		if ($plainText === '') {
432
-			$plainText = $text;
433
-			$text = htmlspecialchars($text);
434
-		}
435
-
436
-		$this->ensureBodyIsOpened();
437
-
438
-		$this->htmlBody .= vsprintf($this->bodyText, [$text]);
439
-		if ($plainText !== false) {
440
-			$this->plainBody .= $plainText . PHP_EOL . PHP_EOL;
441
-		}
442
-	}
443
-
444
-	/**
445
-	 * Adds a list item to the body of the email
446
-	 *
447
-	 * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
448
-	 * @param string $metaInfo Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
449
-	 * @param string $icon Absolute path, must be 16*16 pixels
450
-	 * @param string $plainText Text that is used in the plain text email
451
-	 *   if empty the $text is used, if false none will be used
452
-	 * @param string $plainMetaInfo Meta info that is used in the plain text email
453
-	 *   if empty the $metaInfo is used, if false none will be used
454
-	 * @since 12.0.0
455
-	 */
456
-	public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '') {
457
-		$this->ensureBodyListOpened();
458
-
459
-		if ($plainText === '') {
460
-			$plainText = $text;
461
-			$text = htmlspecialchars($text);
462
-		}
463
-		if ($plainMetaInfo === '') {
464
-			$plainMetaInfo = $metaInfo;
465
-			$metaInfo = htmlspecialchars($metaInfo);
466
-		}
467
-
468
-		$htmlText = $text;
469
-		if ($metaInfo) {
470
-			$htmlText = '<em style="color:#777;">' . $metaInfo . '</em><br>' . $htmlText;
471
-		}
472
-		if ($icon !== '') {
473
-			$icon = '<img src="' . htmlspecialchars($icon) . '" alt="&bull;">';
474
-		} else {
475
-			$icon = '&bull;';
476
-		}
477
-		$this->htmlBody .= vsprintf($this->listItem, [$icon, $htmlText]);
478
-		if ($plainText !== false) {
479
-			$this->plainBody .= '  * ' . $plainText;
480
-			if ($plainMetaInfo !== false) {
481
-				$this->plainBody .= ' (' . $plainMetaInfo . ')';
482
-			}
483
-			$this->plainBody .= PHP_EOL;
484
-		}
485
-	}
486
-
487
-	protected function ensureBodyListOpened() {
488
-		if ($this->bodyListOpened) {
489
-			return;
490
-		}
491
-
492
-		$this->ensureBodyIsOpened();
493
-		$this->bodyListOpened = true;
494
-		$this->htmlBody .= $this->listBegin;
495
-	}
496
-
497
-	protected function ensureBodyListClosed() {
498
-		if (!$this->bodyListOpened) {
499
-			return;
500
-		}
501
-
502
-		$this->bodyListOpened = false;
503
-		$this->htmlBody .= $this->listEnd;
504
-	}
505
-
506
-	/**
507
-	 * Adds a button group of two buttons to the body of the email
508
-	 *
509
-	 * @param string $textLeft Text of left button; Note: When $plainTextLeft falls back to this, HTML is automatically escaped in the HTML email
510
-	 * @param string $urlLeft URL of left button
511
-	 * @param string $textRight Text of right button; Note: When $plainTextRight falls back to this, HTML is automatically escaped in the HTML email
512
-	 * @param string $urlRight URL of right button
513
-	 * @param string $plainTextLeft Text of left button that is used in the plain text version - if unset the $textLeft is used
514
-	 * @param string $plainTextRight Text of right button that is used in the plain text version - if unset the $textRight is used
515
-	 */
516
-	public function addBodyButtonGroup($textLeft,
517
-									   $urlLeft,
518
-									   $textRight,
519
-									   $urlRight,
520
-									   $plainTextLeft = '',
521
-									   $plainTextRight = '') {
522
-		if ($this->footerAdded) {
523
-			return;
524
-		}
525
-		if ($plainTextLeft === '') {
526
-			$plainTextLeft = $textLeft;
527
-			$textLeft = htmlspecialchars($textLeft);
528
-		}
529
-
530
-		if ($plainTextRight === '') {
531
-			$plainTextRight = $textRight;
532
-			$textRight = htmlspecialchars($textRight);
533
-		}
534
-
535
-		$this->ensureBodyIsOpened();
536
-		$this->ensureBodyListClosed();
537
-
538
-		$color = $this->themingDefaults->getColorPrimary();
539
-		$textColor = $this->themingDefaults->getTextColorPrimary();
540
-
541
-		$this->htmlBody .= vsprintf($this->buttonGroup, [$color, $color, $urlLeft, $color, $textColor, $textColor, $textLeft, $urlRight, $textRight]);
542
-		$this->plainBody .= $plainTextLeft . ': ' . $urlLeft . PHP_EOL;
543
-		$this->plainBody .= $plainTextRight . ': ' . $urlRight . PHP_EOL . PHP_EOL;
544
-
545
-	}
546
-
547
-	/**
548
-	 * Adds a button to the body of the email
549
-	 *
550
-	 * @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
551
-	 * @param string $url URL of button
552
-	 * @param string $plainText Text of button in plain text version
553
-	 * 		if empty the $text is used, if false none will be used
554
-	 *
555
-	 * @since 12.0.0
556
-	 */
557
-	public function addBodyButton($text, $url, $plainText = '') {
558
-		if ($this->footerAdded) {
559
-			return;
560
-		}
561
-
562
-		$this->ensureBodyIsOpened();
563
-		$this->ensureBodyListClosed();
564
-
565
-		if ($plainText === '') {
566
-			$plainText = $text;
567
-			$text = htmlspecialchars($text);
568
-		}
569
-
570
-		$color = $this->themingDefaults->getColorPrimary();
571
-		$textColor = $this->themingDefaults->getTextColorPrimary();
572
-		$this->htmlBody .= vsprintf($this->button, [$color, $color, $url, $color, $textColor, $textColor, $text]);
573
-
574
-		if ($plainText !== false) {
575
-			$this->plainBody .= $plainText . ': ';
576
-		}
577
-
578
-		$this->plainBody .=  $url . PHP_EOL;
579
-
580
-	}
581
-
582
-	/**
583
-	 * Close the HTML body when it is open
584
-	 */
585
-	protected function ensureBodyIsClosed() {
586
-		if (!$this->bodyOpened) {
587
-			return;
588
-		}
589
-
590
-		$this->ensureBodyListClosed();
591
-
592
-		$this->htmlBody .= $this->bodyEnd;
593
-		$this->bodyOpened = false;
594
-	}
595
-
596
-	/**
597
-	 * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email
598
-	 *
599
-	 * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
600
-	 */
601
-	public function addFooter($text = '') {
602
-		if($text === '') {
603
-			$text = $this->themingDefaults->getName() . ' - ' . $this->themingDefaults->getSlogan() . '<br>' . $this->l10n->t('This is an automatically sent email, please do not reply.');
604
-		}
605
-
606
-		if ($this->footerAdded) {
607
-			return;
608
-		}
609
-		$this->footerAdded = true;
610
-
611
-		$this->ensureBodyIsClosed();
612
-
613
-		$this->htmlBody .= vsprintf($this->footer, [$text]);
614
-		$this->htmlBody .= $this->tail;
615
-		$this->plainBody .= PHP_EOL . '-- ' . PHP_EOL;
616
-		$this->plainBody .= str_replace('<br>', PHP_EOL, $text);
617
-	}
618
-
619
-	/**
620
-	 * Returns the rendered email subject as string
621
-	 *
622
-	 * @return string
623
-	 */
624
-	public function renderSubject() {
625
-		return $this->subject;
626
-	}
627
-
628
-	/**
629
-	 * Returns the rendered HTML email as string
630
-	 *
631
-	 * @return string
632
-	 */
633
-	public function renderHtml() {
634
-		if (!$this->footerAdded) {
635
-			$this->footerAdded = true;
636
-			$this->ensureBodyIsClosed();
637
-			$this->htmlBody .= $this->tail;
638
-		}
639
-		return $this->htmlBody;
640
-	}
641
-
642
-	/**
643
-	 * Returns the rendered plain text email as string
644
-	 *
645
-	 * @return string
646
-	 */
647
-	public function renderText() {
648
-		if (!$this->footerAdded) {
649
-			$this->footerAdded = true;
650
-			$this->ensureBodyIsClosed();
651
-			$this->htmlBody .= $this->tail;
652
-		}
653
-		return $this->plainBody;
654
-	}
345
+    /**
346
+     * @param Defaults $themingDefaults
347
+     * @param IURLGenerator $urlGenerator
348
+     * @param IL10N $l10n
349
+     * @param string $emailId
350
+     * @param array $data
351
+     */
352
+    public function __construct(Defaults $themingDefaults,
353
+                                IURLGenerator $urlGenerator,
354
+                                IL10N $l10n,
355
+                                $emailId,
356
+                                array $data) {
357
+        $this->themingDefaults = $themingDefaults;
358
+        $this->urlGenerator = $urlGenerator;
359
+        $this->l10n = $l10n;
360
+        $this->htmlBody .= $this->head;
361
+        $this->emailId = $emailId;
362
+        $this->data = $data;
363
+    }
364
+
365
+    /**
366
+     * Sets the subject of the email
367
+     *
368
+     * @param string $subject
369
+     */
370
+    public function setSubject($subject) {
371
+        $this->subject = $subject;
372
+    }
373
+
374
+    /**
375
+     * Adds a header to the email
376
+     */
377
+    public function addHeader() {
378
+        if ($this->headerAdded) {
379
+            return;
380
+        }
381
+        $this->headerAdded = true;
382
+
383
+        $logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false));
384
+        $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getColorPrimary(), $logoUrl, $this->themingDefaults->getName()]);
385
+    }
386
+
387
+    /**
388
+     * Adds a heading to the email
389
+     *
390
+     * @param string $title
391
+     * @param string|bool $plainTitle Title that is used in the plain text email
392
+     *   if empty the $title is used, if false none will be used
393
+     */
394
+    public function addHeading($title, $plainTitle = '') {
395
+        if ($this->footerAdded) {
396
+            return;
397
+        }
398
+        if ($plainTitle === '') {
399
+            $plainTitle = $title;
400
+        }
401
+
402
+        $this->htmlBody .= vsprintf($this->heading, [htmlspecialchars($title)]);
403
+        if ($plainTitle !== false) {
404
+            $this->plainBody .= $plainTitle . PHP_EOL . PHP_EOL;
405
+        }
406
+    }
407
+
408
+    /**
409
+     * Open the HTML body when it is not already
410
+     */
411
+    protected function ensureBodyIsOpened() {
412
+        if ($this->bodyOpened) {
413
+            return;
414
+        }
415
+
416
+        $this->htmlBody .= $this->bodyBegin;
417
+        $this->bodyOpened = true;
418
+    }
419
+
420
+    /**
421
+     * Adds a paragraph to the body of the email
422
+     *
423
+     * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
424
+     * @param string|bool $plainText Text that is used in the plain text email
425
+     *   if empty the $text is used, if false none will be used
426
+     */
427
+    public function addBodyText($text, $plainText = '') {
428
+        if ($this->footerAdded) {
429
+            return;
430
+        }
431
+        if ($plainText === '') {
432
+            $plainText = $text;
433
+            $text = htmlspecialchars($text);
434
+        }
435
+
436
+        $this->ensureBodyIsOpened();
437
+
438
+        $this->htmlBody .= vsprintf($this->bodyText, [$text]);
439
+        if ($plainText !== false) {
440
+            $this->plainBody .= $plainText . PHP_EOL . PHP_EOL;
441
+        }
442
+    }
443
+
444
+    /**
445
+     * Adds a list item to the body of the email
446
+     *
447
+     * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
448
+     * @param string $metaInfo Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
449
+     * @param string $icon Absolute path, must be 16*16 pixels
450
+     * @param string $plainText Text that is used in the plain text email
451
+     *   if empty the $text is used, if false none will be used
452
+     * @param string $plainMetaInfo Meta info that is used in the plain text email
453
+     *   if empty the $metaInfo is used, if false none will be used
454
+     * @since 12.0.0
455
+     */
456
+    public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '') {
457
+        $this->ensureBodyListOpened();
458
+
459
+        if ($plainText === '') {
460
+            $plainText = $text;
461
+            $text = htmlspecialchars($text);
462
+        }
463
+        if ($plainMetaInfo === '') {
464
+            $plainMetaInfo = $metaInfo;
465
+            $metaInfo = htmlspecialchars($metaInfo);
466
+        }
467
+
468
+        $htmlText = $text;
469
+        if ($metaInfo) {
470
+            $htmlText = '<em style="color:#777;">' . $metaInfo . '</em><br>' . $htmlText;
471
+        }
472
+        if ($icon !== '') {
473
+            $icon = '<img src="' . htmlspecialchars($icon) . '" alt="&bull;">';
474
+        } else {
475
+            $icon = '&bull;';
476
+        }
477
+        $this->htmlBody .= vsprintf($this->listItem, [$icon, $htmlText]);
478
+        if ($plainText !== false) {
479
+            $this->plainBody .= '  * ' . $plainText;
480
+            if ($plainMetaInfo !== false) {
481
+                $this->plainBody .= ' (' . $plainMetaInfo . ')';
482
+            }
483
+            $this->plainBody .= PHP_EOL;
484
+        }
485
+    }
486
+
487
+    protected function ensureBodyListOpened() {
488
+        if ($this->bodyListOpened) {
489
+            return;
490
+        }
491
+
492
+        $this->ensureBodyIsOpened();
493
+        $this->bodyListOpened = true;
494
+        $this->htmlBody .= $this->listBegin;
495
+    }
496
+
497
+    protected function ensureBodyListClosed() {
498
+        if (!$this->bodyListOpened) {
499
+            return;
500
+        }
501
+
502
+        $this->bodyListOpened = false;
503
+        $this->htmlBody .= $this->listEnd;
504
+    }
505
+
506
+    /**
507
+     * Adds a button group of two buttons to the body of the email
508
+     *
509
+     * @param string $textLeft Text of left button; Note: When $plainTextLeft falls back to this, HTML is automatically escaped in the HTML email
510
+     * @param string $urlLeft URL of left button
511
+     * @param string $textRight Text of right button; Note: When $plainTextRight falls back to this, HTML is automatically escaped in the HTML email
512
+     * @param string $urlRight URL of right button
513
+     * @param string $plainTextLeft Text of left button that is used in the plain text version - if unset the $textLeft is used
514
+     * @param string $plainTextRight Text of right button that is used in the plain text version - if unset the $textRight is used
515
+     */
516
+    public function addBodyButtonGroup($textLeft,
517
+                                        $urlLeft,
518
+                                        $textRight,
519
+                                        $urlRight,
520
+                                        $plainTextLeft = '',
521
+                                        $plainTextRight = '') {
522
+        if ($this->footerAdded) {
523
+            return;
524
+        }
525
+        if ($plainTextLeft === '') {
526
+            $plainTextLeft = $textLeft;
527
+            $textLeft = htmlspecialchars($textLeft);
528
+        }
529
+
530
+        if ($plainTextRight === '') {
531
+            $plainTextRight = $textRight;
532
+            $textRight = htmlspecialchars($textRight);
533
+        }
534
+
535
+        $this->ensureBodyIsOpened();
536
+        $this->ensureBodyListClosed();
537
+
538
+        $color = $this->themingDefaults->getColorPrimary();
539
+        $textColor = $this->themingDefaults->getTextColorPrimary();
540
+
541
+        $this->htmlBody .= vsprintf($this->buttonGroup, [$color, $color, $urlLeft, $color, $textColor, $textColor, $textLeft, $urlRight, $textRight]);
542
+        $this->plainBody .= $plainTextLeft . ': ' . $urlLeft . PHP_EOL;
543
+        $this->plainBody .= $plainTextRight . ': ' . $urlRight . PHP_EOL . PHP_EOL;
544
+
545
+    }
546
+
547
+    /**
548
+     * Adds a button to the body of the email
549
+     *
550
+     * @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
551
+     * @param string $url URL of button
552
+     * @param string $plainText Text of button in plain text version
553
+     * 		if empty the $text is used, if false none will be used
554
+     *
555
+     * @since 12.0.0
556
+     */
557
+    public function addBodyButton($text, $url, $plainText = '') {
558
+        if ($this->footerAdded) {
559
+            return;
560
+        }
561
+
562
+        $this->ensureBodyIsOpened();
563
+        $this->ensureBodyListClosed();
564
+
565
+        if ($plainText === '') {
566
+            $plainText = $text;
567
+            $text = htmlspecialchars($text);
568
+        }
569
+
570
+        $color = $this->themingDefaults->getColorPrimary();
571
+        $textColor = $this->themingDefaults->getTextColorPrimary();
572
+        $this->htmlBody .= vsprintf($this->button, [$color, $color, $url, $color, $textColor, $textColor, $text]);
573
+
574
+        if ($plainText !== false) {
575
+            $this->plainBody .= $plainText . ': ';
576
+        }
577
+
578
+        $this->plainBody .=  $url . PHP_EOL;
579
+
580
+    }
581
+
582
+    /**
583
+     * Close the HTML body when it is open
584
+     */
585
+    protected function ensureBodyIsClosed() {
586
+        if (!$this->bodyOpened) {
587
+            return;
588
+        }
589
+
590
+        $this->ensureBodyListClosed();
591
+
592
+        $this->htmlBody .= $this->bodyEnd;
593
+        $this->bodyOpened = false;
594
+    }
595
+
596
+    /**
597
+     * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email
598
+     *
599
+     * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
600
+     */
601
+    public function addFooter($text = '') {
602
+        if($text === '') {
603
+            $text = $this->themingDefaults->getName() . ' - ' . $this->themingDefaults->getSlogan() . '<br>' . $this->l10n->t('This is an automatically sent email, please do not reply.');
604
+        }
605
+
606
+        if ($this->footerAdded) {
607
+            return;
608
+        }
609
+        $this->footerAdded = true;
610
+
611
+        $this->ensureBodyIsClosed();
612
+
613
+        $this->htmlBody .= vsprintf($this->footer, [$text]);
614
+        $this->htmlBody .= $this->tail;
615
+        $this->plainBody .= PHP_EOL . '-- ' . PHP_EOL;
616
+        $this->plainBody .= str_replace('<br>', PHP_EOL, $text);
617
+    }
618
+
619
+    /**
620
+     * Returns the rendered email subject as string
621
+     *
622
+     * @return string
623
+     */
624
+    public function renderSubject() {
625
+        return $this->subject;
626
+    }
627
+
628
+    /**
629
+     * Returns the rendered HTML email as string
630
+     *
631
+     * @return string
632
+     */
633
+    public function renderHtml() {
634
+        if (!$this->footerAdded) {
635
+            $this->footerAdded = true;
636
+            $this->ensureBodyIsClosed();
637
+            $this->htmlBody .= $this->tail;
638
+        }
639
+        return $this->htmlBody;
640
+    }
641
+
642
+    /**
643
+     * Returns the rendered plain text email as string
644
+     *
645
+     * @return string
646
+     */
647
+    public function renderText() {
648
+        if (!$this->footerAdded) {
649
+            $this->footerAdded = true;
650
+            $this->ensureBodyIsClosed();
651
+            $this->htmlBody .= $this->tail;
652
+        }
653
+        return $this->plainBody;
654
+    }
655 655
 }
Please login to merge, or discard this patch.
core/Controller/LostController.php 1 patch
Indentation   +315 added lines, -315 removed lines patch added patch discarded remove patch
@@ -56,319 +56,319 @@
 block discarded – undo
56 56
  */
57 57
 class LostController extends Controller {
58 58
 
59
-	/** @var IURLGenerator */
60
-	protected $urlGenerator;
61
-	/** @var IUserManager */
62
-	protected $userManager;
63
-	/** @var Defaults */
64
-	protected $defaults;
65
-	/** @var IL10N */
66
-	protected $l10n;
67
-	/** @var string */
68
-	protected $from;
69
-	/** @var IManager */
70
-	protected $encryptionManager;
71
-	/** @var IConfig */
72
-	protected $config;
73
-	/** @var ISecureRandom */
74
-	protected $secureRandom;
75
-	/** @var IMailer */
76
-	protected $mailer;
77
-	/** @var ITimeFactory */
78
-	protected $timeFactory;
79
-	/** @var ICrypto */
80
-	protected $crypto;
81
-
82
-	/**
83
-	 * @param string $appName
84
-	 * @param IRequest $request
85
-	 * @param IURLGenerator $urlGenerator
86
-	 * @param IUserManager $userManager
87
-	 * @param Defaults $defaults
88
-	 * @param IL10N $l10n
89
-	 * @param IConfig $config
90
-	 * @param ISecureRandom $secureRandom
91
-	 * @param string $defaultMailAddress
92
-	 * @param IManager $encryptionManager
93
-	 * @param IMailer $mailer
94
-	 * @param ITimeFactory $timeFactory
95
-	 * @param ICrypto $crypto
96
-	 */
97
-	public function __construct($appName,
98
-								IRequest $request,
99
-								IURLGenerator $urlGenerator,
100
-								IUserManager $userManager,
101
-								Defaults $defaults,
102
-								IL10N $l10n,
103
-								IConfig $config,
104
-								ISecureRandom $secureRandom,
105
-								$defaultMailAddress,
106
-								IManager $encryptionManager,
107
-								IMailer $mailer,
108
-								ITimeFactory $timeFactory,
109
-								ICrypto $crypto) {
110
-		parent::__construct($appName, $request);
111
-		$this->urlGenerator = $urlGenerator;
112
-		$this->userManager = $userManager;
113
-		$this->defaults = $defaults;
114
-		$this->l10n = $l10n;
115
-		$this->secureRandom = $secureRandom;
116
-		$this->from = $defaultMailAddress;
117
-		$this->encryptionManager = $encryptionManager;
118
-		$this->config = $config;
119
-		$this->mailer = $mailer;
120
-		$this->timeFactory = $timeFactory;
121
-		$this->crypto = $crypto;
122
-	}
123
-
124
-	/**
125
-	 * Someone wants to reset their password:
126
-	 *
127
-	 * @PublicPage
128
-	 * @NoCSRFRequired
129
-	 *
130
-	 * @param string $token
131
-	 * @param string $userId
132
-	 * @return TemplateResponse
133
-	 */
134
-	public function resetform($token, $userId) {
135
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
136
-			return new TemplateResponse('core', 'error', [
137
-					'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
138
-				],
139
-				'guest'
140
-			);
141
-		}
142
-
143
-		try {
144
-			$this->checkPasswordResetToken($token, $userId);
145
-		} catch (\Exception $e) {
146
-			return new TemplateResponse(
147
-				'core', 'error', [
148
-					"errors" => array(array("error" => $e->getMessage()))
149
-				],
150
-				'guest'
151
-			);
152
-		}
153
-
154
-		return new TemplateResponse(
155
-			'core',
156
-			'lostpassword/resetpassword',
157
-			array(
158
-				'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
159
-			),
160
-			'guest'
161
-		);
162
-	}
163
-
164
-	/**
165
-	 * @param string $token
166
-	 * @param string $userId
167
-	 * @throws \Exception
168
-	 */
169
-	protected function checkPasswordResetToken($token, $userId) {
170
-		$user = $this->userManager->get($userId);
171
-		if($user === null || !$user->isEnabled()) {
172
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
173
-		}
174
-
175
-		try {
176
-			$encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
177
-			$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
178
-			$decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
179
-		} catch (\Exception $e) {
180
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
181
-		}
182
-
183
-		$splittedToken = explode(':', $decryptedToken);
184
-		if(count($splittedToken) !== 2) {
185
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
186
-		}
187
-
188
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
189
-			$user->getLastLogin() > $splittedToken[0]) {
190
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
191
-		}
192
-
193
-		if (!hash_equals($splittedToken[1], $token)) {
194
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
195
-		}
196
-	}
197
-
198
-	/**
199
-	 * @param $message
200
-	 * @param array $additional
201
-	 * @return array
202
-	 */
203
-	private function error($message, array $additional=array()) {
204
-		return array_merge(array('status' => 'error', 'msg' => $message), $additional);
205
-	}
206
-
207
-	/**
208
-	 * @return array
209
-	 */
210
-	private function success() {
211
-		return array('status'=>'success');
212
-	}
213
-
214
-	/**
215
-	 * @PublicPage
216
-	 * @BruteForceProtection(action=passwordResetEmail)
217
-	 * @AnonRateThrottle(limit=10, period=300)
218
-	 *
219
-	 * @param string $user
220
-	 * @return JSONResponse
221
-	 */
222
-	public function email($user){
223
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
224
-			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
225
-		}
226
-
227
-		\OCP\Util::emitHook(
228
-			'\OCA\Files_Sharing\API\Server2Server',
229
-			'preLoginNameUsedAsUserName',
230
-			['uid' => &$user]
231
-		);
232
-
233
-		// FIXME: use HTTP error codes
234
-		try {
235
-			$this->sendEmail($user);
236
-		} catch (\Exception $e){
237
-			$response = new JSONResponse($this->error($e->getMessage()));
238
-			$response->throttle();
239
-			return $response;
240
-		}
241
-
242
-		$response = new JSONResponse($this->success());
243
-		$response->throttle();
244
-		return $response;
245
-	}
246
-
247
-	/**
248
-	 * @PublicPage
249
-	 * @param string $token
250
-	 * @param string $userId
251
-	 * @param string $password
252
-	 * @param boolean $proceed
253
-	 * @return array
254
-	 */
255
-	public function setPassword($token, $userId, $password, $proceed) {
256
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
257
-			return $this->error($this->l10n->t('Password reset is disabled'));
258
-		}
259
-
260
-		if ($this->encryptionManager->isEnabled() && !$proceed) {
261
-			return $this->error('', array('encryption' => true));
262
-		}
263
-
264
-		try {
265
-			$this->checkPasswordResetToken($token, $userId);
266
-			$user = $this->userManager->get($userId);
267
-
268
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
269
-
270
-			if (!$user->setPassword($password)) {
271
-				throw new \Exception();
272
-			}
273
-
274
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
275
-
276
-			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
277
-			@\OC::$server->getUserSession()->unsetMagicInCookie();
278
-		} catch (\Exception $e){
279
-			return $this->error($e->getMessage());
280
-		}
281
-
282
-		return $this->success();
283
-	}
284
-
285
-	/**
286
-	 * @param string $input
287
-	 * @throws \Exception
288
-	 */
289
-	protected function sendEmail($input) {
290
-		$user = $this->findUserByIdOrMail($input);
291
-		$email = $user->getEMailAddress();
292
-
293
-		if (empty($email)) {
294
-			throw new \Exception(
295
-				$this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
296
-			);
297
-		}
298
-
299
-		// Generate the token. It is stored encrypted in the database with the
300
-		// secret being the users' email address appended with the system secret.
301
-		// This makes the token automatically invalidate once the user changes
302
-		// their email address.
303
-		$token = $this->secureRandom->generate(
304
-			21,
305
-			ISecureRandom::CHAR_DIGITS.
306
-			ISecureRandom::CHAR_LOWER.
307
-			ISecureRandom::CHAR_UPPER
308
-		);
309
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
310
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
311
-		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
312
-
313
-		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
314
-
315
-		$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
316
-			'link' => $link,
317
-		]);
318
-
319
-		$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
320
-		$emailTemplate->addHeader();
321
-		$emailTemplate->addHeading($this->l10n->t('Password reset'));
322
-
323
-		$emailTemplate->addBodyText(
324
-			htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
325
-			$this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
326
-		);
327
-
328
-		$emailTemplate->addBodyButton(
329
-			htmlspecialchars($this->l10n->t('Reset your password')),
330
-			$link,
331
-			false
332
-		);
333
-		$emailTemplate->addFooter();
334
-
335
-		try {
336
-			$message = $this->mailer->createMessage();
337
-			$message->setTo([$email => $user->getUID()]);
338
-			$message->setFrom([$this->from => $this->defaults->getName()]);
339
-			$message->useTemplate($emailTemplate);
340
-			$this->mailer->send($message);
341
-		} catch (\Exception $e) {
342
-			throw new \Exception($this->l10n->t(
343
-				'Couldn\'t send reset email. Please contact your administrator.'
344
-			));
345
-		}
346
-	}
347
-
348
-	/**
349
-	 * @param string $input
350
-	 * @return IUser
351
-	 * @throws \InvalidArgumentException
352
-	 */
353
-	protected function findUserByIdOrMail($input) {
354
-		$user = $this->userManager->get($input);
355
-		if ($user instanceof IUser) {
356
-			if (!$user->isEnabled()) {
357
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
358
-			}
359
-
360
-			return $user;
361
-		}
362
-		$users = $this->userManager->getByEmail($input);
363
-		if (count($users) === 1) {
364
-			$user = $users[0];
365
-			if (!$user->isEnabled()) {
366
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
367
-			}
368
-
369
-			return $user;
370
-		}
371
-
372
-		throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
373
-	}
59
+    /** @var IURLGenerator */
60
+    protected $urlGenerator;
61
+    /** @var IUserManager */
62
+    protected $userManager;
63
+    /** @var Defaults */
64
+    protected $defaults;
65
+    /** @var IL10N */
66
+    protected $l10n;
67
+    /** @var string */
68
+    protected $from;
69
+    /** @var IManager */
70
+    protected $encryptionManager;
71
+    /** @var IConfig */
72
+    protected $config;
73
+    /** @var ISecureRandom */
74
+    protected $secureRandom;
75
+    /** @var IMailer */
76
+    protected $mailer;
77
+    /** @var ITimeFactory */
78
+    protected $timeFactory;
79
+    /** @var ICrypto */
80
+    protected $crypto;
81
+
82
+    /**
83
+     * @param string $appName
84
+     * @param IRequest $request
85
+     * @param IURLGenerator $urlGenerator
86
+     * @param IUserManager $userManager
87
+     * @param Defaults $defaults
88
+     * @param IL10N $l10n
89
+     * @param IConfig $config
90
+     * @param ISecureRandom $secureRandom
91
+     * @param string $defaultMailAddress
92
+     * @param IManager $encryptionManager
93
+     * @param IMailer $mailer
94
+     * @param ITimeFactory $timeFactory
95
+     * @param ICrypto $crypto
96
+     */
97
+    public function __construct($appName,
98
+                                IRequest $request,
99
+                                IURLGenerator $urlGenerator,
100
+                                IUserManager $userManager,
101
+                                Defaults $defaults,
102
+                                IL10N $l10n,
103
+                                IConfig $config,
104
+                                ISecureRandom $secureRandom,
105
+                                $defaultMailAddress,
106
+                                IManager $encryptionManager,
107
+                                IMailer $mailer,
108
+                                ITimeFactory $timeFactory,
109
+                                ICrypto $crypto) {
110
+        parent::__construct($appName, $request);
111
+        $this->urlGenerator = $urlGenerator;
112
+        $this->userManager = $userManager;
113
+        $this->defaults = $defaults;
114
+        $this->l10n = $l10n;
115
+        $this->secureRandom = $secureRandom;
116
+        $this->from = $defaultMailAddress;
117
+        $this->encryptionManager = $encryptionManager;
118
+        $this->config = $config;
119
+        $this->mailer = $mailer;
120
+        $this->timeFactory = $timeFactory;
121
+        $this->crypto = $crypto;
122
+    }
123
+
124
+    /**
125
+     * Someone wants to reset their password:
126
+     *
127
+     * @PublicPage
128
+     * @NoCSRFRequired
129
+     *
130
+     * @param string $token
131
+     * @param string $userId
132
+     * @return TemplateResponse
133
+     */
134
+    public function resetform($token, $userId) {
135
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
136
+            return new TemplateResponse('core', 'error', [
137
+                    'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
138
+                ],
139
+                'guest'
140
+            );
141
+        }
142
+
143
+        try {
144
+            $this->checkPasswordResetToken($token, $userId);
145
+        } catch (\Exception $e) {
146
+            return new TemplateResponse(
147
+                'core', 'error', [
148
+                    "errors" => array(array("error" => $e->getMessage()))
149
+                ],
150
+                'guest'
151
+            );
152
+        }
153
+
154
+        return new TemplateResponse(
155
+            'core',
156
+            'lostpassword/resetpassword',
157
+            array(
158
+                'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
159
+            ),
160
+            'guest'
161
+        );
162
+    }
163
+
164
+    /**
165
+     * @param string $token
166
+     * @param string $userId
167
+     * @throws \Exception
168
+     */
169
+    protected function checkPasswordResetToken($token, $userId) {
170
+        $user = $this->userManager->get($userId);
171
+        if($user === null || !$user->isEnabled()) {
172
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
173
+        }
174
+
175
+        try {
176
+            $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
177
+            $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
178
+            $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
179
+        } catch (\Exception $e) {
180
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
181
+        }
182
+
183
+        $splittedToken = explode(':', $decryptedToken);
184
+        if(count($splittedToken) !== 2) {
185
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
186
+        }
187
+
188
+        if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
189
+            $user->getLastLogin() > $splittedToken[0]) {
190
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
191
+        }
192
+
193
+        if (!hash_equals($splittedToken[1], $token)) {
194
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
195
+        }
196
+    }
197
+
198
+    /**
199
+     * @param $message
200
+     * @param array $additional
201
+     * @return array
202
+     */
203
+    private function error($message, array $additional=array()) {
204
+        return array_merge(array('status' => 'error', 'msg' => $message), $additional);
205
+    }
206
+
207
+    /**
208
+     * @return array
209
+     */
210
+    private function success() {
211
+        return array('status'=>'success');
212
+    }
213
+
214
+    /**
215
+     * @PublicPage
216
+     * @BruteForceProtection(action=passwordResetEmail)
217
+     * @AnonRateThrottle(limit=10, period=300)
218
+     *
219
+     * @param string $user
220
+     * @return JSONResponse
221
+     */
222
+    public function email($user){
223
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
224
+            return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
225
+        }
226
+
227
+        \OCP\Util::emitHook(
228
+            '\OCA\Files_Sharing\API\Server2Server',
229
+            'preLoginNameUsedAsUserName',
230
+            ['uid' => &$user]
231
+        );
232
+
233
+        // FIXME: use HTTP error codes
234
+        try {
235
+            $this->sendEmail($user);
236
+        } catch (\Exception $e){
237
+            $response = new JSONResponse($this->error($e->getMessage()));
238
+            $response->throttle();
239
+            return $response;
240
+        }
241
+
242
+        $response = new JSONResponse($this->success());
243
+        $response->throttle();
244
+        return $response;
245
+    }
246
+
247
+    /**
248
+     * @PublicPage
249
+     * @param string $token
250
+     * @param string $userId
251
+     * @param string $password
252
+     * @param boolean $proceed
253
+     * @return array
254
+     */
255
+    public function setPassword($token, $userId, $password, $proceed) {
256
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
257
+            return $this->error($this->l10n->t('Password reset is disabled'));
258
+        }
259
+
260
+        if ($this->encryptionManager->isEnabled() && !$proceed) {
261
+            return $this->error('', array('encryption' => true));
262
+        }
263
+
264
+        try {
265
+            $this->checkPasswordResetToken($token, $userId);
266
+            $user = $this->userManager->get($userId);
267
+
268
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
269
+
270
+            if (!$user->setPassword($password)) {
271
+                throw new \Exception();
272
+            }
273
+
274
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
275
+
276
+            $this->config->deleteUserValue($userId, 'core', 'lostpassword');
277
+            @\OC::$server->getUserSession()->unsetMagicInCookie();
278
+        } catch (\Exception $e){
279
+            return $this->error($e->getMessage());
280
+        }
281
+
282
+        return $this->success();
283
+    }
284
+
285
+    /**
286
+     * @param string $input
287
+     * @throws \Exception
288
+     */
289
+    protected function sendEmail($input) {
290
+        $user = $this->findUserByIdOrMail($input);
291
+        $email = $user->getEMailAddress();
292
+
293
+        if (empty($email)) {
294
+            throw new \Exception(
295
+                $this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
296
+            );
297
+        }
298
+
299
+        // Generate the token. It is stored encrypted in the database with the
300
+        // secret being the users' email address appended with the system secret.
301
+        // This makes the token automatically invalidate once the user changes
302
+        // their email address.
303
+        $token = $this->secureRandom->generate(
304
+            21,
305
+            ISecureRandom::CHAR_DIGITS.
306
+            ISecureRandom::CHAR_LOWER.
307
+            ISecureRandom::CHAR_UPPER
308
+        );
309
+        $tokenValue = $this->timeFactory->getTime() .':'. $token;
310
+        $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
311
+        $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
312
+
313
+        $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
314
+
315
+        $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
316
+            'link' => $link,
317
+        ]);
318
+
319
+        $emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
320
+        $emailTemplate->addHeader();
321
+        $emailTemplate->addHeading($this->l10n->t('Password reset'));
322
+
323
+        $emailTemplate->addBodyText(
324
+            htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
325
+            $this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
326
+        );
327
+
328
+        $emailTemplate->addBodyButton(
329
+            htmlspecialchars($this->l10n->t('Reset your password')),
330
+            $link,
331
+            false
332
+        );
333
+        $emailTemplate->addFooter();
334
+
335
+        try {
336
+            $message = $this->mailer->createMessage();
337
+            $message->setTo([$email => $user->getUID()]);
338
+            $message->setFrom([$this->from => $this->defaults->getName()]);
339
+            $message->useTemplate($emailTemplate);
340
+            $this->mailer->send($message);
341
+        } catch (\Exception $e) {
342
+            throw new \Exception($this->l10n->t(
343
+                'Couldn\'t send reset email. Please contact your administrator.'
344
+            ));
345
+        }
346
+    }
347
+
348
+    /**
349
+     * @param string $input
350
+     * @return IUser
351
+     * @throws \InvalidArgumentException
352
+     */
353
+    protected function findUserByIdOrMail($input) {
354
+        $user = $this->userManager->get($input);
355
+        if ($user instanceof IUser) {
356
+            if (!$user->isEnabled()) {
357
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
358
+            }
359
+
360
+            return $user;
361
+        }
362
+        $users = $this->userManager->getByEmail($input);
363
+        if (count($users) === 1) {
364
+            $user = $users[0];
365
+            if (!$user->isEnabled()) {
366
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
367
+            }
368
+
369
+            return $user;
370
+        }
371
+
372
+        throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
373
+    }
374 374
 }
Please login to merge, or discard this patch.
lib/public/Mail/IEMailTemplate.php 2 patches
Doc Comments   +10 added lines patch added patch discarded remove patch
@@ -61,6 +61,7 @@  discard block
 block discarded – undo
61 61
 	 * @param string $subject
62 62
 	 *
63 63
 	 * @since 13.0.0
64
+	 * @return void
64 65
 	 */
65 66
 	public function setSubject($subject);
66 67
 
@@ -68,6 +69,7 @@  discard block
 block discarded – undo
68 69
 	 * Adds a header to the email
69 70
 	 *
70 71
 	 * @since 12.0.0
72
+	 * @return void
71 73
 	 */
72 74
 	public function addHeader();
73 75
 
@@ -79,6 +81,7 @@  discard block
 block discarded – undo
79 81
 	 *   if empty the $title is used, if false none will be used
80 82
 	 *
81 83
 	 * @since 12.0.0
84
+	 * @return void
82 85
 	 */
83 86
 	public function addHeading($title, $plainTitle = '');
84 87
 
@@ -88,8 +91,10 @@  discard block
 block discarded – undo
88 91
 	 * @param string $text; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
89 92
 	 * @param string|bool $plainText Text that is used in the plain text email
90 93
 	 *   if empty the $text is used, if false none will be used
94
+	 * @param string $text
91 95
 	 *
92 96
 	 * @since 12.0.0
97
+	 * @return void
93 98
 	 */
94 99
 	public function addBodyText($text, $plainText = '');
95 100
 
@@ -103,7 +108,9 @@  discard block
 block discarded – undo
103 108
 	 *   if empty the $text is used, if false none will be used
104 109
 	 * @param string $plainMetaInfo Meta info that is used in the plain text email
105 110
 	 *   if empty the $metaInfo is used, if false none will be used
111
+	 * @param string $text
106 112
 	 * @since 12.0.0
113
+	 * @return void
107 114
 	 */
108 115
 	public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '');
109 116
 
@@ -118,6 +125,7 @@  discard block
 block discarded – undo
118 125
 	 * @param string $plainTextRight Text of right button that is used in the plain text version - if empty the $textRight is used
119 126
 	 *
120 127
 	 * @since 12.0.0
128
+	 * @return void
121 129
 	 */
122 130
 	public function addBodyButtonGroup($textLeft, $urlLeft, $textRight, $urlRight, $plainTextLeft = '', $plainTextRight = '');
123 131
 
@@ -130,6 +138,7 @@  discard block
 block discarded – undo
130 138
 	 * 		if empty the $text is used, if false none will be used
131 139
 	 *
132 140
 	 * @since 12.0.0
141
+	 * @return void
133 142
 	 */
134 143
 	public function addBodyButton($text, $url, $plainText = '');
135 144
 
@@ -139,6 +148,7 @@  discard block
 block discarded – undo
139 148
 	 * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
140 149
 	 *
141 150
 	 * @since 12.0.0
151
+	 * @return void
142 152
 	 */
143 153
 	public function addFooter($text = '');
144 154
 
Please login to merge, or discard this patch.
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -55,117 +55,117 @@
 block discarded – undo
55 55
  */
56 56
 interface IEMailTemplate {
57 57
 
58
-	/**
59
-	 * Sets the subject of the email
60
-	 *
61
-	 * @param string $subject
62
-	 *
63
-	 * @since 13.0.0
64
-	 */
65
-	public function setSubject($subject);
58
+    /**
59
+     * Sets the subject of the email
60
+     *
61
+     * @param string $subject
62
+     *
63
+     * @since 13.0.0
64
+     */
65
+    public function setSubject($subject);
66 66
 
67
-	/**
68
-	 * Adds a header to the email
69
-	 *
70
-	 * @since 12.0.0
71
-	 */
72
-	public function addHeader();
67
+    /**
68
+     * Adds a header to the email
69
+     *
70
+     * @since 12.0.0
71
+     */
72
+    public function addHeader();
73 73
 
74
-	/**
75
-	 * Adds a heading to the email
76
-	 *
77
-	 * @param string $title
78
-	 * @param string|bool $plainTitle Title that is used in the plain text email
79
-	 *   if empty the $title is used, if false none will be used
80
-	 *
81
-	 * @since 12.0.0
82
-	 */
83
-	public function addHeading($title, $plainTitle = '');
74
+    /**
75
+     * Adds a heading to the email
76
+     *
77
+     * @param string $title
78
+     * @param string|bool $plainTitle Title that is used in the plain text email
79
+     *   if empty the $title is used, if false none will be used
80
+     *
81
+     * @since 12.0.0
82
+     */
83
+    public function addHeading($title, $plainTitle = '');
84 84
 
85
-	/**
86
-	 * Adds a paragraph to the body of the email
87
-	 *
88
-	 * @param string $text; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
89
-	 * @param string|bool $plainText Text that is used in the plain text email
90
-	 *   if empty the $text is used, if false none will be used
91
-	 *
92
-	 * @since 12.0.0
93
-	 */
94
-	public function addBodyText($text, $plainText = '');
85
+    /**
86
+     * Adds a paragraph to the body of the email
87
+     *
88
+     * @param string $text; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
89
+     * @param string|bool $plainText Text that is used in the plain text email
90
+     *   if empty the $text is used, if false none will be used
91
+     *
92
+     * @since 12.0.0
93
+     */
94
+    public function addBodyText($text, $plainText = '');
95 95
 
96
-	/**
97
-	 * Adds a list item to the body of the email
98
-	 *
99
-	 * @param string $text; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
100
-	 * @param string $metaInfo; Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
101
-	 * @param string $icon Absolute path, must be 16*16 pixels
102
-	 * @param string $plainText Text that is used in the plain text email
103
-	 *   if empty the $text is used, if false none will be used
104
-	 * @param string $plainMetaInfo Meta info that is used in the plain text email
105
-	 *   if empty the $metaInfo is used, if false none will be used
106
-	 * @since 12.0.0
107
-	 */
108
-	public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '');
96
+    /**
97
+     * Adds a list item to the body of the email
98
+     *
99
+     * @param string $text; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
100
+     * @param string $metaInfo; Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
101
+     * @param string $icon Absolute path, must be 16*16 pixels
102
+     * @param string $plainText Text that is used in the plain text email
103
+     *   if empty the $text is used, if false none will be used
104
+     * @param string $plainMetaInfo Meta info that is used in the plain text email
105
+     *   if empty the $metaInfo is used, if false none will be used
106
+     * @since 12.0.0
107
+     */
108
+    public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '');
109 109
 
110
-	/**
111
-	 * Adds a button group of two buttons to the body of the email
112
-	 *
113
-	 * @param string $textLeft Text of left button; Note: When $plainTextLeft falls back to this, HTML is automatically escaped in the HTML email
114
-	 * @param string $urlLeft URL of left button
115
-	 * @param string $textRight Text of right button; Note: When $plainTextRight falls back to this, HTML is automatically escaped in the HTML email
116
-	 * @param string $urlRight URL of right button
117
-	 * @param string $plainTextLeft Text of left button that is used in the plain text version - if empty the $textLeft is used
118
-	 * @param string $plainTextRight Text of right button that is used in the plain text version - if empty the $textRight is used
119
-	 *
120
-	 * @since 12.0.0
121
-	 */
122
-	public function addBodyButtonGroup($textLeft, $urlLeft, $textRight, $urlRight, $plainTextLeft = '', $plainTextRight = '');
110
+    /**
111
+     * Adds a button group of two buttons to the body of the email
112
+     *
113
+     * @param string $textLeft Text of left button; Note: When $plainTextLeft falls back to this, HTML is automatically escaped in the HTML email
114
+     * @param string $urlLeft URL of left button
115
+     * @param string $textRight Text of right button; Note: When $plainTextRight falls back to this, HTML is automatically escaped in the HTML email
116
+     * @param string $urlRight URL of right button
117
+     * @param string $plainTextLeft Text of left button that is used in the plain text version - if empty the $textLeft is used
118
+     * @param string $plainTextRight Text of right button that is used in the plain text version - if empty the $textRight is used
119
+     *
120
+     * @since 12.0.0
121
+     */
122
+    public function addBodyButtonGroup($textLeft, $urlLeft, $textRight, $urlRight, $plainTextLeft = '', $plainTextRight = '');
123 123
 
124
-	/**
125
-	 * Adds a button to the body of the email
126
-	 *
127
-	 * @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
128
-	 * @param string $url URL of button
129
-	 * @param string $plainText Text of button in plain text version
130
-	 * 		if empty the $text is used, if false none will be used
131
-	 *
132
-	 * @since 12.0.0
133
-	 */
134
-	public function addBodyButton($text, $url, $plainText = '');
124
+    /**
125
+     * Adds a button to the body of the email
126
+     *
127
+     * @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
128
+     * @param string $url URL of button
129
+     * @param string $plainText Text of button in plain text version
130
+     * 		if empty the $text is used, if false none will be used
131
+     *
132
+     * @since 12.0.0
133
+     */
134
+    public function addBodyButton($text, $url, $plainText = '');
135 135
 
136
-	/**
137
-	 * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email
138
-	 *
139
-	 * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
140
-	 *
141
-	 * @since 12.0.0
142
-	 */
143
-	public function addFooter($text = '');
136
+    /**
137
+     * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email
138
+     *
139
+     * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
140
+     *
141
+     * @since 12.0.0
142
+     */
143
+    public function addFooter($text = '');
144 144
 
145
-	/**
146
-	 * Returns the rendered email subject as string
147
-	 *
148
-	 * @return string
149
-	 *
150
-	 * @since 13.0.0
151
-	 */
152
-	public function renderSubject();
145
+    /**
146
+     * Returns the rendered email subject as string
147
+     *
148
+     * @return string
149
+     *
150
+     * @since 13.0.0
151
+     */
152
+    public function renderSubject();
153 153
 
154
-	/**
155
-	 * Returns the rendered HTML email as string
156
-	 *
157
-	 * @return string
158
-	 *
159
-	 * @since 12.0.0
160
-	 */
161
-	public function renderHtml();
154
+    /**
155
+     * Returns the rendered HTML email as string
156
+     *
157
+     * @return string
158
+     *
159
+     * @since 12.0.0
160
+     */
161
+    public function renderHtml();
162 162
 
163
-	/**
164
-	 * Returns the rendered plain text email as string
165
-	 *
166
-	 * @return string
167
-	 *
168
-	 * @since 12.0.0
169
-	 */
170
-	public function renderText();
163
+    /**
164
+     * Returns the rendered plain text email as string
165
+     *
166
+     * @return string
167
+     *
168
+     * @since 12.0.0
169
+     */
170
+    public function renderText();
171 171
 }
Please login to merge, or discard this patch.