Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 57 additions & 25 deletions apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use OCP\IConfig;
use OCP\IUserSession;
use OCP\Mail\IMailer;
use OCP\Mail\Provider\IManager as IMailManager;
use OCP\Mail\Provider\IMessageSend;
use OCP\Util;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin;
Expand Down Expand Up @@ -55,6 +57,7 @@ class IMipPlugin extends SabreIMipPlugin {
public const METHOD_CANCEL = 'cancel';
public const IMIP_INDENT = 15; // Enough for the length of all body bullet items, in all languages
private EventComparisonService $eventComparisonService;
private IMailManager $mailManager;

public function __construct(IConfig $config,
IMailer $mailer,
Expand All @@ -63,7 +66,8 @@ public function __construct(IConfig $config,
Defaults $defaults,
IUserSession $userSession,
IMipService $imipService,
EventComparisonService $eventComparisonService) {
EventComparisonService $eventComparisonService,
IMailManager $mailManager) {
parent::__construct('');
$this->userSession = $userSession;
$this->config = $config;
Expand All @@ -73,6 +77,7 @@ public function __construct(IConfig $config,
$this->defaults = $defaults;
$this->imipService = $imipService;
$this->eventComparisonService = $eventComparisonService;
$this->mailManager = $mailManager;
}

public function initialize(DAV\Server $server): void {
Expand Down Expand Up @@ -212,21 +217,6 @@ public function schedule(Message $iTipMessage) {
$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
$fromName = $this->imipService->getFrom($senderName, $this->defaults->getName());

$message = $this->mailer->createMessage()
->setFrom([$fromEMail => $fromName]);

if ($recipientName !== null) {
$message->setTo([$recipient => $recipientName]);
} else {
$message->setTo([$recipient]);
}

if ($senderName !== null) {
$message->setReplyTo([$sender => $senderName]);
} else {
$message->setReplyTo([$sender]);
}

$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
$template->addHeader();

Expand Down Expand Up @@ -268,18 +258,60 @@ public function schedule(Message $iTipMessage) {
}

$template->addFooter();

$message->useTemplate($template);

// convert iTip Message to string
$itip_msg = $iTipMessage->message->serialize();
$message->attachInline(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method,
);

$user = null;
$mailService = null;

try {
$failed = $this->mailer->send($message);
// retrieve user object
$user = $this->userSession->getUser();
// evaluate if user object exist
if ($user !== null) {
// retrieve appropriate service with the same address as sender
$mailService = $this->mailManager->findServiceByAddress($user->getUID(), $sender);
}
// evaluate if a mail service was found and has sending capabilities
if ($mailService !== null && $mailService instanceof IMessageSend) {
// construct mail message and set required parameters
$message = $mailService->initiateMessage();
$message->setFrom(
(new \OCP\Mail\Provider\Address($sender, $fromName))
);
$message->setTo(
(new \OCP\Mail\Provider\Address($recipient, $recipientName))
);
$message->setSubject($template->renderSubject());
$message->setBodyPlain($template->renderText());
$message->setBodyHtml($template->renderHtml());
$message->setAttachments((new \OCP\Mail\Provider\Attachment(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method,
true
)));
// send message
$mailService->sendMessage($message);
} else {
// construct symfony mailer message and set required parameters
$message = $this->mailer->createMessage();
$message->setFrom([$fromEMail => $fromName]);
$message->setTo(
(($recipientName !== null) ? [$recipient => $recipientName] : [$recipient])
);
$message->setReplyTo(
(($senderName !== null) ? [$sender => $senderName] : [$sender])

Check notice

Code scanning / Psalm

RedundantConditionGivenDocblockType

Docblock-defined type string can never contain null

Check notice

Code scanning / Psalm

DocblockTypeContradiction

Cannot resolve types for $senderName - docblock-defined type string does not contain null
);
$message->useTemplate($template);
$message->attachInline(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method
);
$failed = $this->mailer->send($message);
}

$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
if (!empty($failed)) {
$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
Expand Down
3 changes: 2 additions & 1 deletion apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ public function __construct(IRequest $request, string $baseUri) {
\OC::$server->get(\OCP\Defaults::class),
$userSession,
\OC::$server->get(\OCA\DAV\CalDAV\Schedule\IMipService::class),
\OC::$server->get(\OCA\DAV\CalDAV\EventComparisonService::class)
\OC::$server->get(\OCA\DAV\CalDAV\EventComparisonService::class),
\OC::$server->get(\OCP\Mail\Provider\IManager::class)
));
}
$this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin());
Expand Down
136 changes: 131 additions & 5 deletions apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Mail\IMessage;
use OCP\Mail\Provider\IManager as IMailManager;
use OCP\Mail\Provider\IMessage as IMailMessageNew;
use OCP\Mail\Provider\IMessageSend as IMailMessageSend;
use OCP\Mail\Provider\IService as IMailService;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Sabre\VObject\Component\VCalendar;
Expand All @@ -26,6 +30,11 @@
use Test\TestCase;
use function array_merge;

interface IMailServiceMock extends IMailService, IMailMessageSend {
// workaround for creating mock class with multiple interfaces
// TODO: remove after phpUnit 10 is supported.
}

class IMipPluginTest extends TestCase {

/** @var IMessage|MockObject */
Expand Down Expand Up @@ -67,6 +76,15 @@ class IMipPluginTest extends TestCase {
/** @var EventComparisonService|MockObject */
private $eventComparisonService;

/** @var IMailManager|MockObject */
private $mailManager;

/** @var IMailService|IMailMessageSend|MockObject */
private $mailService;

/** @var IMailMessageNew|MockObject */
private $mailMessageNew;

protected function setUp(): void {
$this->mailMessage = $this->createMock(IMessage::class);
$this->mailMessage->method('setFrom')->willReturn($this->mailMessage);
Expand All @@ -90,10 +108,6 @@ protected function setUp(): void {
$this->config = $this->createMock(IConfig::class);

$this->user = $this->createMock(IUser::class);
/*
$this->user->method('getUID');
$this->user->method('getDisplayName');
*/

$this->userSession = $this->createMock(IUserSession::class);
$this->userSession->method('getUser')
Expand All @@ -107,6 +121,12 @@ protected function setUp(): void {

$this->eventComparisonService = $this->createMock(EventComparisonService::class);

$this->mailManager = $this->createMock(IMailManager::class);

$this->mailService = $this->createMock(IMailServiceMock::class);

$this->mailMessageNew = $this->createMock(IMailMessageNew::class);

$this->plugin = new IMipPlugin(
$this->config,
$this->mailer,
Expand All @@ -115,7 +135,8 @@ protected function setUp(): void {
$this->defaults,
$this->userSession,
$this->service,
$this->eventComparisonService
$this->eventComparisonService,
$this->mailManager,
);
}

Expand Down Expand Up @@ -582,6 +603,111 @@ public function testFailedDelivery(): void {
$this->assertEquals('5.0', $message->getScheduleStatus());
}

public function testMailProviderSend(): void {
// construct iTip message with event and attendees
$message = new Message();
$message->method = 'REQUEST';
$calendar = new VCalendar();
$event = new VEvent($calendar, 'one', array_merge([
'UID' => 'uid-1234',
'SEQUENCE' => 1,
'SUMMARY' => 'Fellowship meeting without (!) Boromir',
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
], []));
$event->add('ORGANIZER', 'mailto:[email protected]');
$event->add('ATTENDEE', 'mailto:' . '[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$message->message = $calendar;
$message->sender = 'mailto:[email protected]';
$message->senderName = 'Mr. Wizard';
$message->recipient = 'mailto:' . '[email protected]';
// construct
foreach ($event->select('ATTENDEE') as $entry) {
if (strcasecmp($entry->getValue(), $message->recipient) === 0) {
$attendee = $entry;
}
}
// construct body data return
$data = ['invitee_name' => 'Mr. Wizard',
'meeting_title' => 'Fellowship meeting without (!) Boromir',
'attendee_name' => '[email protected]'
];
// construct system config mock returns
$this->config->expects(self::once())
->method('getAppValue')
->with('dav', 'invitation_link_recipients', 'yes')
->willReturn('yes');
// construct user mock returns
$this->user->expects(self::any())
->method('getUID')
->willReturn('user1');
$this->user->expects(self::any())
->method('getDisplayName')
->willReturn('Mr. Wizard');
// construct user session mock returns
$this->userSession->expects(self::any())
->method('getUser')
->willReturn($this->user);
// construct service mock returns
$this->service->expects(self::once())
->method('getLastOccurrence')
->willReturn('1496912700');
$this->service->expects(self::once())
->method('getCurrentAttendee')
->with($message)
->willReturn($attendee);
$this->service->expects(self::once())
->method('isRoomOrResource')
->with($attendee)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($event, null)
->willReturn($data);
$this->service->expects(self::once())
->method('getFrom');
$this->service->expects(self::once())
->method('addSubjectAndHeading')
->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting without (!) Boromir', false);
$this->service->expects(self::once())
->method('addBulletList')
->with($this->emailTemplate, $event, $data);
$this->service->expects(self::once())
->method('getAttendeeRsvpOrReqForParticipant')
->willReturn(true);
$this->service->expects(self::once())
->method('createInvitationToken')
->with($message, $event, '1496912700')
->willReturn('token');
$this->service->expects(self::once())
->method('addResponseButtons')
->with($this->emailTemplate, 'token');
$this->service->expects(self::once())
->method('addMoreOptionsButton')
->with($this->emailTemplate, 'token');
$this->eventComparisonService->expects(self::once())
->method('findModified')
->willReturn(['old' => [] ,'new' => [$event]]);
// construct mail mock returns
$this->mailer->expects(self::once())
->method('validateMailAddress')
->with('[email protected]')
->willReturn(true);
// construct mail provider mock returns
$this->mailService
->method('initiateMessage')
->willReturn($this->mailMessageNew);
$this->mailService
->method('sendMessage')
->with($this->mailMessageNew);
$this->mailManager
->method('findServiceByAddress')
->with('user1', '[email protected]')
->willReturn($this->mailService);

$this->plugin->schedule($message);
$this->assertEquals('1.1', $message->getScheduleStatus());
}

public function testNoOldEvent(): void {
$message = new Message();
$message->method = 'REQUEST';
Expand Down
13 changes: 13 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,18 @@
'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php',
'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php',
'OCP\\Mail\\Provider\\Attachment' => $baseDir . '/lib/public/Mail/Provider/Attachment.php',
'OCP\\Mail\\Provider\\Exception\\Exception' => $baseDir . '/lib/public/Mail/Provider/Exception/Exception.php',
'OCP\\Mail\\Provider\\Exception\\SendException' => $baseDir . '/lib/public/Mail/Provider/Exception/SendException.php',
'OCP\\Mail\\Provider\\IAddress' => $baseDir . '/lib/public/Mail/Provider/IAddress.php',
'OCP\\Mail\\Provider\\IAttachment' => $baseDir . '/lib/public/Mail/Provider/IAttachment.php',
'OCP\\Mail\\Provider\\IManager' => $baseDir . '/lib/public/Mail/Provider/IManager.php',
'OCP\\Mail\\Provider\\IMessage' => $baseDir . '/lib/public/Mail/Provider/IMessage.php',
'OCP\\Mail\\Provider\\IMessageSend' => $baseDir . '/lib/public/Mail/Provider/IMessageSend.php',
'OCP\\Mail\\Provider\\IProvider' => $baseDir . '/lib/public/Mail/Provider/IProvider.php',
'OCP\\Mail\\Provider\\IService' => $baseDir . '/lib/public/Mail/Provider/IService.php',
'OCP\\Mail\\Provider\\Message' => $baseDir . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php',
Expand Down Expand Up @@ -1618,6 +1630,7 @@
'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php',
'OC\\Mail\\Provider\\Manager' => $baseDir . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => $baseDir . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => $baseDir . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => $baseDir . '/lib/private/Memcache/CADTrait.php',
Expand Down
13 changes: 13 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,18 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php',
'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php',
'OCP\\Mail\\Provider\\Attachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Attachment.php',
'OCP\\Mail\\Provider\\Exception\\Exception' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/Exception.php',
'OCP\\Mail\\Provider\\Exception\\SendException' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/SendException.php',
'OCP\\Mail\\Provider\\IAddress' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAddress.php',
'OCP\\Mail\\Provider\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAttachment.php',
'OCP\\Mail\\Provider\\IManager' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IManager.php',
'OCP\\Mail\\Provider\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessage.php',
'OCP\\Mail\\Provider\\IMessageSend' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessageSend.php',
'OCP\\Mail\\Provider\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IProvider.php',
'OCP\\Mail\\Provider\\IService' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IService.php',
'OCP\\Mail\\Provider\\Message' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php',
Expand Down Expand Up @@ -1651,6 +1663,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php',
'OC\\Mail\\Provider\\Manager' => __DIR__ . '/../../..' . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => __DIR__ . '/../../..' . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CADTrait.php',
Expand Down
Loading