1use smtp_proto::{
12 response::parser::{ResponseReceiver, MAX_RESPONSE_LENGTH},
13 EhloResponse,
14};
15use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
16
17use crate::SmtpClient;
18
19impl<T: AsyncRead + AsyncWrite + Unpin> SmtpClient<T> {
20 pub async fn ehlo(&mut self, hostname: &str) -> crate::Result<EhloResponse<String>> {
22 tokio::time::timeout(self.timeout, async {
23 self.stream
24 .write_all(format!("EHLO {hostname}\r\n").as_bytes())
25 .await?;
26 self.stream.flush().await?;
27 self.read_ehlo().await
28 })
29 .await
30 .map_err(|_| crate::Error::Timeout)?
31 }
32
33 pub async fn lhlo(&mut self, hostname: &str) -> crate::Result<EhloResponse<String>> {
35 tokio::time::timeout(self.timeout, async {
36 self.stream
37 .write_all(format!("LHLO {hostname}\r\n").as_bytes())
38 .await?;
39 self.stream.flush().await?;
40 self.read_ehlo().await
41 })
42 .await
43 .map_err(|_| crate::Error::Timeout)?
44 }
45
46 pub async fn read_ehlo(&mut self) -> crate::Result<EhloResponse<String>> {
47 let mut buf = vec![0u8; 1024];
48 let mut buf_concat = Vec::with_capacity(0);
49
50 loop {
51 let br = self.stream.read(&mut buf).await?;
52
53 if br == 0 {
54 return Err(crate::Error::UnparseableReply);
55 }
56 let mut iter = if buf_concat.is_empty() {
57 buf[..br].iter()
58 } else if br + buf_concat.len() < MAX_RESPONSE_LENGTH {
59 buf_concat.extend_from_slice(&buf[..br]);
60 buf_concat.iter()
61 } else {
62 return Err(crate::Error::UnparseableReply);
63 };
64
65 match EhloResponse::parse(&mut iter) {
66 Ok(reply) => return Ok(reply),
67 Err(err) => match err {
68 smtp_proto::Error::NeedsMoreData { .. } => {
69 if buf_concat.is_empty() {
70 buf_concat = buf[..br].to_vec();
71 }
72 }
73 smtp_proto::Error::InvalidResponse { code } => {
74 match ResponseReceiver::from_code(code).parse(&mut iter) {
75 Ok(response) => {
76 return Err(crate::Error::UnexpectedReply(response));
77 }
78 Err(smtp_proto::Error::NeedsMoreData { .. }) => {
79 if buf_concat.is_empty() {
80 buf_concat = buf[..br].to_vec();
81 }
82 }
83 Err(_) => return Err(crate::Error::UnparseableReply),
84 }
85 }
86 _ => {
87 return Err(crate::Error::UnparseableReply);
88 }
89 },
90 }
91 }
92 }
93}