imap_next/
server.rs

1use std::fmt::{Debug, Formatter};
2
3use imap_codec::{
4    imap_types::{
5        auth::AuthenticateData,
6        command::{Command, CommandBody},
7        core::{LiteralMode, Tag, Text},
8        extensions::idle::IdleDone,
9        response::{
10            CommandContinuationRequest, CommandContinuationRequestBasic, Data, Greeting, Response,
11            Status,
12        },
13        secret::Secret,
14        ToStatic,
15    },
16    AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec,
17};
18use thiserror::Error;
19
20use crate::{
21    handle::{Handle, HandleGenerator, HandleGeneratorGenerator, RawHandle},
22    receive::{ReceiveError, ReceiveEvent, ReceiveState},
23    server_send::{ServerSendEvent, ServerSendState},
24    types::CommandAuthenticate,
25    Interrupt, State,
26};
27
28static HANDLE_GENERATOR_GENERATOR: HandleGeneratorGenerator<ResponseHandle> =
29    HandleGeneratorGenerator::new();
30
31#[derive(Clone, Debug, PartialEq)]
32#[non_exhaustive]
33pub struct Options {
34    pub crlf_relaxed: bool,
35    /// Max literal size accepted by server.
36    ///
37    /// Bigger literals are rejected by the server.
38    ///
39    /// Currently, we don't distinguish between general literals and the literal used in the
40    /// APPEND command. However, this might change in the future. Note that
41    /// `max_literal_size < max_command_size` must hold.
42    pub max_literal_size: u32,
43    /// Max command size that can be parsed by the server.
44    ///
45    /// Bigger commands raise an error.
46    pub max_command_size: u32,
47    literal_accept_ccr: CommandContinuationRequest<'static>,
48    literal_reject_ccr: CommandContinuationRequest<'static>,
49}
50
51impl Default for Options {
52    fn default() -> Self {
53        Self {
54            // Lean towards conformity
55            crlf_relaxed: false,
56            // 25 MiB is a common maximum email size (Oct. 2023).
57            max_literal_size: 25 * 1024 * 1024,
58            // Must be bigger than `max_literal_size`.
59            // 64 KiB is used by Dovecot.
60            max_command_size: (25 * 1024 * 1024) + (64 * 1024),
61            // Short unmeaning text
62            literal_accept_ccr: CommandContinuationRequest::basic(None, Text::unvalidated("..."))
63                .unwrap(),
64            // Short unmeaning text
65            literal_reject_ccr: CommandContinuationRequest::basic(None, Text::unvalidated("..."))
66                .unwrap(),
67        }
68    }
69}
70
71impl Options {
72    pub fn literal_accept_text(&self) -> &Text {
73        match self.literal_accept_ccr {
74            CommandContinuationRequest::Basic(ref basic) => basic.text(),
75            CommandContinuationRequest::Base64(_) => unreachable!(),
76        }
77    }
78
79    pub fn set_literal_accept_text(&mut self, text: String) -> Result<(), String> {
80        // imap-codec doesn't return `text` on error. Thus, we first check with &str as a
81        // workaround ...
82        if CommandContinuationRequestBasic::new(None, text.as_str()).is_ok() {
83            // ... and can use `unwrap` later.
84            self.literal_accept_ccr = CommandContinuationRequest::basic(None, text).unwrap();
85            Ok(())
86        } else {
87            Err(text)
88        }
89    }
90
91    pub fn literal_reject_text(&self) -> &Text {
92        match self.literal_reject_ccr {
93            CommandContinuationRequest::Basic(ref basic) => basic.text(),
94            CommandContinuationRequest::Base64(_) => unreachable!(),
95        }
96    }
97
98    pub fn set_literal_reject_text(&mut self, text: String) -> Result<(), String> {
99        // imap-codec doesn't return `text` on error. Thus, we first check with &str as a
100        // workaround ...
101        if CommandContinuationRequestBasic::new(None, text.as_str()).is_ok() {
102            // ... and can use `unwrap` later.
103            self.literal_reject_ccr = CommandContinuationRequest::basic(None, text).unwrap();
104            Ok(())
105        } else {
106            Err(text)
107        }
108    }
109}
110
111pub struct Server {
112    options: Options,
113    handle_generator: HandleGenerator<ResponseHandle>,
114    send_state: ServerSendState,
115    receive_state: ReceiveState,
116    next_expected_message: NextExpectedMessage,
117}
118
119impl Server {
120    pub fn new(options: Options, greeting: Greeting<'static>) -> Self {
121        let mut send_state =
122            ServerSendState::new(GreetingCodec::default(), ResponseCodec::default());
123        send_state.enqueue_greeting(greeting);
124
125        let receive_state = ReceiveState::new(options.crlf_relaxed, Some(options.max_command_size));
126        let next_expected_message = NextExpectedMessage::Command(CommandCodec::default());
127
128        Self {
129            options,
130            handle_generator: HANDLE_GENERATOR_GENERATOR.generate(),
131            send_state,
132            receive_state,
133            next_expected_message,
134        }
135    }
136
137    /// Enqueues the [`Data`] response for being sent to the client.
138    ///
139    /// The response is not sent immediately but during one of the next calls of
140    /// [`Server::next`]. All responses are sent in the same order they have been
141    /// enqueued.
142    pub fn enqueue_data(&mut self, data: Data<'static>) -> ResponseHandle {
143        let handle = self.handle_generator.generate();
144        self.send_state
145            .enqueue_response(Some(handle), Response::Data(data));
146        handle
147    }
148
149    /// Enqueues the [`Status`] response for being sent to the client.
150    ///
151    /// The response is not sent immediately but during one of the next calls of
152    /// [`Server::next`]. All responses are sent in the same order they have been
153    /// enqueued.
154    pub fn enqueue_status(&mut self, status: Status<'static>) -> ResponseHandle {
155        let handle = self.handle_generator.generate();
156        self.send_state
157            .enqueue_response(Some(handle), Response::Status(status));
158        handle
159    }
160
161    /// Enqueues the [`CommandContinuationRequest`] response for being sent to the client.
162    ///
163    /// The response is not sent immediately but during one of the next calls of
164    /// [`Server::next`]. All responses are sent in the same order they have been
165    /// enqueued.
166    pub fn enqueue_continuation_request(
167        &mut self,
168        continuation_request: CommandContinuationRequest<'static>,
169    ) -> ResponseHandle {
170        let handle = self.handle_generator.generate();
171        self.send_state.enqueue_response(
172            Some(handle),
173            Response::CommandContinuationRequest(continuation_request),
174        );
175        handle
176    }
177
178    fn progress_send(&mut self) -> Result<Option<Event>, Interrupt<Error>> {
179        match self.send_state.next() {
180            Ok(Some(ServerSendEvent::Greeting { greeting })) => {
181                // The initial greeting was sucessfully sent, inform the caller
182                Ok(Some(Event::GreetingSent { greeting }))
183            }
184            Ok(Some(ServerSendEvent::Response {
185                handle: Some(handle),
186                response,
187            })) => {
188                // A response was sucessfully sent, inform the caller
189                Ok(Some(Event::ResponseSent { handle, response }))
190            }
191            Ok(Some(ServerSendEvent::Response { handle: None, .. })) => {
192                // An internally created response was sent, don't inform the caller
193                Ok(None)
194            }
195            Ok(_) => {
196                // No progress yet
197                Ok(None)
198            }
199            Err(Interrupt::Io(io)) => Err(Interrupt::Io(io)),
200            Err(Interrupt::Error(_)) => unreachable!(),
201        }
202    }
203
204    fn progress_receive(&mut self) -> Result<Option<Event>, Interrupt<Error>> {
205        match &self.next_expected_message {
206            NextExpectedMessage::Command(codec) => match self
207                .receive_state
208                .next::<CommandCodec>(codec)
209            {
210                Ok(ReceiveEvent::DecodingSuccess(command)) => match command.body {
211                    CommandBody::Authenticate {
212                        mechanism,
213                        initial_response,
214                    } => {
215                        self.next_expected_message =
216                            NextExpectedMessage::AuthenticateData(AuthenticateDataCodec::default());
217
218                        Ok(Some(Event::CommandAuthenticateReceived {
219                            command_authenticate: CommandAuthenticate {
220                                tag: command.tag,
221                                mechanism,
222                                initial_response,
223                            },
224                        }))
225                    }
226                    CommandBody::Idle => {
227                        self.next_expected_message = NextExpectedMessage::IdleAccept;
228
229                        Ok(Some(Event::IdleCommandReceived { tag: command.tag }))
230                    }
231                    body => Ok(Some(Event::CommandReceived {
232                        command: Command {
233                            tag: command.tag,
234                            body,
235                        },
236                    })),
237                },
238                Ok(ReceiveEvent::LiteralAnnouncement { mode, length }) => {
239                    if length > self.options.max_literal_size {
240                        match mode {
241                            LiteralMode::Sync => {
242                                // Inform the client that the literal was rejected.
243                                if let Some(tag) = self.receive_state.message_tag() {
244                                    // Unwrap: This should never fail because the text is
245                                    // not Base64.
246                                    let status = Status::bad(
247                                        Some(tag),
248                                        None,
249                                        self.options.literal_reject_text().to_static(),
250                                    )
251                                    .unwrap();
252                                    self.send_state
253                                        .enqueue_response(None, Response::Status(status));
254
255                                    let discarded_bytes = self.receive_state.discard_message();
256
257                                    Err(Interrupt::Error(Error::LiteralTooLong { discarded_bytes }))
258                                } else {
259                                    // We need a tag for rejecting the literal, but the
260                                    // message seems to be malformed because it contains no
261                                    // tag. Discarding the message immediately would be
262                                    // dangerous because the literal might contain bytes that
263                                    // look like IMAP commands. Doing nothing might lead
264                                    // to a deadlock because the client is waiting for a
265                                    // response. We prefer the latter because it is more safe.
266                                    // If we receive the complete message for whatever reason,
267                                    // we need to make sure that it will be discarded.
268                                    // Note that `max_command_size` will still prevent
269                                    // allocation of unlimited memory.
270                                    self.receive_state.poison_message();
271
272                                    Ok(None)
273                                }
274                            }
275                            LiteralMode::NonSync => {
276                                // We can't (reliably) make the client stop sending data.
277                                // Discarding the message immediately would be dangerous
278                                // because the literal might contain bytes that look like
279                                // IMAP commands. So instead we continue receiving the
280                                // message but discard it afterwards. Note that
281                                // `max_command_size` will still prevent allocation of
282                                // unlimited memory.
283                                self.receive_state.poison_message();
284
285                                Ok(None)
286                            }
287                        }
288                    } else {
289                        match mode {
290                            LiteralMode::Sync => {
291                                // Inform the client that the literal was accepted.
292
293                                // Unwrap: This should never fail because the text is not Base64.
294                                let cont = CommandContinuationRequest::basic(
295                                    None,
296                                    self.options.literal_accept_text().to_static(),
297                                )
298                                .unwrap();
299                                self.send_state.enqueue_response(
300                                    None,
301                                    Response::CommandContinuationRequest(cont),
302                                );
303                            }
304                            LiteralMode::NonSync => {
305                                // We don't need to inform the client because non-sync literals
306                                // are automatically accepted.
307                            }
308                        }
309
310                        Ok(None)
311                    }
312                }
313                Err(interrupt) => Err(handle_receive_interrupt(interrupt)),
314            },
315            NextExpectedMessage::AuthenticateData(codec) => {
316                match self.receive_state.next::<AuthenticateDataCodec>(codec) {
317                    Ok(ReceiveEvent::DecodingSuccess(authenticate_data)) => {
318                        Ok(Some(Event::AuthenticateDataReceived { authenticate_data }))
319                    }
320                    Ok(ReceiveEvent::LiteralAnnouncement { .. }) => {
321                        // Unexpected literal, let's continue and see what happens
322                        Ok(None)
323                    }
324                    Err(interrupt) => Err(handle_receive_interrupt(interrupt)),
325                }
326            }
327            NextExpectedMessage::IdleAccept => {
328                // We don't expect any message until the server user calls
329                // `idle_accept` or `idle_reject`.
330                // TODO: It's strange to return NeedMoreInput here, but it works for now.
331                Err(Interrupt::Io(crate::Io::NeedMoreInput))
332            }
333            NextExpectedMessage::IdleDone(codec) => {
334                match self.receive_state.next::<IdleDoneCodec>(codec) {
335                    Ok(ReceiveEvent::DecodingSuccess(IdleDone)) => {
336                        self.next_expected_message =
337                            NextExpectedMessage::Command(CommandCodec::default());
338
339                        Ok(Some(Event::IdleDoneReceived))
340                    }
341                    Ok(ReceiveEvent::LiteralAnnouncement { .. }) => {
342                        // Unexpected literal, let's continue and see what happens
343                        Ok(None)
344                    }
345                    Err(interrupt) => Err(handle_receive_interrupt(interrupt)),
346                }
347            }
348        }
349    }
350
351    pub fn authenticate_continue(
352        &mut self,
353        continuation_request: CommandContinuationRequest<'static>,
354    ) -> Result<ResponseHandle, CommandContinuationRequest<'static>> {
355        if let NextExpectedMessage::AuthenticateData { .. } = self.next_expected_message {
356            let handle = self.enqueue_continuation_request(continuation_request);
357            Ok(handle)
358        } else {
359            Err(continuation_request)
360        }
361    }
362
363    pub fn authenticate_finish(
364        &mut self,
365        status: Status<'static>,
366    ) -> Result<ResponseHandle, Status<'static>> {
367        if let NextExpectedMessage::AuthenticateData(_) = &mut self.next_expected_message {
368            let handle = self.enqueue_status(status);
369
370            self.next_expected_message = NextExpectedMessage::Command(CommandCodec::default());
371
372            Ok(handle)
373        } else {
374            Err(status)
375        }
376    }
377
378    pub fn idle_accept(
379        &mut self,
380        continuation_request: CommandContinuationRequest<'static>,
381    ) -> Result<ResponseHandle, CommandContinuationRequest<'static>> {
382        if let NextExpectedMessage::IdleAccept = &mut self.next_expected_message {
383            let handle = self.enqueue_continuation_request(continuation_request);
384
385            self.next_expected_message = NextExpectedMessage::IdleDone(IdleDoneCodec::default());
386
387            Ok(handle)
388        } else {
389            Err(continuation_request)
390        }
391    }
392
393    pub fn idle_reject(
394        &mut self,
395        status: Status<'static>,
396    ) -> Result<ResponseHandle, Status<'static>> {
397        if let NextExpectedMessage::IdleAccept = &mut self.next_expected_message {
398            let handle = self.enqueue_status(status);
399
400            self.next_expected_message = NextExpectedMessage::Command(CommandCodec::default());
401
402            Ok(handle)
403        } else {
404            Err(status)
405        }
406    }
407}
408
409impl Debug for Server {
410    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
411        f.debug_struct("Server")
412            .field("options", &self.options)
413            .field("handle_generator", &self.handle_generator)
414            .finish_non_exhaustive()
415    }
416}
417
418impl State for Server {
419    type Event = Event;
420    type Error = Error;
421
422    fn enqueue_input(&mut self, bytes: &[u8]) {
423        self.receive_state.enqueue_input(bytes);
424    }
425
426    fn next(&mut self) -> Result<Self::Event, Interrupt<Self::Error>> {
427        loop {
428            if let Some(event) = self.progress_send()? {
429                return Ok(event);
430            }
431
432            if let Some(event) = self.progress_receive()? {
433                return Ok(event);
434            }
435        }
436    }
437}
438
439fn handle_receive_interrupt(receive_interrupt: Interrupt<ReceiveError>) -> Interrupt<Error> {
440    match receive_interrupt {
441        Interrupt::Io(io) => Interrupt::Io(io),
442        Interrupt::Error(ReceiveError::DecodingFailure { discarded_bytes }) => {
443            Interrupt::Error(Error::MalformedMessage { discarded_bytes })
444        }
445        Interrupt::Error(ReceiveError::ExpectedCrlfGotLf { discarded_bytes }) => {
446            Interrupt::Error(Error::ExpectedCrlfGotLf { discarded_bytes })
447        }
448        Interrupt::Error(ReceiveError::MessageIsPoisoned { discarded_bytes }) => {
449            Interrupt::Error(Error::MalformedMessage { discarded_bytes })
450        }
451        Interrupt::Error(ReceiveError::MessageTooLong { discarded_bytes }) => {
452            Interrupt::Error(Error::CommandTooLong { discarded_bytes })
453        }
454    }
455}
456
457/// Handle for enqueued [`Response`].
458///
459/// This handle can be used to track the sending progress. After a [`Response`] was enqueued via
460/// [`Server::enqueue_data`] or [`Server::enqueue_status`] it is in the process of being
461/// sent until [`Server::next`] returns a [`Event::ResponseSent`] with the
462/// corresponding handle.
463#[derive(Clone, Copy, Eq, PartialEq, Hash)]
464pub struct ResponseHandle(RawHandle);
465
466// Implement a short debug representation that hides the underlying raw handle
467impl Debug for ResponseHandle {
468    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469        f.debug_tuple("ResponseHandle")
470            .field(&self.0.generator_id())
471            .field(&self.0.handle_id())
472            .finish()
473    }
474}
475
476impl Handle for ResponseHandle {
477    fn from_raw(raw_handle: RawHandle) -> Self {
478        Self(raw_handle)
479    }
480}
481
482#[derive(Debug)]
483pub enum Event {
484    /// Initial [`Greeting] was sent successfully.
485    GreetingSent {
486        greeting: Greeting<'static>,
487    },
488    /// Enqueued [`Response`] was sent successfully.
489    ResponseSent {
490        /// Handle of the formerly enqueued [`Response`].
491        handle: ResponseHandle,
492        /// Formerly enqueued [`Response`] that was now sent.
493        response: Response<'static>,
494    },
495    /// Command received.
496    CommandReceived {
497        command: Command<'static>,
498    },
499    /// Command AUTHENTICATE received.
500    ///
501    /// Note: The server MUST call [`Server::authenticate_continue`] (if it needs more data for
502    /// authentication) or [`Server::authenticate_finish`] (if there already is enough data for
503    /// authentication) next. "Enough data" is determined by the used SASL mechanism, if there was
504    /// an initial response (SASL-IR), etc.
505    CommandAuthenticateReceived {
506        command_authenticate: CommandAuthenticate,
507    },
508    /// Continuation to AUTHENTICATE received.
509    ///
510    /// Note: The server MUST call [`Server::authenticate_continue`] (if it needs more data for
511    /// authentication) or [`Server::authenticate_finish`] (if there already is enough data for
512    /// authentication) next. "Enough data" is determined by the used SASL mechanism, if there was
513    /// an initial response (SASL-IR), etc.
514    ///
515    /// Note, too: The client may abort the authentication by using [`AuthenticateData::Cancel`].
516    /// Make sure to honor the client's request to not end up in an infinite loop. It's up to the
517    /// server to end the authentication flow.
518    AuthenticateDataReceived {
519        authenticate_data: AuthenticateData<'static>,
520    },
521    IdleCommandReceived {
522        tag: Tag<'static>,
523    },
524    IdleDoneReceived,
525}
526
527#[derive(Debug, Error)]
528pub enum Error {
529    #[error("Expected `\\r\\n`, got `\\n`")]
530    ExpectedCrlfGotLf { discarded_bytes: Secret<Box<[u8]>> },
531    #[error("Received malformed message")]
532    MalformedMessage { discarded_bytes: Secret<Box<[u8]>> },
533    #[error("Literal was rejected because it was too long")]
534    LiteralTooLong { discarded_bytes: Secret<Box<[u8]>> },
535    #[error("Command is too long")]
536    CommandTooLong { discarded_bytes: Secret<Box<[u8]>> },
537}
538
539#[derive(Clone, Debug)]
540enum NextExpectedMessage {
541    Command(CommandCodec),
542    AuthenticateData(AuthenticateDataCodec),
543    IdleAccept,
544    IdleDone(IdleDoneCodec),
545}