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