Skip to content
Open
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
32 changes: 28 additions & 4 deletions lib/Service/MailTransmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Horde_Imap_Client_Data_Fetch;
use Horde_Imap_Client_Fetch_Query;
use Horde_Imap_Client_Ids;
use Horde_Mail_Exception;
use Horde_Mail_Rfc822_Address;
use Horde_Mail_Rfc822_List;
use Horde_Mail_Transport_Null;
Expand Down Expand Up @@ -51,6 +52,7 @@
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\EventDispatcher\IEventDispatcher;
use Psr\Log\LoggerInterface;
use Throwable;

class MailTransmission implements IMailTransmission {
private const RETRIABLE_CODES = [
Expand Down Expand Up @@ -155,7 +157,7 @@ public function sendMessage(Account $account, LocalMessage $localMessage): void
// Send the message
try {
$mail->send($transport, false, false);
$localMessage->setRaw($mail->getRaw(false));
$localMessage->setRaw($this->getRawAsString($mail));
$localMessage->setStatus(LocalMessage::STATUS_RAW);
} catch (Horde_Mime_Exception $e) {
if ($e->getPrevious() instanceof Horde_Smtp_Exception) {
Expand All @@ -172,13 +174,19 @@ public function sendMessage(Account $account, LocalMessage $localMessage): void
$localMessage->setStatus(LocalMessage::STATUS_SMPT_SEND_FAIL);
return;
}

try {
$localMessage->setRaw($this->getRawAsString($mail));
} catch (Throwable) {
// Having the raw message is nice for troubleshooting, but should not fail hard.
}
$localMessage->setStatus(LocalMessage::STATUS_ERROR);
Comment on lines +178 to 183
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior now attempts to persist the raw RFC822 message when Horde_Mime_Exception occurs (non-retriable path), but there doesn't appear to be unit/integration coverage asserting that LocalMessage::getRaw() is populated (and that failures in getRaw(false) are tolerated). Adding a focused test would prevent regressions in this troubleshooting path.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😕

return;
} finally {
if ($transport instanceof Horde_Mail_Transport_Smtphorde) {
try {
$transport->getSMTPObject()->logout();
} catch (\Throwable $e) {
} catch (Throwable) {
// Handle silently as this is a resource usage optimization
}
}
Expand Down Expand Up @@ -233,7 +241,7 @@ public function saveLocalDraft(Account $account, LocalMessage $message): void {
$this->messageMapper->save(
$client,
$draftsMailbox,
$mail->getRaw(false),
$this->getRawAsString($mail),
[Horde_Imap_Client::FLAG_DRAFT]
);
$perfLogger->step('save local draft message on IMAP');
Expand Down Expand Up @@ -319,7 +327,7 @@ public function saveDraft(NewMessageData $message, ?Message $previousDraft = nul
$newUid = $this->messageMapper->save(
$client,
$draftsMailbox,
$mail->getRaw(false),
$this->getRawAsString($mail),
[Horde_Imap_Client::FLAG_DRAFT]
);
$perfLogger->step('save message on IMAP');
Expand Down Expand Up @@ -437,4 +445,20 @@ public function sendMdn(Account $account, Mailbox $mailbox, Message $message): v
}
}

/**
* @throws Horde_Mail_Exception if no base part set
*/
public function getRawAsString(Horde_Mime_Mail $mail): string {
$raw = $mail->getRaw(false);
if (is_resource($raw)) {
// dead code to make psalm happy
// https://github.com/bytestream/Mime/pull/15
$raw = stream_get_contents($raw);
if ($raw === false) {
return '';
}
}
return $raw;
}

}
Loading