rip_starttls/imap/
mod.rs

1//! # IMAP
2//!
3//! This module contains the sans I/O implementation for the IMAP
4//! protocol, as well as feature-gated I/O connectors.
5
6#[cfg(feature = "async-std")]
7pub mod async_std;
8#[cfg(feature = "std")]
9pub mod std;
10#[cfg(feature = "tokio")]
11pub mod tokio;
12
13use tracing::debug;
14
15/// The main structure of the IMAP module.
16///
17/// This structure allows you to move a TCP stream to a TLS-ready
18/// state.
19#[derive(Clone, Debug, Default, Eq, PartialEq)]
20pub struct RipStarttls {
21    state: Option<State>,
22    event: Option<Event>,
23    handshake_discarded: bool,
24}
25
26impl RipStarttls {
27    pub const COMMAND: &str = "A STARTTLS\r\n";
28
29    pub fn new(handshake_discarded: bool) -> Self {
30        Self {
31            state: None,
32            event: None,
33            handshake_discarded,
34        }
35    }
36
37    /// Acts like a coroutine's resume function, where the argument is
38    /// replaced by an event.
39    pub fn resume(&mut self, event: Option<Event>) -> Option<State> {
40        self.event = event;
41        self.next()
42    }
43}
44
45impl Iterator for RipStarttls {
46    type Item = State;
47
48    fn next(&mut self) -> Option<State> {
49        let event = self.event.take();
50
51        match self.state {
52            None => {
53                self.state = Some(if self.handshake_discarded {
54                    State::WriteStarttlsCommand
55                } else {
56                    State::DiscardHandshake
57                })
58            }
59            Some(State::DiscardHandshake) => {
60                if let Some(Event::HandshakeDiscarded(line)) = event {
61                    debug!("discarded IMAP greeting: {line:?}");
62                    self.state = Some(State::WriteStarttlsCommand);
63                }
64            }
65            Some(State::WriteStarttlsCommand) => {
66                if let Some(Event::StarttlsCommandWrote(_)) = event {
67                    let cmd = Self::COMMAND;
68                    debug!("wrote IMAP STARTTLS command: {cmd:?}");
69                    self.state = Some(State::DiscardResponse);
70                }
71            }
72            Some(State::DiscardResponse) => {
73                if let Some(Event::ResponseDiscarded(line)) = event {
74                    debug!("discarded IMAP response: {line:?}");
75
76                    if line.starts_with("A ") {
77                        debug!("stream ready for TLS negociation");
78                        self.state = None;
79                    }
80                }
81            }
82        }
83
84        self.state.clone()
85    }
86}
87
88#[derive(Clone, Debug, Eq, PartialEq)]
89pub enum State {
90    DiscardHandshake,
91    WriteStarttlsCommand,
92    DiscardResponse,
93}
94
95#[derive(Clone, Debug, Eq, PartialEq)]
96pub enum Event {
97    HandshakeDiscarded(String),
98    StarttlsCommandWrote(usize),
99    ResponseDiscarded(String),
100}