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