1use core::mem;
8
9use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
10
11use imap_codec::{
12 ResponseCodec,
13 encode::{Encoder, Fragment},
14 fragmentizer::{DecodeMessageError, FragmentInfo, Fragmentizer},
15 imap_types::{
16 IntoStatic,
17 core::LiteralMode,
18 response::{Bye, CommandContinuationRequest, Data, Response, Status, StatusBody, Tagged},
19 secret::Secret,
20 utils::escape_byte_string,
21 },
22};
23use log::{debug, trace};
24use thiserror::Error;
25
26use crate::coroutine::{ImapCoroutine, ImapCoroutineState, ImapYield};
27
28#[derive(Clone, Debug, Error)]
30pub enum ImapSendError {
31 #[error("Reached unexpected EOF on IMAP stream")]
33 Eof,
34 #[error("Decode IMAP response error")]
36 DecodingFailure(Secret<Box<[u8]>>),
37 #[error("Parse IMAP response error: message is poisoned")]
40 MessageIsPoisoned(Secret<Box<[u8]>>),
41 #[error("Parse IMAP response error: message is too long")]
44 MessageTooLong(Secret<Box<[u8]>>),
45}
46
47pub enum ImapSendResult<T: Encoder> {
49 Ok(Box<ImapSendOutput<T>>),
52 WantsRead,
54 WantsWrite(Vec<u8>),
56 Err(ImapSendError),
58}
59
60#[derive(Debug)]
61enum State {
62 Serialize,
63 Read,
64 Deserialize,
65}
66
67pub struct ImapSend<T: Encoder> {
69 message: Option<T::Message<'static>>,
70 state: State,
71 wants_read: bool,
72 wants_write: Option<Vec<u8>>,
73 fragments: VecDeque<Fragment>,
74 codec: ResponseCodec,
75 data: Vec<Data<'static>>,
76 untagged: Vec<StatusBody<'static>>,
77 tagged: Option<Tagged<'static>>,
78 bye: Option<Bye<'static>>,
79 cr: Option<CommandContinuationRequest<'static>>,
80 limbo_literal: Option<Vec<u8>>,
81 done: bool,
82}
83
84impl<T: Encoder> ImapSend<T> {
85 pub fn new(encoder: T, message: T::Message<'static>) -> Self {
88 let fragments = encoder.encode(&message).collect();
89
90 Self {
91 message: Some(message),
92 codec: ResponseCodec::new(),
93 state: State::Serialize,
94 wants_read: false,
95 wants_write: None,
96 fragments,
97 data: Vec::new(),
98 untagged: Vec::new(),
99 tagged: None,
100 bye: None,
101 cr: None,
102 limbo_literal: None,
103 done: false,
104 }
105 }
106
107 pub fn receive(message: T::Message<'static>) -> Self {
113 Self {
114 message: Some(message),
115 codec: ResponseCodec::new(),
116 state: State::Read,
117 wants_read: false,
118 wants_write: None,
119 fragments: VecDeque::new(),
120 data: Vec::new(),
121 untagged: Vec::new(),
122 tagged: None,
123 bye: None,
124 cr: None,
125 limbo_literal: None,
126 done: false,
127 }
128 }
129
130 pub fn resume(
133 &mut self,
134 fragmentizer: &mut Fragmentizer,
135 mut arg: Option<&[u8]>,
136 ) -> ImapSendResult<T> {
137 loop {
138 if let Some(bytes) = self.wants_write.take() {
139 return ImapSendResult::WantsWrite(bytes);
140 }
141
142 if mem::take(&mut self.wants_read) {
143 return ImapSendResult::WantsRead;
144 }
145
146 match self.state {
147 State::Serialize => {
148 let mut buf = Vec::new();
149
150 if let Some(bytes) = self.limbo_literal.take() {
151 buf.extend(bytes);
152 }
153
154 while let Some(fragment) = self.fragments.pop_front() {
155 match fragment {
156 Fragment::Line { data } => {
157 buf.extend(data);
158 }
159 Fragment::Literal { data, mode } => match mode {
160 LiteralMode::NonSync => {
161 buf.extend(data);
162 }
163 LiteralMode::Sync => {
164 self.limbo_literal.replace(data);
165 break;
166 }
167 },
168 }
169 }
170
171 if !buf.is_empty() {
172 self.wants_write = Some(buf);
173 }
174 self.state = State::Read;
175 }
176 State::Read => match arg.take() {
177 Some(&[]) => {
178 return ImapSendResult::Err(ImapSendError::Eof);
179 }
180 Some(data) => {
181 trace!("read bytes: {}", escape_byte_string(data));
182 fragmentizer.enqueue_bytes(data);
183 self.state = State::Deserialize;
184 }
185 None => {
186 self.wants_read = true;
187 }
188 },
189 State::Deserialize => match fragmentizer.progress() {
190 Some(info @ FragmentInfo::Line { .. }) => {
191 let bytes = fragmentizer.fragment_bytes(info);
192 trace!("read line fragment: {}", escape_byte_string(bytes));
193
194 if !fragmentizer.is_message_complete() {
195 continue;
196 }
197
198 match fragmentizer.decode_message(&self.codec) {
199 Ok(Response::Data(data)) => {
200 self.data.push(data.into_static());
201 }
202 Ok(Response::Status(Status::Untagged(status))) => {
203 self.untagged.push(status.into_static());
204 }
205 Ok(Response::Status(Status::Tagged(tagged))) => {
206 self.tagged.replace(tagged.into_static());
207 self.done = true;
208 }
209 Ok(Response::Status(Status::Bye(bye))) => {
210 self.bye.replace(bye.into_static());
211 self.done = true;
212 }
213 Ok(Response::CommandContinuationRequest(cr)) => {
214 self.cr.replace(cr.into_static());
215 self.done = self.limbo_literal.is_none();
216 }
217 Err(decode_err) => {
218 let bytes = fragmentizer.message_bytes();
219 let err = match decode_err {
220 DecodeMessageError::DecodingFailure(_)
221 | DecodeMessageError::DecodingRemainder { .. } => {
222 if bytes.starts_with(b"* ") {
228 debug!("skipping undecodable untagged response");
229 trace!("{}", escape_byte_string(bytes));
230 continue;
231 }
232
233 let err = Secret::new(bytes.into());
234 ImapSendError::DecodingFailure(err)
235 }
236 DecodeMessageError::MessageTooLong { .. } => {
237 let err = Secret::new(bytes.into());
238 ImapSendError::MessageTooLong(err)
239 }
240 DecodeMessageError::MessagePoisoned { .. } => {
241 let err = Secret::new(bytes.into());
242 ImapSendError::MessageIsPoisoned(err)
243 }
244 };
245
246 return ImapSendResult::Err(err);
247 }
248 }
249 }
250 Some(info @ FragmentInfo::Literal { .. }) => {
251 let bytes = fragmentizer.fragment_bytes(info);
252 trace!("read literal fragment ({} bytes)", bytes.len());
253 }
254 None if self.done => {
255 return ImapSendResult::Ok(Box::new(ImapSendOutput {
257 message: self.message.take().unwrap(),
258 data: mem::take(&mut self.data),
259 untagged: mem::take(&mut self.untagged),
260 tagged: self.tagged.take(),
261 bye: self.bye.take(),
262 continuation_request: self.cr.take(),
263 }));
264 }
265 None if self.limbo_literal.is_some() => {
266 self.state = State::Serialize;
267 }
268 None => {
269 self.state = State::Read;
270 }
271 },
272 }
273 }
274 }
275}
276
277#[derive(Debug)]
280pub struct ImapSendOutput<T: Encoder> {
281 pub message: T::Message<'static>,
283 pub data: Vec<Data<'static>>,
285 pub untagged: Vec<StatusBody<'static>>,
287 pub tagged: Option<Tagged<'static>>,
289 pub bye: Option<Bye<'static>>,
291 pub continuation_request: Option<CommandContinuationRequest<'static>>,
294}
295
296impl<T: Encoder> ImapCoroutine for ImapSend<T> {
297 type Yield = ImapYield;
298 type Return = Result<ImapSendOutput<T>, ImapSendError>;
299
300 fn resume(
301 &mut self,
302 fragmentizer: &mut Fragmentizer,
303 arg: Option<&[u8]>,
304 ) -> ImapCoroutineState<Self::Yield, Self::Return> {
305 match ImapSend::<T>::resume(self, fragmentizer, arg) {
307 ImapSendResult::WantsRead => ImapCoroutineState::Yielded(ImapYield::WantsRead),
308 ImapSendResult::WantsWrite(bytes) => {
309 ImapCoroutineState::Yielded(ImapYield::WantsWrite(bytes))
310 }
311 ImapSendResult::Ok(output) => ImapCoroutineState::Complete(Ok(*output)),
312 ImapSendResult::Err(err) => ImapCoroutineState::Complete(Err(err)),
313 }
314 }
315}