1#[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#[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 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}