1use core::{fmt, marker::PhantomData, mem};
6
7use alloc::{string::String, vec::Vec};
8
9use bounded_static::IntoBoundedStatic;
10use log::trace;
11use thiserror::Error;
12
13use crate::{
14 coroutine::*,
15 rfc5321::types::response::Response,
16 utils::{escape_byte_string, parsers::format_rich_errors},
17};
18
19#[derive(Clone, Debug, Error)]
21pub enum SendSmtpCommandError {
22 #[error("Reached unexpected EOF on SMTP stream")]
23 Eof,
24 #[error("Parse SMTP response error: {0}")]
25 ParseResponse(String),
26}
27
28pub struct SendSmtpCommandOk {
30 pub response: Response<'static>,
32}
33
34enum State {
35 Write,
36 Read,
37 Parse,
38}
39
40impl fmt::Display for State {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::Write => f.write_str("write command"),
44 Self::Read => f.write_str("read response"),
45 Self::Parse => f.write_str("parse response"),
46 }
47 }
48}
49
50pub struct SendSmtpCommand<Cmd> {
54 bytes: Option<Vec<u8>>,
55 state: State,
56 wants_read: bool,
57 buf: Vec<u8>,
58 _cmd: PhantomData<Cmd>,
59}
60
61impl<Cmd: Into<Vec<u8>>> SendSmtpCommand<Cmd> {
62 pub fn new(cmd: Cmd) -> Self {
63 Self {
64 bytes: Some(cmd.into()),
65 state: State::Write,
66 wants_read: false,
67 buf: Vec::new(),
68 _cmd: PhantomData,
69 }
70 }
71}
72
73impl<Cmd> SmtpCoroutine for SendSmtpCommand<Cmd> {
74 type Yield = SmtpYield;
75 type Return = Result<SendSmtpCommandOk, SendSmtpCommandError>;
76
77 fn resume(&mut self, mut arg: Option<&[u8]>) -> SmtpCoroutineState<Self::Yield, Self::Return> {
78 loop {
79 trace!("send: {}", self.state);
80
81 if mem::take(&mut self.wants_read) {
82 return SmtpCoroutineState::Yielded(SmtpYield::WantsRead);
83 }
84
85 match &mut self.state {
86 State::Write => {
87 let bytes = self.bytes.take().expect("command bytes taken twice");
88 self.state = State::Read;
89 return SmtpCoroutineState::Yielded(SmtpYield::WantsWrite(bytes));
90 }
91 State::Read => match arg.take() {
92 Some(&[]) => {
93 return SmtpCoroutineState::Complete(Err(SendSmtpCommandError::Eof));
94 }
95 Some(data) => {
96 trace!("read SMTP bytes: {}", escape_byte_string(data));
97 self.buf.extend_from_slice(data);
98
99 if !Response::is_complete(&self.buf) {
100 self.wants_read = true;
101 continue;
102 }
103
104 self.state = State::Parse;
105 }
106 None => {
107 self.wants_read = true;
108 }
109 },
110 State::Parse => {
111 return match Response::parse(&self.buf) {
112 Ok(response) => {
113 let response = response.into_static();
114 let _ = mem::take(&mut self.buf);
115 SmtpCoroutineState::Complete(Ok(SendSmtpCommandOk { response }))
116 }
117 Err(errors) => {
118 let reason = format_rich_errors(errors);
119 let err = SendSmtpCommandError::ParseResponse(reason);
120 SmtpCoroutineState::Complete(Err(err))
121 }
122 };
123 }
124 }
125 }
126 }
127}