Skip to content

feat(imap): support the NAMESPACE extension (RFC 2342) - #295

Merged
robert-virkus merged 1 commit into
Enough-Software:mainfrom
xx25:namespace-main
Sep 29, 2026
Merged

robert-virkus merged 1 commit into
Enough-Software:mainfrom
xx25:namespace-main

Conversation

@xx25

@xx25 xx25 commented Sep 29, 2026

Copy link
Copy Markdown

Summary

Adds the IMAP NAMESPACE extension (RFC 2342):

  • ImapClient.namespace() sends NAMESPACE and returns a NamespaceResponse with the personal, other users' and shared namespaces. Each Namespace carries the prefix as sent (encodedPrefix) and decoded from modified UTF-7 (prefix, the same codec Mailbox.path uses), the hierarchy delimiter (null for NIL) and any response extensions.
  • ImapServerInfo.supportsNamespace / ImapServerInfo.capabilityNamespace.
  • README extension list and CHANGELOG entry.

The parser follows the RFC 2342 §6 grammar strictly: prefixes and extension values are IMAP strings (quoted or literal — a literal arrives as its own raw response line and stays one token), the only bare atom is NIL, and a response that does not match the grammar is not consumed, so namespace() returns null instead of a guess. NO/BAD surfaces as an ImapException like every other command.

Motivation: a FidoNet-over-IMAP gateway I write a client for now advertises its conference tree as the shared namespace (* NAMESPACE (("" "/")) NIL (("Echoes/" "/"))), and a client should discover that prefix instead of hardcoding it — the reason the RFC exists.

Tests

  • test/src/imap/namespace_parser_test.dart: the RFC's examples 5.1–5.9 verbatim, a NIL delimiter, quoted specials, a modified-UTF-7 prefix, a literal prefix, malformed responses, an unrelated untagged line, toString.
  • test/imap/imap_client_test.dart: mock-server round trips for the command, a literal prefix, an OK without a NAMESPACE line (→ null), and a BAD (→ ImapException).

dart analyze adds no new issues; dart format clean; dart test passes (463 tests).

Adds `ImapClient.namespace()`, which sends NAMESPACE and returns a
`NamespaceResponse` with the personal, other users' and shared
namespaces — each a `Namespace` carrying the prefix (as sent, and
decoded from modified UTF-7 like `Mailbox.path`), the hierarchy
delimiter (`null` for NIL) and any response extensions — plus
`ImapServerInfo.supportsNamespace` / `capabilityNamespace`.

The parser follows the RFC 2342 section 6 grammar strictly: prefixes
and extension values are IMAP strings (quoted or literal; a literal
arrives as its own raw response line and stays one token), the only
bare atom is NIL, and a response that does not match the grammar is
not consumed, so `namespace()` returns `null` rather than a guess.
NO/BAD surfaces as an `ImapException` like every other command.

Tests: the RFC's examples 5.1–5.9 verbatim, a NIL delimiter, quoted
specials, a modified-UTF-7 prefix, a literal prefix, malformed
responses; mock-server round trips for the command, a literal, an
OK without a NAMESPACE line, and a BAD.

(cherry picked from commit 9a6a7cd)
@robert-virkus

Copy link
Copy Markdown
Member

Thanks a bunch, looks great!

@robert-virkus
robert-virkus merged commit fe51166 into Enough-Software:main Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants