1use core::{fmt, mem};
48
49use alloc::{boxed::Box, string::String, string::ToString, vec::Vec};
50
51use imap_codec::{
52 GreetingCodec,
53 fragmentizer::{DecodeMessageError, FragmentInfo, Fragmentizer},
54 imap_types::{
55 IntoStatic,
56 response::{Capability, Code, GreetingKind},
57 secret::Secret,
58 utils::escape_byte_string,
59 },
60};
61use log::trace;
62use thiserror::Error;
63
64use crate::{coroutine::*, imap_try, rfc3501::capability::*};
65
66#[derive(Clone, Debug, Error)]
68pub enum ImapGreetingGetError {
69 #[error("IMAP greeting failed: BYE {0}")]
70 Bye(String),
71
72 #[error("IMAP greeting failed: reached unexpected EOF on stream")]
73 Eof,
74 #[error("IMAP greeting failed: decode error")]
75 DecodingFailure(Secret<Box<[u8]>>),
76 #[error("IMAP greeting failed: parse error: message is poisoned")]
77 MessageIsPoisoned(Secret<Box<[u8]>>),
78 #[error("IMAP greeting failed: parse error: message is too long")]
79 MessageTooLong(Secret<Box<[u8]>>),
80
81 #[error(transparent)]
82 Capability(#[from] ImapCapabilityGetError),
83}
84
85#[derive(Debug)]
87pub struct ImapGreetingOk {
88 pub capability: Vec<Capability<'static>>,
89 pub pre_authenticated: bool,
90}
91
92#[derive(Clone, Debug, Default, Eq, PartialEq)]
94pub struct ImapGreetingGetOptions {
95 pub ensure_capabilities: bool,
97}
98
99pub struct ImapGreetingGet {
101 codec: GreetingCodec,
102 state: State,
103 wants_read: bool,
104 observed: Vec<Capability<'static>>,
105 pre_authenticated: bool,
106 opts: ImapGreetingGetOptions,
107}
108
109impl ImapGreetingGet {
110 pub fn new(opts: ImapGreetingGetOptions) -> Self {
111 Self {
112 codec: GreetingCodec::new(),
113 state: State::Read,
114 wants_read: false,
115 observed: Vec::new(),
116 pre_authenticated: false,
117 opts,
118 }
119 }
120}
121
122impl ImapCoroutine for ImapGreetingGet {
123 type Yield = ImapYield;
124 type Return = Result<ImapGreetingOk, ImapGreetingGetError>;
125
126 fn resume(
127 &mut self,
128 fragmentizer: &mut Fragmentizer,
129 mut arg: Option<&[u8]>,
130 ) -> ImapCoroutineState<Self::Yield, Self::Return> {
131 loop {
132 trace!("greeting: {}", self.state);
133
134 if mem::take(&mut self.wants_read) {
135 return ImapCoroutineState::Yielded(ImapYield::WantsRead);
136 }
137
138 match &mut self.state {
139 State::Read => match arg.take() {
140 Some(&[]) => {
141 return ImapCoroutineState::Complete(Err(ImapGreetingGetError::Eof));
142 }
143 Some(bytes) => {
144 trace!("read bytes: {}", escape_byte_string(bytes));
145 fragmentizer.enqueue_bytes(bytes);
146 self.state = State::Deserialize;
147 }
148 None => {
149 self.wants_read = true;
150 }
151 },
152 State::Deserialize => match fragmentizer.progress() {
153 Some(info @ FragmentInfo::Line { .. }) => {
154 let bytes = fragmentizer.fragment_bytes(info);
155 trace!("read greeting line: {}", escape_byte_string(bytes));
156
157 if !fragmentizer.is_message_complete() {
158 continue;
159 }
160
161 match fragmentizer.decode_message(&self.codec) {
162 Ok(greeting) if greeting.kind == GreetingKind::Bye => {
163 let err = ImapGreetingGetError::Bye(greeting.text.to_string());
164 return ImapCoroutineState::Complete(Err(err));
165 }
166 Ok(greeting) => {
167 self.pre_authenticated = greeting.kind == GreetingKind::PreAuth;
168
169 if let Some(Code::Capability(capability)) = greeting.code {
170 self.observed = capability.into_static().into_iter().collect();
171 }
172
173 if self.opts.ensure_capabilities && self.observed.is_empty() {
174 self.state = State::Capability(ImapCapabilityGet::new());
175 continue;
176 }
177
178 return ImapCoroutineState::Complete(Ok(ImapGreetingOk {
179 capability: mem::take(&mut self.observed),
180 pre_authenticated: self.pre_authenticated,
181 }));
182 }
183 Err(err) => {
184 let bytes = fragmentizer.message_bytes();
185 let bytes = Secret::new(bytes.into());
186 let err = match err {
187 DecodeMessageError::DecodingFailure(_)
188 | DecodeMessageError::DecodingRemainder { .. } => {
189 ImapGreetingGetError::DecodingFailure(bytes)
190 }
191 DecodeMessageError::MessageTooLong { .. } => {
192 ImapGreetingGetError::MessageTooLong(bytes)
193 }
194 DecodeMessageError::MessagePoisoned { .. } => {
195 ImapGreetingGetError::MessageIsPoisoned(bytes)
196 }
197 };
198 return ImapCoroutineState::Complete(Err(err));
199 }
200 }
201 }
202 Some(FragmentInfo::Literal { .. }) => unreachable!(),
204 None => {
205 self.state = State::Read;
206 }
207 },
208 State::Capability(capability) => {
209 let caps = imap_try!(capability, fragmentizer, arg.take());
210 return ImapCoroutineState::Complete(Ok(ImapGreetingOk {
211 capability: caps,
212 pre_authenticated: self.pre_authenticated,
213 }));
214 }
215 }
216 }
217 }
218}
219
220enum State {
221 Read,
222 Deserialize,
223 Capability(ImapCapabilityGet),
224}
225
226impl fmt::Display for State {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 match self {
229 Self::Read => f.write_str("read greeting"),
230 Self::Deserialize => f.write_str("decode greeting"),
231 Self::Capability(_) => f.write_str("fetch capabilities"),
232 }
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use alloc::vec::Vec;
239
240 use super::*;
241
242 #[test]
243 fn ok_with_inline_capability_returns_ok() {
244 let mut greeting = ImapGreetingGet::new(ImapGreetingGetOptions {
245 ensure_capabilities: true,
246 });
247 let mut frag = Fragmentizer::new(50 * 1024 * 1024);
248
249 expect_wants_read(&mut greeting, &mut frag);
250
251 let reply = b"* OK [CAPABILITY IMAP4REV1 IDLE] hello\r\n";
252 let ok = expect_complete_ok(&mut greeting, &mut frag, reply);
253 assert!(!ok.pre_authenticated);
254 assert_eq!(2, ok.capability.len());
255 }
256
257 #[test]
258 fn ok_without_inline_capability_drives_extra_round_trip() {
259 let mut greeting = ImapGreetingGet::new(ImapGreetingGetOptions {
260 ensure_capabilities: true,
261 });
262 let mut frag = Fragmentizer::new(50 * 1024 * 1024);
263
264 expect_wants_read(&mut greeting, &mut frag);
265 expect_wants_write_after(&mut greeting, &mut frag, b"* OK hello\r\n");
266 }
267
268 #[test]
269 fn preauth_sets_flag() {
270 let mut greeting = ImapGreetingGet::new(ImapGreetingGetOptions::default());
271 let mut frag = Fragmentizer::new(50 * 1024 * 1024);
272
273 expect_wants_read(&mut greeting, &mut frag);
274
275 let reply = b"* PREAUTH [CAPABILITY IMAP4REV1] welcome\r\n";
276 let ok = expect_complete_ok(&mut greeting, &mut frag, reply);
277 assert!(ok.pre_authenticated);
278 }
279
280 #[test]
281 fn bye_returns_bye_error() {
282 let mut greeting = ImapGreetingGet::new(ImapGreetingGetOptions::default());
283 let mut frag = Fragmentizer::new(50 * 1024 * 1024);
284
285 expect_wants_read(&mut greeting, &mut frag);
286
287 let err = expect_complete_err(&mut greeting, &mut frag, b"* BYE service unavailable\r\n");
288 let ImapGreetingGetError::Bye(text) = err else {
289 panic!("expected ImapGreetingGetError::Bye, got {err:?}");
290 };
291 assert_eq!(text, "service unavailable");
292 }
293
294 #[test]
295 fn eof_returns_eof_error() {
296 let mut greeting = ImapGreetingGet::new(ImapGreetingGetOptions::default());
297 let mut frag = Fragmentizer::new(50 * 1024 * 1024);
298
299 expect_wants_read(&mut greeting, &mut frag);
300
301 let err = expect_complete_err(&mut greeting, &mut frag, b"");
302 assert!(matches!(err, ImapGreetingGetError::Eof));
303 }
304
305 fn expect_wants_read(cor: &mut ImapGreetingGet, frag: &mut Fragmentizer) {
308 match cor.resume(frag, None) {
309 ImapCoroutineState::Yielded(ImapYield::WantsRead) => {}
310 state => panic!("expected WantsRead, got {state:?}"),
311 }
312 }
313
314 fn expect_wants_write_after(
315 cor: &mut ImapGreetingGet,
316 frag: &mut Fragmentizer,
317 arg: &[u8],
318 ) -> Vec<u8> {
319 match cor.resume(frag, Some(arg)) {
320 ImapCoroutineState::Yielded(ImapYield::WantsWrite(bytes)) => bytes,
321 state => panic!("expected WantsWrite, got {state:?}"),
322 }
323 }
324
325 fn expect_complete_ok(
326 cor: &mut ImapGreetingGet,
327 frag: &mut Fragmentizer,
328 reply: &[u8],
329 ) -> ImapGreetingOk {
330 match cor.resume(frag, Some(reply)) {
331 ImapCoroutineState::Complete(Ok(value)) => value,
332 state => panic!("expected Complete(Ok), got {state:?}"),
333 }
334 }
335
336 fn expect_complete_err(
337 cor: &mut ImapGreetingGet,
338 frag: &mut Fragmentizer,
339 reply: &[u8],
340 ) -> ImapGreetingGetError {
341 match cor.resume(frag, Some(reply)) {
342 ImapCoroutineState::Complete(Err(err)) => err,
343 state => panic!("expected Complete(Err), got {state:?}"),
344 }
345 }
346}