Skip to content

Commit 8801634

Browse files
Merge pull request #12129 from nextcloud/fix/imip-processing-error-catch
fix: handle imip failures properly
2 parents 26c72b0 + 3a200ac commit 8801634

File tree

2 files changed

+94
-14
lines changed

2 files changed

+94
-14
lines changed

lib/Service/IMipService.php

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use OCP\AppFramework\Db\DoesNotExistException;
2020
use OCP\Calendar\IManager;
2121
use Psr\Log\LoggerInterface;
22+
use Throwable;
23+
2224
use function array_filter;
2325

2426
class IMipService {
@@ -121,31 +123,44 @@ public function process(): void {
121123
$imapMessage = current(array_filter($imapMessages, static fn (IMAPMessage $imapMessage) => $message->getUid() === $imapMessage->getUid()));
122124
if (empty($imapMessage->scheduling)) {
123125
// No scheduling info, maybe the DB is wrong
126+
$message->setImipProcessed(true);
124127
$message->setImipError(true);
125128
continue;
126129
}
127130

128131
$sender = $imapMessage->getFrom()->first()?->getEmail();
129132
if ($sender === null) {
133+
$message->setImipProcessed(true);
130134
$message->setImipError(true);
131135
continue;
132136
}
133137

134-
foreach ($imapMessage->scheduling as $schedulingInfo) { // an IMAP message could contain more than one iMIP object
135-
if ($schedulingInfo['method'] === 'REQUEST') {
136-
$processed = $this->calendarManager->handleIMipRequest($principalUri, $sender, $recipient, $schedulingInfo['contents']);
137-
$message->setImipProcessed($processed);
138-
$message->setImipError(!$processed);
139-
} elseif ($schedulingInfo['method'] === 'REPLY') {
140-
$processed = $this->calendarManager->handleIMipReply($principalUri, $sender, $recipient, $schedulingInfo['contents']);
141-
$message->setImipProcessed($processed);
142-
$message->setImipError(!$processed);
143-
} elseif ($schedulingInfo['method'] === 'CANCEL') {
144-
$replyTo = $imapMessage->getReplyTo()->first()?->getEmail();
145-
$processed = $this->calendarManager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $schedulingInfo['contents']);
146-
$message->setImipProcessed($processed);
147-
$message->setImipError(!$processed);
138+
try {
139+
// an IMAP message could contain more than one iMIP object
140+
foreach ($imapMessage->scheduling as $schedulingInfo) {
141+
if ($schedulingInfo['method'] === 'REQUEST') {
142+
$processed = $this->calendarManager->handleIMipRequest($principalUri, $sender, $recipient, $schedulingInfo['contents']);
143+
$message->setImipProcessed($processed);
144+
$message->setImipError(!$processed);
145+
} elseif ($schedulingInfo['method'] === 'REPLY') {
146+
$processed = $this->calendarManager->handleIMipReply($principalUri, $sender, $recipient, $schedulingInfo['contents']);
147+
$message->setImipProcessed($processed);
148+
$message->setImipError(!$processed);
149+
} elseif ($schedulingInfo['method'] === 'CANCEL') {
150+
$replyTo = $imapMessage->getReplyTo()->first()?->getEmail();
151+
$processed = $this->calendarManager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $schedulingInfo['contents']);
152+
$message->setImipProcessed($processed);
153+
$message->setImipError(!$processed);
154+
}
148155
}
156+
} catch (Throwable $e) {
157+
$this->logger->error('iMIP message processing failed', [
158+
'exception' => $e,
159+
'messageId' => $message->getId(),
160+
'mailboxId' => $mailbox->getId(),
161+
]);
162+
$message->setImipProcessed(true);
163+
$message->setImipError(true);
149164
}
150165
}
151166
$this->messageMapper->updateImipData(...$filteredMessages);

tests/Unit/Service/IMipServiceTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,69 @@ public function testIsCancel(): void {
413413

414414
$this->service->process();
415415
}
416+
417+
public function testHandleImipRequestThrowsException(): void {
418+
$message = new Message();
419+
$message->setImipMessage(true);
420+
$message->setUid(1);
421+
$message->setMailboxId(100);
422+
$mailbox = new Mailbox();
423+
$mailbox->setId(100);
424+
$mailbox->setAccountId(200);
425+
$mailAccount = new MailAccount();
426+
$mailAccount->setId(200);
427+
$mailAccount->setEmail('[email protected]');
428+
$mailAccount->setUserId('vincent');
429+
$account = new Account($mailAccount);
430+
$imapMessage = $this->createMock(IMAPMessage::class);
431+
$imapMessage->scheduling[] = ['method' => 'REQUEST', 'contents' => 'VCALENDAR'];
432+
$addressList = $this->createMock(AddressList::class);
433+
$address = $this->createMock(Address::class);
434+
435+
$this->messageMapper->expects(self::once())
436+
->method('findIMipMessagesAscending')
437+
->willReturn([$message]);
438+
$this->mailboxMapper->expects(self::once())
439+
->method('findById')
440+
->willReturn($mailbox);
441+
$this->accountService->expects(self::once())
442+
->method('findById')
443+
->willReturn($account);
444+
$this->mailManager->expects(self::once())
445+
->method('getImapMessagesForScheduleProcessing')
446+
->with($account, $mailbox, [$message->getUid()])
447+
->willReturn([$imapMessage]);
448+
$imapMessage->expects(self::once())
449+
->method('getUid')
450+
->willReturn(1);
451+
$imapMessage->expects(self::once())
452+
->method('getFrom')
453+
->willReturn($addressList);
454+
$addressList->expects(self::once())
455+
->method('first')
456+
->willReturn($address);
457+
$address->expects(self::once())
458+
->method('getEmail')
459+
->willReturn('[email protected]');
460+
$this->calendarManager->expects(self::once())
461+
->method('handleIMipRequest')
462+
->willThrowException(new \Exception('Calendar error'));
463+
$this->logger->expects(self::once())
464+
->method('error')
465+
->with(
466+
'iMIP message processing failed',
467+
self::callback(function ($context) use ($message, $mailbox) {
468+
return isset($context['exception'])
469+
&& $context['messageId'] === $message->getId()
470+
&& $context['mailboxId'] === $mailbox->getId();
471+
})
472+
);
473+
$this->messageMapper->expects(self::once())
474+
->method('updateImipData')
475+
->with(self::callback(function (Message $msg) {
476+
return $msg->isImipProcessed() === true && $msg->isImipError() === true;
477+
}));
478+
479+
$this->service->process();
480+
}
416481
}

0 commit comments

Comments
 (0)