io_smtp/rfc5321/
greeting.rs1use core::{fmt, mem};
43
44use alloc::{string::String, vec::Vec};
45
46use bounded_static::IntoBoundedStatic;
47use log::{debug, trace};
48use thiserror::Error;
49
50use crate::{
51 coroutine::*,
52 rfc5321::SmtpGreeting,
53 utils::{escape_byte_string, parsers::format_rich_errors},
54};
55
56#[derive(Clone, Debug, Error)]
58pub enum SmtpGreetingGetError {
59 #[error("SMTP greeting failed: reached unexpected EOF on stream")]
61 Eof,
62 #[error("SMTP greeting failed: parse error: {0}")]
64 ParseResponse(String),
65}
66
67pub struct SmtpGreetingGet {
69 state: State,
70 wants_read: bool,
71 buf: Vec<u8>,
72}
73
74impl SmtpGreetingGet {
75 pub fn new() -> Self {
77 Self {
78 state: State::Read,
79 wants_read: false,
80 buf: Vec::new(),
81 }
82 }
83}
84
85impl Default for SmtpGreetingGet {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl SmtpCoroutine for SmtpGreetingGet {
92 type Yield = SmtpYield;
93 type Return = Result<SmtpGreeting<'static>, SmtpGreetingGetError>;
94
95 fn resume(&mut self, mut arg: Option<&[u8]>) -> SmtpCoroutineState<Self::Yield, Self::Return> {
96 loop {
97 if mem::take(&mut self.wants_read) {
98 return SmtpCoroutineState::Yielded(SmtpYield::WantsRead);
99 }
100
101 match &mut self.state {
102 State::Read => match arg.take() {
103 Some(&[]) => {
104 return SmtpCoroutineState::Complete(Err(SmtpGreetingGetError::Eof));
105 }
106 Some(data) => {
107 trace!("read bytes: {}", escape_byte_string(data));
108 self.buf.extend_from_slice(data);
109
110 if !SmtpGreeting::is_complete(&self.buf) {
111 self.wants_read = true;
112 continue;
113 }
114
115 self.state = State::Parse;
116 debug!("greeting complete, parsing");
117 }
118 None => {
119 self.wants_read = true;
120 }
121 },
122 State::Parse => {
123 return match SmtpGreeting::parse(&self.buf) {
124 Ok(greeting) => {
125 let greeting = greeting.into_static();
126 let _ = mem::take(&mut self.buf);
127 debug!("greeting parsed");
128 trace!("{greeting:?}");
129 SmtpCoroutineState::Complete(Ok(greeting))
130 }
131 Err(errors) => {
132 let reason = format_rich_errors(errors);
133 SmtpCoroutineState::Complete(Err(SmtpGreetingGetError::ParseResponse(
134 reason,
135 )))
136 }
137 };
138 }
139 }
140 }
141 }
142}
143
144enum State {
145 Read,
146 Parse,
147}
148
149impl fmt::Display for State {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 match self {
152 Self::Read => f.write_str("read greeting"),
153 Self::Parse => f.write_str("parse greeting"),
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use crate::{coroutine::*, rfc5321::greeting::*};
161
162 #[test]
163 fn single_line_success_returns_ok() {
164 let mut greeting = SmtpGreetingGet::new();
165 expect_wants_read(&mut greeting);
166
167 let g = expect_complete_ok(&mut greeting, b"220 server.example.com ready\r\n");
168 assert_eq!(g.domain.0.as_ref(), "server.example.com");
169 }
170
171 #[test]
172 fn multi_line_success_returns_ok() {
173 let mut greeting = SmtpGreetingGet::new();
174 expect_wants_read(&mut greeting);
175
176 let reply = b"220-server.example.com hello\r\n220-extra info\r\n220 ready\r\n";
177 let g = expect_complete_ok(&mut greeting, reply);
178 assert_eq!(g.domain.0.as_ref(), "server.example.com");
179 }
180
181 #[test]
182 fn incomplete_greeting_re_yields_read() {
183 let mut greeting = SmtpGreetingGet::new();
184 expect_wants_read(&mut greeting);
185
186 match greeting.resume(Some(b"220 server.example.com")) {
188 SmtpCoroutineState::Yielded(SmtpYield::WantsRead) => {}
189 state => panic!("expected WantsRead, got {state:?}"),
190 }
191 }
192
193 #[test]
194 fn parse_error_returns_parse_error() {
195 let mut greeting = SmtpGreetingGet::new();
196 expect_wants_read(&mut greeting);
197
198 let err = expect_complete_err(&mut greeting, b"250 wrong code\r\n");
200 assert!(matches!(err, SmtpGreetingGetError::ParseResponse(_)));
201 }
202
203 #[test]
204 fn eof_returns_eof_error() {
205 let mut greeting = SmtpGreetingGet::new();
206 expect_wants_read(&mut greeting);
207
208 let err = expect_complete_err(&mut greeting, b"");
209 assert!(matches!(err, SmtpGreetingGetError::Eof));
210 }
211
212 fn expect_wants_read(cor: &mut SmtpGreetingGet) {
213 match cor.resume(None) {
214 SmtpCoroutineState::Yielded(SmtpYield::WantsRead) => {}
215 state => panic!("expected WantsRead, got {state:?}"),
216 }
217 }
218
219 fn expect_complete_ok(cor: &mut SmtpGreetingGet, reply: &[u8]) -> SmtpGreeting<'static> {
220 match cor.resume(Some(reply)) {
221 SmtpCoroutineState::Complete(Ok(value)) => value,
222 state => panic!("expected Complete(Ok), got {state:?}"),
223 }
224 }
225
226 fn expect_complete_err(cor: &mut SmtpGreetingGet, reply: &[u8]) -> SmtpGreetingGetError {
227 match cor.resume(Some(reply)) {
228 SmtpCoroutineState::Complete(Err(err)) => err,
229 state => panic!("expected Complete(Err), got {state:?}"),
230 }
231 }
232}