use bytes::{Buf, BytesMut};
use mailparse::MailHeaderMap;
use std::fmt::{Display, Write as _};
use std::io::Cursor;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt, BufWriter},
net::{TcpListener, TcpStream},
};
use crate::mail::{Mail, MailPart, Mailbox};
#[derive(PartialEq, Eq)]
pub enum ConnectionState {
Commands,
Data,
}
#[derive(Debug, Clone)]
pub enum Frame {
Header,
Raw(String),
Ehlo(String),
From(String),
To(String),
Ok(String),
DataStart,
DataEnd,
Quit,
StartMailInput,
Close,
}
impl Display for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Frame::Ok(v) => write!(f, "250 {}\r\n", v),
Frame::Raw(v) => write!(f, "{}\r\n", v),
Frame::Close => write!(f, "221 Closing connection\r\n"),
Frame::Header => write!(f, "220 Mailspy Test Server\r\n"),
Frame::StartMailInput => write!(f, "354 Start mail input\r\n"),
_ => Ok(()),
}
}
}
impl Frame {
pub fn check(buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> {
get_line(buf)?;
Ok(())
}
pub fn parse(buf: &mut Cursor<&[u8]>) -> anyhow::Result {
let line = get_line(buf)?.to_vec();
let string = String::from_utf8(line)?;
if string.to_lowercase().starts_with("ehlo") {
return Ok(Frame::Ehlo(string[3..].to_string()));
}
if string.to_lowercase().starts_with("mail from:") {
return Ok(Frame::From(string[9..].to_string()));
}
if string.to_lowercase().starts_with("rcpt to:") {
return Ok(Frame::To(string[8..].to_string()));
}
if string.to_lowercase().starts_with("data") {
return Ok(Frame::DataStart);
}
if string.to_lowercase().starts_with("quit") {
return Ok(Frame::Quit);
}
if string.to_lowercase().trim().starts_with('.') {
return Ok(Frame::DataEnd);
}
Ok(Frame::Raw(string))
}
}
#[async_trait::async_trait]
pub trait Transmitter {
async fn read_frame(&mut self) -> anyhow::Result