commits
Sync opam package metadata including x-maintenance-intent
and external dependency specifications.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace unsafe Obj.magic casts with proper type-safe alternatives:
- conpool: Make protocol parameter required, add create_basic for simple
pools. The previous optional protocol with Obj.magic default was
fundamentally unsound as OCaml cannot have optional parameters that
change return types.
- publicsuffix: Add explicit id field to trie_node instead of using
Obj.magic to cast nodes to int for hashtable keys.
- yamlt: Add init_unknown_builder helper that properly handles GADT
refinement, returning () for Unknown_skip/Unknown_error cases where
builder=unit.
- jmap_brr: Use Jsont_brr.encode/decode Jsont.json instead of unsafe
casts between Jv.t and Jsont.json.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add explicit unix library dependency to dune files (html5rw, tomlt)
- Fix odoc heading levels ({0 -> {1) in imap subject.mli and thread.mli
- Fix code block indentation in subject.mli and h2_stream.mli
- Change unresolved module references to plain text (Bytesrw_unix,
Bytesrw_eio, Webfinger.Jrd.t)
- Fix @raise tags to use Error instead of Error.t
- Escape @mention/@mentions text in poe docs to avoid unknown tag warnings
- Remove unreachable flag_perm rule and redundant list_mailbox production
from IMAP grammar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These executables are internal tests and examples that should not be
installed as public binaries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert Flag_color to polymorphic variants (`Red, `Orange, etc.)
- Remove Imap_wire module from mail-flag, merge into ocaml-imap Flag
- Flag.system now uses polymorphic variants
- Flag.Keyword now wraps Mail_flag.Keyword.t directly
- Add flags_of_keywords/keywords_of_flags batch conversions
- Remove Jmap_wire module from mail-flag, merge into ocaml-jmap
- Add role_of_special_use/special_use_of_role to Mail_mailbox
- Add keywords_to_assoc/keywords_of_assoc to Mail_email
- Create toplevel mail_flag.mli with module aliases and full API docs
- Update all IMAP library code and tests for polymorphic variant types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New mail-flag library providing shared types for email protocols:
Core modules:
- keyword.ml: Unified message keywords (RFC 8621, draft-ietf-mailmaint)
- Standard: Seen, Answered, Flagged, Draft, Deleted, Forwarded
- Spam: Phishing, Junk, NotJunk
- Extended: HasAttachment, Muted, Followed, Notify, etc.
- Apple Mail flag color bits
- mailbox_attr.ml: Mailbox attributes and roles (RFC 6154, RFC 5258)
- LIST attributes: Noinferiors, Noselect, HasChildren, etc.
- Special-use roles: Inbox, Drafts, Sent, Trash, Archive, etc.
- Extended: Snoozed, Scheduled, Memos
- flag_color.ml: Apple Mail 7-color encoding via 3-bit keywords
Wire format adapters:
- imap_wire.ml: IMAP protocol serialization (\Seen, $forwarded)
- jmap_wire.ml: JMAP JSON format ({"$seen": true})
Integration:
- ocaml-imap: Flag and List_attr modules now interop with mail-flag
- ocaml-jmap: Keyword and Role modules now use mail-flag types
54 tests for mail-flag library, all existing tests pass.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
P0 Critical Fixes:
- Fix SEARCH response parsing in client library
- Add BODY/BODYSTRUCTURE recursive parsing for MIME structures
- Parse BODY[section] literals with section specifiers
P1 Core Protocol Compliance:
- ESEARCH (RFC 4731): search_return_opt types, MIN/MAX/COUNT/ALL
- Parse APPENDUID/COPYUID response codes with UID set parsing
- Add UNSELECT capability (RFC 3691)
- Add SPECIAL-USE capability and mailbox mapping (RFC 6154)
P2 Extension Support:
- THREAD (RFC 5256): algorithm types, response parsing, server handlers
- Base subject extraction per RFC 5256 Section 2.1
- QUOTA (RFC 9208): resource types, GETQUOTA/GETQUOTAROOT/SETQUOTA
- LIST-EXTENDED (RFC 5258): selection/return options, CHILDINFO
P3 Advanced Features:
- UTF-8 support (RFC 6855): validation, UTF8=ACCEPT capability
- CONDSTORE (RFC 7162): MODSEQ, Highestmodseq/Nomodseq/Modified codes
New modules: thread.ml, subject.ml, utf8.ml
New tests: 187 tests passing (was ~150)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fetch 8 RFCs for IMAP extensions:
- RFC 3691 (UNSELECT)
- RFC 4731 (ESEARCH)
- RFC 5256 (SORT/THREAD)
- RFC 5258 (LIST Extensions)
- RFC 5530 (Response Codes)
- RFC 6154 (Special-Use Mailboxes)
- RFC 6855 (UTF-8 Support)
- RFC 9208 (QUOTA)
Also fetch draft-ietf-mailmaint-messageflag-mailboxattribute-13
for unified IMAP/JMAP keyword and mailbox attribute handling.
Each RFC has a corresponding PLAN-*.md with implementation
analysis comparing spec to current codebase, prioritized tasks,
and OCamldoc citation templates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a comprehensive integration test suite for ocaml-imap inspired by
Dovecot's imaptest tool. The suite tests against a real IMAP server with
both scripted deterministic tests and randomized stress tests.
Test suite structure:
- imaptest_config.ml: CLI configuration via Cmdliner
- imaptest_output.ml: Colored terminal output
- imaptest_state.ml: State tracking for violation detection
- imaptest_utils.ml: Test helpers and RFC 5322 sample messages
- imaptest_scripted.ml: 36 deterministic tests across categories
- imaptest_stress.ml: Randomized stress tests with state tracking
- imaptest.ml: Combined runner with subcommands
Bug fixes discovered during testing:
1. APPEND literal synchronization (write.ml)
- Changed from synchronizing literal {size} to non-synchronizing
literal {size+} (LITERAL+ extension, RFC 7888)
- Without this, APPEND hangs waiting for continuation response
that was already sent
2. SEARCH response not implemented (response.ml, read.ml, client.ml)
- Added Response.Search type for traditional SEARCH responses
- Added parser for "* SEARCH n1 n2 n3..." format
- Fixed search/uid_search functions which were stubs returning []
References:
- RFC 9051: IMAP4rev2 protocol specification
- RFC 7888: LITERAL+ extension for non-synchronizing literals
- RFC 5322: Internet Message Format (test message format)
- Dovecot imaptest: https://dovecot.github.io/imaptest/
Test results: 30 passed, 2 failed (BODY/BODYSTRUCTURE parsing bugs
in core library), 4 skipped (server capability dependent)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SORT and CONDSTORE extensions to ocaml-imap
Implement RFC 5256 SORT and RFC 7162 CONDSTORE extensions for the
IMAP client library.
- Add Sort module with sort keys (Arrival, Cc, Date, From, Size,
Subject, To) and criterion type with reverse flag
- Add Sort and Uid_sort commands to Command module
- Add sort() and uid_sort() client functions requiring SORT capability
- Add SORT response parsing in read.ml
- Add Sort response type to Response module
- Add changedsince parameter to Fetch and Uid_fetch commands
- Add unchangedsince parameter to Store and Uid_store commands
- Add MODSEQ fetch item parsing
- Add modseq field to message_info and Fetch.message types
- Update fetch/uid_fetch/store/uid_store client functions
- Add test_sort_by_date testing SORT with ARRIVAL key and REVERSE
- Add test_condstore testing STORE with UNCHANGEDSINCE modifier
- Expand test suite from 65 to 98 tests covering FETCH variants,
SEARCH criteria, envelope parsing, and multi-message operations
- Fix keyword flag parsing to avoid double $ prefix
- Fix capability cache population in test setup
- Update API call sites for new optional parameters
- RFC 5256: Internet Message Access Protocol - SORT and THREAD Extensions
https://datatracker.ietf.org/doc/html/rfc5256
- RFC 7162: IMAP Extensions: Quick Flag Changes Resynchronization
(CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC)
https://datatracker.ietf.org/doc/html/rfc7162
- RFC 9051: Internet Message Access Protocol (IMAP) - Version 4rev2
https://datatracker.ietf.org/doc/html/rfc9051
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The parser had a fallback that returned Response.Exists 0 for any
unrecognized `* <number> <keyword>` response. This caused `* 0 RECENT`
to overwrite the real EXISTS count.
Changes:
- Add Response.Recent to handle `* <n> RECENT` responses
- Add Response.Unknown for unrecognized responses instead of returning
a fake Exists 0
- Update client to populate mailbox_info.recent field
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
IMAP UIDs and UIDVALIDITY are unsigned 32-bit integers (RFC 9051),
which can exceed OCaml's signed int32 max value (2147483647).
Changed to int64 throughout to properly handle values up to 4294967295.
Fixes Int32.of_string failure when UIDVALIDITY value exceeds 2^31-1.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix "cannot write to closed writer" by storing TLS flow instead of
Buf_write.t, creating fresh writer for each command
- Add Logs library integration with imap.client source for debugging
- Add --plain flag to use AUTHENTICATE PLAIN instead of LOGIN
- Add Command.pp for safe logging (passwords redacted)
- Fix parsing of \* wildcard flag in PERMANENTFLAGS response
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reorganize from 11 subdirectories to 2:
- lib/imap/ - standalone IMAP client library
- lib/imapd/ - consolidated server library
Modules renamed for clarity:
- Types -> Protocol (IMAP protocol definitions)
- imap_auth -> Auth
- imap_parser -> Parser
- imap_server -> Server
- imap_storage -> Storage
- imap_read -> Read
- imap_write -> Write
- imap_client -> Client
- imap_client_error -> Client_error
- imap_client_pool -> Client_pool
Access via Imapd.Protocol, Imapd.Server, etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace unsafe Obj.magic casts with proper type-safe alternatives:
- conpool: Make protocol parameter required, add create_basic for simple
pools. The previous optional protocol with Obj.magic default was
fundamentally unsound as OCaml cannot have optional parameters that
change return types.
- publicsuffix: Add explicit id field to trie_node instead of using
Obj.magic to cast nodes to int for hashtable keys.
- yamlt: Add init_unknown_builder helper that properly handles GADT
refinement, returning () for Unknown_skip/Unknown_error cases where
builder=unit.
- jmap_brr: Use Jsont_brr.encode/decode Jsont.json instead of unsafe
casts between Jv.t and Jsont.json.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add explicit unix library dependency to dune files (html5rw, tomlt)
- Fix odoc heading levels ({0 -> {1) in imap subject.mli and thread.mli
- Fix code block indentation in subject.mli and h2_stream.mli
- Change unresolved module references to plain text (Bytesrw_unix,
Bytesrw_eio, Webfinger.Jrd.t)
- Fix @raise tags to use Error instead of Error.t
- Escape @mention/@mentions text in poe docs to avoid unknown tag warnings
- Remove unreachable flag_perm rule and redundant list_mailbox production
from IMAP grammar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert Flag_color to polymorphic variants (`Red, `Orange, etc.)
- Remove Imap_wire module from mail-flag, merge into ocaml-imap Flag
- Flag.system now uses polymorphic variants
- Flag.Keyword now wraps Mail_flag.Keyword.t directly
- Add flags_of_keywords/keywords_of_flags batch conversions
- Remove Jmap_wire module from mail-flag, merge into ocaml-jmap
- Add role_of_special_use/special_use_of_role to Mail_mailbox
- Add keywords_to_assoc/keywords_of_assoc to Mail_email
- Create toplevel mail_flag.mli with module aliases and full API docs
- Update all IMAP library code and tests for polymorphic variant types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New mail-flag library providing shared types for email protocols:
Core modules:
- keyword.ml: Unified message keywords (RFC 8621, draft-ietf-mailmaint)
- Standard: Seen, Answered, Flagged, Draft, Deleted, Forwarded
- Spam: Phishing, Junk, NotJunk
- Extended: HasAttachment, Muted, Followed, Notify, etc.
- Apple Mail flag color bits
- mailbox_attr.ml: Mailbox attributes and roles (RFC 6154, RFC 5258)
- LIST attributes: Noinferiors, Noselect, HasChildren, etc.
- Special-use roles: Inbox, Drafts, Sent, Trash, Archive, etc.
- Extended: Snoozed, Scheduled, Memos
- flag_color.ml: Apple Mail 7-color encoding via 3-bit keywords
Wire format adapters:
- imap_wire.ml: IMAP protocol serialization (\Seen, $forwarded)
- jmap_wire.ml: JMAP JSON format ({"$seen": true})
Integration:
- ocaml-imap: Flag and List_attr modules now interop with mail-flag
- ocaml-jmap: Keyword and Role modules now use mail-flag types
54 tests for mail-flag library, all existing tests pass.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
P0 Critical Fixes:
- Fix SEARCH response parsing in client library
- Add BODY/BODYSTRUCTURE recursive parsing for MIME structures
- Parse BODY[section] literals with section specifiers
P1 Core Protocol Compliance:
- ESEARCH (RFC 4731): search_return_opt types, MIN/MAX/COUNT/ALL
- Parse APPENDUID/COPYUID response codes with UID set parsing
- Add UNSELECT capability (RFC 3691)
- Add SPECIAL-USE capability and mailbox mapping (RFC 6154)
P2 Extension Support:
- THREAD (RFC 5256): algorithm types, response parsing, server handlers
- Base subject extraction per RFC 5256 Section 2.1
- QUOTA (RFC 9208): resource types, GETQUOTA/GETQUOTAROOT/SETQUOTA
- LIST-EXTENDED (RFC 5258): selection/return options, CHILDINFO
P3 Advanced Features:
- UTF-8 support (RFC 6855): validation, UTF8=ACCEPT capability
- CONDSTORE (RFC 7162): MODSEQ, Highestmodseq/Nomodseq/Modified codes
New modules: thread.ml, subject.ml, utf8.ml
New tests: 187 tests passing (was ~150)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fetch 8 RFCs for IMAP extensions:
- RFC 3691 (UNSELECT)
- RFC 4731 (ESEARCH)
- RFC 5256 (SORT/THREAD)
- RFC 5258 (LIST Extensions)
- RFC 5530 (Response Codes)
- RFC 6154 (Special-Use Mailboxes)
- RFC 6855 (UTF-8 Support)
- RFC 9208 (QUOTA)
Also fetch draft-ietf-mailmaint-messageflag-mailboxattribute-13
for unified IMAP/JMAP keyword and mailbox attribute handling.
Each RFC has a corresponding PLAN-*.md with implementation
analysis comparing spec to current codebase, prioritized tasks,
and OCamldoc citation templates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a comprehensive integration test suite for ocaml-imap inspired by
Dovecot's imaptest tool. The suite tests against a real IMAP server with
both scripted deterministic tests and randomized stress tests.
Test suite structure:
- imaptest_config.ml: CLI configuration via Cmdliner
- imaptest_output.ml: Colored terminal output
- imaptest_state.ml: State tracking for violation detection
- imaptest_utils.ml: Test helpers and RFC 5322 sample messages
- imaptest_scripted.ml: 36 deterministic tests across categories
- imaptest_stress.ml: Randomized stress tests with state tracking
- imaptest.ml: Combined runner with subcommands
Bug fixes discovered during testing:
1. APPEND literal synchronization (write.ml)
- Changed from synchronizing literal {size} to non-synchronizing
literal {size+} (LITERAL+ extension, RFC 7888)
- Without this, APPEND hangs waiting for continuation response
that was already sent
2. SEARCH response not implemented (response.ml, read.ml, client.ml)
- Added Response.Search type for traditional SEARCH responses
- Added parser for "* SEARCH n1 n2 n3..." format
- Fixed search/uid_search functions which were stubs returning []
References:
- RFC 9051: IMAP4rev2 protocol specification
- RFC 7888: LITERAL+ extension for non-synchronizing literals
- RFC 5322: Internet Message Format (test message format)
- Dovecot imaptest: https://dovecot.github.io/imaptest/
Test results: 30 passed, 2 failed (BODY/BODYSTRUCTURE parsing bugs
in core library), 4 skipped (server capability dependent)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SORT and CONDSTORE extensions to ocaml-imap
Implement RFC 5256 SORT and RFC 7162 CONDSTORE extensions for the
IMAP client library.
- Add Sort module with sort keys (Arrival, Cc, Date, From, Size,
Subject, To) and criterion type with reverse flag
- Add Sort and Uid_sort commands to Command module
- Add sort() and uid_sort() client functions requiring SORT capability
- Add SORT response parsing in read.ml
- Add Sort response type to Response module
- Add changedsince parameter to Fetch and Uid_fetch commands
- Add unchangedsince parameter to Store and Uid_store commands
- Add MODSEQ fetch item parsing
- Add modseq field to message_info and Fetch.message types
- Update fetch/uid_fetch/store/uid_store client functions
- Add test_sort_by_date testing SORT with ARRIVAL key and REVERSE
- Add test_condstore testing STORE with UNCHANGEDSINCE modifier
- Expand test suite from 65 to 98 tests covering FETCH variants,
SEARCH criteria, envelope parsing, and multi-message operations
- Fix keyword flag parsing to avoid double $ prefix
- Fix capability cache population in test setup
- Update API call sites for new optional parameters
- RFC 5256: Internet Message Access Protocol - SORT and THREAD Extensions
https://datatracker.ietf.org/doc/html/rfc5256
- RFC 7162: IMAP Extensions: Quick Flag Changes Resynchronization
(CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC)
https://datatracker.ietf.org/doc/html/rfc7162
- RFC 9051: Internet Message Access Protocol (IMAP) - Version 4rev2
https://datatracker.ietf.org/doc/html/rfc9051
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The parser had a fallback that returned Response.Exists 0 for any
unrecognized `* <number> <keyword>` response. This caused `* 0 RECENT`
to overwrite the real EXISTS count.
Changes:
- Add Response.Recent to handle `* <n> RECENT` responses
- Add Response.Unknown for unrecognized responses instead of returning
a fake Exists 0
- Update client to populate mailbox_info.recent field
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
IMAP UIDs and UIDVALIDITY are unsigned 32-bit integers (RFC 9051),
which can exceed OCaml's signed int32 max value (2147483647).
Changed to int64 throughout to properly handle values up to 4294967295.
Fixes Int32.of_string failure when UIDVALIDITY value exceeds 2^31-1.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix "cannot write to closed writer" by storing TLS flow instead of
Buf_write.t, creating fresh writer for each command
- Add Logs library integration with imap.client source for debugging
- Add --plain flag to use AUTHENTICATE PLAIN instead of LOGIN
- Add Command.pp for safe logging (passwords redacted)
- Fix parsing of \* wildcard flag in PERMANENTFLAGS response
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reorganize from 11 subdirectories to 2:
- lib/imap/ - standalone IMAP client library
- lib/imapd/ - consolidated server library
Modules renamed for clarity:
- Types -> Protocol (IMAP protocol definitions)
- imap_auth -> Auth
- imap_parser -> Parser
- imap_server -> Server
- imap_storage -> Storage
- imap_read -> Read
- imap_write -> Write
- imap_client -> Client
- imap_client_error -> Client_error
- imap_client_pool -> Client_pool
Access via Imapd.Protocol, Imapd.Server, etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>