-
-
Notifications
You must be signed in to change notification settings - Fork 19
feat(imap): mailbox commands #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
toddheasley
merged 11 commits into
thunderbird:main
from
toddheasley:feature-imap-folders
Feb 9, 2026
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
34eb05c
feat(imap): add list command; rename folder to mailbox
toddheasley 1741f95
feat(imap): add namespace support, mailbox commands
toddheasley 6114763
Merge remote-tracking branch 'upstream/main' into feature-imap-folders
toddheasley a5c080b
Merge remote-tracking branch 'upstream/main' into feature-imap-folders
toddheasley 98e2c83
feat(imap): refactor IMAP/SMTP client tests
toddheasley 893d856
feat(imap): implement remaining mailbox commands
toddheasley 8341195
feat(imap): use UUIDs as IMAP command tags
toddheasley 285bdbd
feat(imap): fix error description wording
toddheasley c0b446d
Merge remote-tracking branch 'upstream/main' into feature-imap-folders
toddheasley efeb4d7
Merge remote-tracking branch 'upstream/main' into feature-imap-folders
toddheasley 87dabc1
Merge remote-tracking branch 'upstream/main' into feature-imap-folders
toddheasley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import NIOCore | ||
| import NIOIMAP | ||
|
|
||
| // Create new mailbox | ||
| struct CreateCommand: IMAPCommand { | ||
| let mailboxName: MailboxName | ||
| let parameters: [CreateParameter] | ||
|
|
||
| init(_ mailboxName: MailboxName, parameters: [CreateParameter] = []) { | ||
| self.mailboxName = mailboxName | ||
| self.parameters = parameters | ||
| } | ||
|
|
||
| // MARK: IMAPCommand | ||
| typealias Result = Void | ||
| typealias Handler = VoidResultHandler | ||
|
|
||
| var name: String { "create \"\(mailboxName)\"" } | ||
|
|
||
| func tagged(_ tag: String) -> NIOIMAPCore.TaggedCommand { | ||
| TaggedCommand(tag: tag, command: .create(mailboxName, parameters)) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import NIOCore | ||
| import NIOIMAP | ||
|
|
||
| // Delete an existing mailbox | ||
| struct DeleteCommand: IMAPCommand { | ||
| let mailboxName: MailboxName | ||
|
|
||
| init(_ mailboxName: MailboxName) { | ||
| self.mailboxName = mailboxName | ||
| } | ||
|
|
||
| // MARK: IMAPCommand | ||
| typealias Result = Void | ||
| typealias Handler = VoidResultHandler | ||
|
|
||
| var name: String { "delete \"\(mailboxName)\"" } | ||
|
|
||
| func tagged(_ tag: String) -> NIOIMAPCore.TaggedCommand { | ||
| TaggedCommand(tag: tag, command: .delete(mailboxName)) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import NIOIMAP | ||
|
|
||
| // Select current working mailbox in read-only mode | ||
| struct ExamineCommand: IMAPCommand { | ||
| let mailboxName: MailboxName | ||
|
|
||
| init(_ mailboxName: MailboxName) { | ||
| self.mailboxName = mailboxName | ||
| } | ||
|
|
||
| // MARK: IMAPCommand | ||
| typealias Result = MailboxStatus | ||
| typealias Handler = ExamineHandler | ||
|
|
||
| var name: String { "examine \"\(mailboxName)\"" } | ||
|
|
||
| func tagged(_ tag: String) -> NIOIMAPCore.TaggedCommand { | ||
| TaggedCommand(tag: tag, command: .examine(mailboxName)) | ||
| } | ||
| } | ||
|
|
||
| typealias ExamineHandler = SelectHandler |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import NIOCore | ||
| import NIOIMAP | ||
|
|
||
| // List all mailboxes available for authenticated user | ||
| struct ListCommand: IMAPCommand { | ||
| let options: [ReturnOption] | ||
| let wildcard: Character | ||
|
|
||
| init(options: [ReturnOption] = [], wildcard: Character) { | ||
| self.options = options | ||
| self.wildcard = wildcard | ||
| } | ||
|
|
||
| // MARK: IMAPCommand | ||
| typealias Result = [MailboxInfo] | ||
| typealias Handler = ListHandler | ||
|
|
||
| var name: String { "list" } | ||
|
|
||
| func tagged(_ tag: String) -> NIOIMAPCore.TaggedCommand { | ||
| TaggedCommand(tag: tag, command: .list(nil, reference: .reference, .mailbox(wildcard), options)) | ||
| } | ||
| } | ||
|
|
||
| class ListHandler: IMAPCommandHandler, @unchecked Sendable { | ||
|
|
||
| // MARK: IMAPCommandHandler | ||
| typealias InboundIn = Response | ||
| typealias InboundOut = Response | ||
| typealias Result = [MailboxInfo] | ||
|
|
||
| var mailboxes: Result = [] | ||
| var clientBug: String? = nil | ||
| let promise: EventLoopPromise<Result> | ||
| let tag: String | ||
|
|
||
| required init(tag: String, promise: EventLoopPromise<Result>) { | ||
| self.promise = promise | ||
| self.tag = tag | ||
| } | ||
|
|
||
| func channelRead(context: ChannelHandlerContext, data: NIOAny) { | ||
| let response: Response = unwrapInboundIn(data) | ||
| clientBug = response.clientBug | ||
| switch response { | ||
| case .tagged(let taggedResponse): | ||
| switch taggedResponse.state { | ||
| case .bad(let text), .no(let text): | ||
| promise.fail(IMAPError.commandFailed(text.text)) | ||
| case .ok: | ||
| promise.succeed(mailboxes) | ||
| } | ||
| case .untagged(let payload): | ||
| switch payload { | ||
| case .mailboxData(.list(let mailbox)): | ||
| mailboxes.append(mailbox) | ||
| default: | ||
| break | ||
| } | ||
| default: | ||
| break | ||
| } | ||
| context.fireChannelRead(data) | ||
| } | ||
| } | ||
|
|
||
| extension MailboxName { | ||
| static var reference: Self { Self(ByteBuffer(string: "")) } | ||
| } | ||
|
|
||
| extension MailboxPatterns { | ||
| static func mailbox(_ wildcard: Character = .wildcard) -> Self { | ||
| .mailbox(ByteBuffer(string: "\(wildcard)")) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing against live IMAP servers (AOL, Gmail, Fastmail, iCloud, Outlook/Hotmail), there's no advantage to traditional, custom tags (a001-a999) over shortened UUIDs, which are less work/code that we own, and way less likely to collide.