From 37806d923df4228c7bc5b5985110509f4beb7ee1 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Tue, 30 Jun 2026 15:42:08 +0200 Subject: [PATCH 1/2] fix(drafts): preserve Bcc header when saving drafts Horde_Mime_Mail::getRaw() omits the Bcc header (same root cause as the sent mailbox fix). Reopening a saved draft therefore lost all Bcc recipients silently. Add buildMimeHeaders() returning Horde_Mime_Headers directly, and use $mail->getBasePart()->toString() with those headers instead of $mail->getRaw() in saveLocalDraft() and saveDraft(). Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> --- lib/Service/MailTransmission.php | 57 ++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/lib/Service/MailTransmission.php b/lib/Service/MailTransmission.php index e58a36d930..da1d1dc4a5 100644 --- a/lib/Service/MailTransmission.php +++ b/lib/Service/MailTransmission.php @@ -235,36 +235,30 @@ public function saveLocalDraft(Account $account, LocalMessage $message): void { $this->transmissionService->handleAttachment($account, $attachment); } - // build mime body - $headers = $this->buildHeaders( - $from, - $to, - $cc, - $bcc, - $message->getSubject() - ); + $draftHeaders = $this->buildMimeHeaders($from, $to, $cc, $bcc, $message->getSubject()); $mail = new Horde_Mime_Mail(); - $mail->addHeaders($headers); if ($message->isHtml()) { $mail->setHtmlBody($message->getBodyHtml()); } else { $mail->setBody($message->getBodyPlain()); } - $mail->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $perfLogger->step('build local draft message'); - // 'Send' the message + // Use a null transport to trigger MIME body encoding without sending $client = $this->imapClientFactory->getClient($account); try { - $transport = new Horde_Mail_Transport_Null(); - $mail->send($transport, false, false); + $mail->send(new Horde_Mail_Transport_Null(), false, false); $perfLogger->step('create IMAP draft message'); $draftsMailbox = $this->findOrCreateDraftsMailbox($account); $this->messageMapper->save( $client, $draftsMailbox, - $mail->getRaw(false), + $mail->getBasePart()->toString([ + 'encode' => Horde_Mime_Part::ENCODE_7BIT | Horde_Mime_Part::ENCODE_8BIT | Horde_Mime_Part::ENCODE_BINARY, + 'headers' => $draftHeaders, + 'stream' => false, + ]), [Horde_Imap_Client::FLAG_DRAFT] ); $perfLogger->step('save local draft message on IMAP'); @@ -317,7 +311,7 @@ public function saveDraft(NewMessageData $message, ?Message $previousDraft = nul $account = $message->getAccount(); $from = Address::fromRaw($account->getName(), $account->getEMailAddress()); - $headers = $this->buildHeaders( + $draftHeaders = $this->buildMimeHeaders( $from, $message->getTo(), $message->getCc(), @@ -326,22 +320,19 @@ public function saveDraft(NewMessageData $message, ?Message $previousDraft = nul ); $mail = new Horde_Mime_Mail(); - $mail->addHeaders($headers); if ($message->isHtml()) { $mail->setHtmlBody($message->getBody()); } else { $mail->setBody($message->getBody()); } - $mail->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $perfLogger->step('build draft message'); - // 'Send' the message + // Use a null transport to trigger MIME body encoding without sending $client = $this->imapClientFactory->getClient($account); try { - $transport = new Horde_Mail_Transport_Null(); - $mail->send($transport, false, false); + $mail->send(new Horde_Mail_Transport_Null(), false, false); $perfLogger->step('create IMAP message'); - // save the message in the drafts folder + // save the message in the drafts mailbox $draftsMailboxId = $account->getMailAccount()->getDraftsMailboxId(); if ($draftsMailboxId === null) { throw new ClientException('No drafts mailbox configured'); @@ -350,7 +341,11 @@ public function saveDraft(NewMessageData $message, ?Message $previousDraft = nul $newUid = $this->messageMapper->save( $client, $draftsMailbox, - $mail->getRaw(false), + $mail->getBasePart()->toString([ + 'encode' => Horde_Mime_Part::ENCODE_7BIT | Horde_Mime_Part::ENCODE_8BIT | Horde_Mime_Part::ENCODE_BINARY, + 'headers' => $draftHeaders, + 'stream' => false, + ]), [Horde_Imap_Client::FLAG_DRAFT] ); $perfLogger->step('save message on IMAP'); @@ -372,6 +367,24 @@ public function saveDraft(NewMessageData $message, ?Message $previousDraft = nul return [$account, $draftsMailbox, $newUid]; } + private function buildMimeHeaders(Address $from, AddressList $to, AddressList $cc, AddressList $bcc, ?string $subject): Horde_Mime_Headers { + $headers = new Horde_Mime_Headers(); + $headers->addHeaderOb(Horde_Mime_Headers_Date::create()); + $headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); + $headers->addHeaderOb(new Horde_Mime_Headers_Addresses('From', $from->toHorde())); + $headers->addHeaderOb(new Horde_Mime_Headers_Addresses('To', $to->toHorde())); + if (count($cc) > 0) { + $headers->addHeaderOb(new Horde_Mime_Headers_Addresses('Cc', $cc->toHorde())); + } + if (count($bcc) > 0) { + $headers->addHeaderOb(new Horde_Mime_Headers_Addresses('Bcc', $bcc->toHorde())); + } + if ($subject !== null) { + $headers->addHeader('Subject', $subject); + } + return $headers; + } + /** * @return array{ * From: Horde_Mail_Rfc822_Address, From 888ef9460ac9741a6fe4a48b83ae44953bf81366 Mon Sep 17 00:00:00 2001 From: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:04:50 +0200 Subject: [PATCH 2/2] fixup! fix(drafts): preserve Bcc header when saving drafts --- lib/Service/MailTransmission.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/lib/Service/MailTransmission.php b/lib/Service/MailTransmission.php index da1d1dc4a5..493e4d0c11 100644 --- a/lib/Service/MailTransmission.php +++ b/lib/Service/MailTransmission.php @@ -14,7 +14,6 @@ use Horde_Imap_Client_Data_Fetch; use Horde_Imap_Client_Fetch_Query; use Horde_Imap_Client_Ids; -use Horde_Mail_Rfc822_Address; use Horde_Mail_Rfc822_List; use Horde_Mail_Transport_Null; use Horde_Mail_Transport_Smtphorde; @@ -385,32 +384,6 @@ private function buildMimeHeaders(Address $from, AddressList $to, AddressList $c return $headers; } - /** - * @return array{ - * From: Horde_Mail_Rfc822_Address, - * To: Horde_Mail_Rfc822_List, - * Subject: string|null, - * Cc?: Horde_Mail_Rfc822_List, - * Bcc?: Horde_Mail_Rfc822_List, - * } - */ - private function buildHeaders(Address $from, AddressList $to, AddressList $cc, AddressList $bcc, ?string $subject): array { - $headers = [ - 'From' => $from->toHorde(), - 'To' => $to->toHorde(), - 'Subject' => $subject, - ]; - - if (count($cc) > 0) { - $headers['Cc'] = $cc->toHorde(); - } - if (count($bcc) > 0) { - $headers['Bcc'] = $bcc->toHorde(); - } - - return $headers; - } - #[\Override] public function sendMdn(Account $account, Mailbox $mailbox, Message $message): void { $query = new Horde_Imap_Client_Fetch_Query();