Repository navigation
feat: jmap support - part 2b - #12871
SebastianKrupinski wants to merge 16 commits into
Conversation
4655ec5 to
6d90ed1
Compare
6d90ed1 to
d0c63d4
Compare
d0c63d4 to
8854938
Compare
8854938 to
6939e95
Compare
9a63ade to
8c758e3
Compare
8a87336 to
bd44a04
Compare
|
@SebastianKrupinski I finished the first round of testing 😄 |
Thank you so much!!! Totally appreciate it!!! |
Fixed |
|
Hey @odzhychko You can retest the sending it has been fixed. I've also rebase to the most recent version of mail |
|
Ready with retesting the opened issues.
|
Okay, all these are fix/resolved, and changes pushed. I think that takes care of all the issues, you found |
There was a problem hiding this comment.
🟡 Changes recommended
JMAP can bypass requested S/MIME protection and contains failure paths that may duplicate sent messages.
8 open findings
JMAP submission ignores requested S/MIME signing and encryption · New Unknown JMAP failures can duplicate accepted submissions · New Flagging failure can cause duplicate email delivery · New Null iMIP attachment fields cause JMAP conversion failure · New IMAP send path omits the AI-generated header · New JMAP conversion drops read receipt and AI-generated metadata · New Failed remote deletion incorrectly emits deletion event · New Draft mailbox errors escape handling and cause endless retries · New
What changed in this PR
Adds protocol-neutral transmission support, enabling JMAP sending while moving existing IMAP transmission logic behind connectors.
Changes:
- Adds IMAP and JMAP transmission connectors.
- Refactors sending, drafts, attachments, and handlers around protocol abstractions.
- Updates unit and integration tests for the new architecture.
| File | Description |
|---|---|
| tests/Unit/Service/TransmissionServiceTest.php | Updates attachment-content tests. |
| tests/Unit/Service/OutboxServiceTest.php | Removes direct IMAP dependencies. |
| tests/Unit/Service/MailTransmissionTest.php | Removes obsolete service tests. |
| tests/Unit/Service/JMAP/JmapOperationsServiceTest.php | Tests JMAP save and upload operations. |
| tests/Unit/Service/DraftsServiceTest.php | Tests connector-based draft storage. |
| tests/Unit/Service/Attachment/AttachmentServiceTest.php | Updates protocol-neutral attachment tests. |
| tests/Unit/Service/AntiSpamServiceTest.php | Removes obsolete transmission mock. |
| tests/Unit/Send/SentMailboxHandlerTest.php | Removes obsolete handler tests. |
| tests/Unit/Send/SendHandlerTest.php | Tests connector-based sending. |
| tests/Unit/Send/FlagRepliedMessageHandlerTest.php | Updates reply-flagging tests. |
| tests/Unit/Send/CopySendMessageHandlerTest.php | Removes obsolete copy-handler tests. |
| tests/Unit/Send/ChainTest.php | Updates the simplified send chain. |
| tests/Unit/Listener/DeleteDraftListenerTest.php | Tests connector-based draft deletion. |
| tests/Unit/JMAP/JmapTransmissionConnectorTest.php | Tests JMAP attachment saving. |
| tests/Unit/IMAP/ImapTransmissionConnectorTest.php | Tests extracted IMAP transmission behavior. |
| tests/Unit/Controller/MessagesControllerTest.php | Updates transmission dependency. |
| tests/Unit/Controller/AccountsControllerTest.php | Removes legacy draft endpoint tests. |
| tests/Integration/Service/OutboxServiceIntegrationTest.php | Updates outbox construction. |
| tests/Integration/Service/MailTransmissionIntegrationTest.php | Uses the registered send chain. |
| tests/Integration/Service/DraftServiceIntegrationTest.php | Updates draft service construction. |
| lib/Service/TransmissionService.php | Exposes protocol-neutral attachment content. |
| lib/Service/OutboxService.php | Removes direct IMAP attachment handling. |
| lib/Service/MailTransmission.php | Removes legacy transmission service. |
| lib/Service/MailManager.php | Adds optional raw-message decryption. |
| lib/Service/JMAP/JmapOperationsService.php | Adds JMAP submission, saving, and uploads. |
| lib/Service/DraftsService.php | Saves drafts through transmission connectors. |
| lib/Service/Attachment/AttachmentService.php | Uses protocol-neutral message access. |
| lib/Send/SentMailboxHandler.php | Removes obsolete mailbox handler. |
| lib/Send/SendHandler.php | Dispatches sending through connectors. |
| lib/Send/FlagRepliedMessageHandler.php | Uses protocol-neutral flagging. |
| lib/Send/CopySentMessageHandler.php | Removes obsolete sent-copy handler. |
| lib/Send/Chain.php | Simplifies the sending chain. |
| lib/Send/AntiAbuseHandler.php | Removes IMAP-client coupling. |
| lib/Send/AHandler.php | Removes IMAP-client parameters. |
| lib/Protocol/ProtocolFactory.php | Registers transmission connectors. |
| lib/Listener/DeleteDraftListener.php | Deletes drafts through message connectors. |
| lib/JMAP/JmapTransmissionConnector.php | Implements JMAP draft and send operations. |
| lib/JMAP/JmapMessageConnector.php | Accepts the raw-message decrypt option. |
| lib/IMAP/ImapTransmissionConnector.php | Implements extracted IMAP transmission. |
| lib/IMAP/ImapMessageConnector.php | Supports optional raw-message decryption. |
| lib/Db/MailboxMapper.php | Adds account-scoped name lookup. |
| lib/Controller/MessagesController.php | Routes MDNs through connectors. |
| lib/Controller/AccountsController.php | Removes the legacy draft action. |
| lib/Contracts/IMessageConnector.php | Extends raw-fetch contract. |
| lib/Contracts/IMailTransmission.php | Removes the legacy transmission contract. |
| lib/AppInfo/Application.php | Removes legacy service registration. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| $rcptTo = $this->collectEnvelopeRecipients($to, $cc, $bcc); | ||
| $attachments = $this->collectAttachments($account, $message); | ||
| $jMessage = $this->convertLocalMessage($from, $to, $cc, $bcc, $message); |
| // Network / HTTP errors and other transient failures are retriable | ||
| return LocalMessage::STATUS_SMPT_SEND_FAIL; |
| $message->setFlagAnswered(true); | ||
| $this->dbMessageMapper->update($message); | ||
| } catch (DoesNotExistException|Horde_Imap_Client_Exception $e) { | ||
| } catch (DoesNotExistException $e) { |
| $part = $message->bodyPartStructure()->addPart(); | ||
| $part->blob($blobId) | ||
| ->type($attachment['type']) | ||
| ->name($attachment['name']) | ||
| ->disposition($attachment['disposition'] ?? 'attachment'); |
| if ($message->getSubject() !== null) { | ||
| $fccHeaders->addHeader('Subject', $message->getSubject()); | ||
| } |
| if (($inReplyTo = $lMessage->getInReplyToMessageId()) !== null) { | ||
| $jMessage->inReplyTo($inReplyTo); | ||
| $jMessage->references($inReplyTo); | ||
| } | ||
|
|
||
| $jMessage->subject($lMessage->getSubject() ?? ''); |
| // TODO: the UID could be from another mailbox | ||
| $this->protocolFactory->messageConnector($account)->deleteMessages($account, $draftsMailbox, $draft); | ||
|
|
||
| $this->eventDispatcher->dispatchTyped( | ||
| new MessageDeletedEvent($account, $draftsMailbox, $draft->getUid()) |
| public function sendMessage(LocalMessage $message, Account $account): void { | ||
| $draftsMailbox = $this->findOrCreateDraftsMailbox($account); | ||
| try { | ||
| $this->transmission->saveLocalDraft($account, $message); | ||
| $this->protocolFactory->transmissionConnector($account)->saveMessage($account, $draftsMailbox, $message, ['$draft']); | ||
| } catch (ClientException|ServiceException $e) { |
|
Note This review was generated with Claude Code (claude-opus-5-5) and checked against the code at ee4156a. It includes the open Copilot findings. Line numbers refer to that commit. OverviewDuplicate send / endless retry
Lost behaviour
Cleanup
Details1. Reply flagging failure resends the mail ( 2. Wrong flag name ( 3. Resend after a failed Sent copy ( 4. JMAP error classification ( 5. Drafts mailbox errors outside the try ( 6. JMAP ignores security options ( 7. 8. Bcc dropped from drafts ( 9. iMIP via JMAP ( 10. 11. 12. Dead code 13. 14. 15. 16. |
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
|
Hi @kesselb I have addressed the issues you posted. 2 are left out for now, 6 will be addressed in a follow up, and 15 is already addressed i the followup in the stack |


Summary
Testing
This PR coverts message sending ONLY
Basically every operation that interacts with a external Imap or Jmap server needs to be tested... Twice... once on a Imap account/server then on a Jmap account/server
Message Operations