use bytes::{Buf, BytesMut};
use mailparse::MailHeaderMap;
use std::fmt::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)]
pub enum Frame {
Header,
Raw(String),
Ehlo(String),
From(String),
To(String),
Ok(String),
DataStart,
DataEnd,
Quit,
StartMailInput,
Close,
}
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))
}
}
pub struct Connection {
stream: BufWriter,
buffer: BytesMut,
mail_from: String,
rcpt_to: String,
data: String,
state: ConnectionState,
}
impl Connection {
pub fn new(socket: TcpStream) -> Connection {
Connection {
stream: BufWriter::new(socket),
buffer: BytesMut::with_capacity(4 * 1024),
state: ConnectionState::Commands,
mail_from: String::default(),
rcpt_to: String::default(),
data: String::default(),
}
}
pub async fn read_frame(&mut self) -> anyhow::Result