1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! # io-imap
//!
//! I/O-free IMAP client coroutines built on
//! [imap-codec](https://docs.rs/imap-codec): every command exchange is
//! a resumable state machine emitting read and write requests instead
//! of performing I/O itself, so the caller owns the socket and pumps
//! the coroutine (see the `client` feature for a ready-made
//! std-blocking pump). imap-codec is re-exported as [`codec`], with
//! imap-types as [`types`], so consumers encode and decode with the
//! exact same codec version.
//!
//! The crate ships the three standard Pimalaya layers: the I/O-free
//! coroutines (no_std core, always present), a light std client
//! (`client` feature) wrapping a caller-provided stream, and a full
//! std client (`rustls-ring` default, `rustls-aws`, `native-tls`) that
//! also opens TCP, negotiates TLS and authenticates.
//!
//! ## Layout: one module per RFC
//!
//! Like io-http and io-oauth, the source tree mirrors the specs:
//! [`rfc3501`] (IMAP4rev1 commands), [`rfc2177`] (IDLE), [`rfc2971`]
//! (ID), [`rfc3691`] (UNSELECT), [`rfc4315`] (UIDPLUS), [`rfc5161`]
//! (ENABLE), [`rfc5256`] (SORT and THREAD), [`rfc6851`] (MOVE),
//! [`rfc7628`] (OAUTHBEARER) and `rfc7677` (SCRAM-SHA-256, behind the
//! `scram` feature); the [`sasl`] module holds the RFC-agnostic
//! mechanisms (ANONYMOUS, LOGIN, PLAIN, XOAUTH2). The CONDSTORE and
//! QRESYNC extensions (RFC 7162) have no module of their own: they
//! surface as parameters and response fields of the [`rfc3501`]
//! select, examine and fetch coroutines, and power [`watch`]. Code
//! spanning several RFC modules lives at the crate root: [`send`],
//! [`session`], [`watch`], [`client`] and [`coroutine`].
//!
//! Public types follow the Imap-Target-Verb naming scheme
//! (`ImapMailboxCreate`, `ImapMessageFetchStream`) with Options, Error,
//! Yield and Event companions; single-step coroutines hold the send
//! directly, multi-step ones keep a private State enum.
//!
//! ## The coroutine contract
//!
//! Every coroutine implements [`coroutine::ImapCoroutine`]. Unlike the
//! sibling io-* crates, resume takes two arguments besides self: a
//! borrowed `Fragmentizer` (the connection-wide parser buffer, shared
//! across every coroutine run on that connection so partial reads
//! survive between commands) and the optional input bytes. It returns
//! [`coroutine::ImapCoroutineState`]: either a yield or the terminal
//! result. The [`imap_try!`] macro is the coroutine equivalent of `?`.
//!
//! The standard yield is [`coroutine::ImapYield`]: WantsRead (the
//! caller reads more bytes and feeds them back, `Some(&[])` on EOF) or
//! WantsWrite (the caller writes the given bytes). Coroutines that
//! need richer signals declare their own yield enum: the IDLE and
//! mailbox-watch coroutines add an Event variant, and the streaming
//! APPEND and FETCH coroutines add WantsStream / BodyChunk variants so
//! message bodies move straight between socket and caller storage
//! without landing in memory whole.
//!
//! ## The send primitive
//!
//! Every command-shaped coroutine delegates to one shared primitive:
//! [`send::ImapSend`]. It serialises the command through imap-codec
//! (handling synchronising literals by pausing until the server
//! continuation), then collects the response: data and untagged
//! status lines accumulate, a tagged, bye or continuation-request line
//! terminates, and undecodable untagged lines are skipped instead of
//! failing the whole command. Its terminal value is
//! [`send::ImapSendOutput`]; failures surface as
//! [`send::ImapSendError`]. The receive-only constructor
//! [`send::ImapSend::receive`] parses the response of a request whose
//! bytes were written out of band (used by the streamed APPEND).
//!
//! ## Authentication
//!
//! Each SASL mechanism is its own coroutine supporting both the non-IR
//! and SASL-IR (RFC 4959) flows. Every auth and login coroutine offers
//! an optional auto_id chaining an RFC 2971 ID round-trip right after
//! authentication, required by providers such as mail.qq.com and
//! Fastmail. Secrets ride in imap-types `Secret` wrappers so they never
//! land in logs.
//!
//! Which flow to use is decided by [`session`], not by any client: it
//! follows the advertised `SASL-IR` capability unless
//! [`session::ImapSessionOpenOptions::sasl_ir`] overrides it for a
//! server advertising it falsely (Coremail).
//!
//! ## Opening a session
//!
//! [`session`] provides `ImapSessionOpen`, the composite coroutine
//! covering everything between an address and an authenticated session:
//! transport selection, the optional STARTTLS upgrade, the greeting,
//! PREAUTH detection, the SASL-IR policy and the SASL exchange. It
//! yields transport requests (connect this socket, upgrade that one)
//! alongside the usual reads and writes, so a caller on any runtime
//! answers them with its own sockets and inherits the ordering and the
//! provider quirks. The std client is a thirty-line pump over it.
//!
//! ## Watching a mailbox
//!
//! [`watch`] provides `ImapMailboxWatch`, a composite coroutine
//! chaining ENABLE QRESYNC, SELECT (CONDSTORE), a FETCH baseline seed,
//! then an IDLE wake-loop with SELECT (QRESYNC) delta pulls, emitting
//! UID-keyed added/changed/removed events. The connection is dedicated;
//! a shared `AtomicBool` winds it down cleanly.
//!
//! ## The clients
//!
//! The command surface is two traits, [`client::ImapClient`] for
//! blocking callers and [`client::ImapClientAsync`] for async ones.
//! Implement the single `run` method over your own transport and the
//! forty-odd commands come with it. The `Yield = ImapYield` bound on
//! `run` is what keeps the surface honest: the five coroutines with a
//! yield vocabulary of their own cannot be defaulted, which is exactly
//! where implementations are expected to differ.
//!
//! [`client::ImapClientStd`] (`client` feature) is the in-tree
//! implementation, wrapping any blocking `Read + Write` stream plus a
//! per-connection `Fragmentizer`. Its connect constructor (TLS features)
//! is a pump over [`session::ImapSessionOpen`] answering the transport
//! yields with pimalaya-stream, and the four opinionated methods
//! (mailbox watch, streamed APPEND, the two streamed FETCHes) stay
//! inherent to it because each encodes a runtime-specific choice.
//!
//! ## Conventions
//!
//! The conventions every Pimalaya repository shares (the sans-I/O
//! coroutine approach, no_std, module and error rules) are described
//! in the [Pimalaya ARCHITECTURE](https://github.com/pimalaya/.github/blob/master/ARCHITECTURE.md)
//! and [GUIDELINES](https://github.com/pimalaya/.github/blob/master/GUIDELINES.md);
//! the Imap-Target-Verb naming above is the org-wide canon, and the
//! [`codec`] / [`types`] root re-exports are its blessed exception for
//! foreign crates the API is built on.
//! Coroutines log through the log crate at two levels: debug carries a
//! short human-readable phrase at state changes, and a trace directly
//! below dumps the data when there is any. Complete runnable programs
//! live in the examples folder, one per layer.
extern crate alloc;
extern crate std;
/// The imap-codec crate this version of io-imap builds on, re-exported
/// so consumers encode and decode with the exact same codec version.
pub use imap_codec as codec;
/// The imap-types crate matching [`codec`], re-exported for the same
/// version-lock reason.
///
/// Coroutine inputs and outputs are made of these types.
pub use imap_types as types;
/// Tests whether a capability list advertises a given capability, written as a
/// [`matches!`]-style variant pattern without the `Capability::` prefix.
///
/// Matches by variant, so payload-carrying capabilities are checked with a
/// wildcard: `has_imap_capability!(caps, Sort(_))` is true for both bare `SORT`
/// and `SORT=DISPLAY`.
///
/// ```
/// use io_imap::has_imap_capability;
/// use io_imap::types::response::Capability;
///
/// let caps = [Capability::Move, Capability::Sort(None)];
/// assert!(has_imap_capability!(caps, Sort(_)));
/// assert!(has_imap_capability!(caps, Move));
/// assert!(!has_imap_capability!(caps, Idle));
/// ```