use crate::escape;
use std::io::Write;
#[derive(Clone, PartialEq, Eq)]
pub enum Command {
Ehlo {
fqdn_or_address_literal: Vec<u8>,
},
Helo {
fqdn_or_address_literal: Vec<u8>,
},
Mail {
reverse_path: Vec<u8>,
parameters: Option<Vec<u8>>,
},
Rcpt {
forward_path: Vec<u8>,
parameters: Option<Vec<u8>>,
},
Data,
Rset,
Vrfy {
user_or_mailbox: Vec<u8>,
},
Expn {
mailing_list: Vec<u8>,
},
Help {
argument: Option<Vec<u8>>,
},
Noop {
argument: Option<Vec<u8>>,
},
Quit,
StartTLS,
AuthLogin(Option<String>),
AuthPlain(Option<String>),
}
impl Command {
pub fn name(&self) -> &'static str {
match self {
Command::Ehlo { .. } => "EHLO",
Command::Helo { .. } => "HELO",
Command::Mail { .. } => "MAIL",
Command::Rcpt { .. } => "RCPT",
Command::Data => "DATA",
Command::Rset => "RSET",
Command::Vrfy { .. } => "VRFY",
Command::Expn { .. } => "EXPN",
Command::Help { .. } => "HELP",
Command::Noop { .. } => "NOOP",
Command::Quit => "QUIT",
Command::StartTLS => "STARTTLS",
Command::AuthLogin(_) => "AUTHLOGIN",
Command::AuthPlain(_) => "AUTHPLAIN",
}
}
}
impl std::fmt::Debug for Command {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use Command::*;
match self {
Ehlo {
fqdn_or_address_literal,
} => write!(f, "Ehlo({})", escape(fqdn_or_address_literal)),
Helo {
fqdn_or_address_literal,
} => write!(f, "Helo({})", escape(fqdn_or_address_literal)),
Mail {
reverse_path: path,
parameters: None,
} => write!(f, "Mail({})", escape(path)),
Mail {
reverse_path: path,
parameters: Some(params),
} => write!(f, "Mail({}, {})", escape(path), escape(params)),
Rcpt {
forward_path: data,
parameters: None,
} => write!(f, "Rcpt({})", escape(data)),
Rcpt {
forward_path: data,
parameters: Some(params),
} => write!(f, "Rcpt({}, {})", escape(data), escape(params)),
Data => write!(f, "Data"),
Rset => write!(f, "Rset"),
Vrfy { user_or_mailbox } => write!(f, "Vrfy({})", escape(user_or_mailbox)),
Expn { mailing_list } => write!(f, "Expn({})", escape(mailing_list)),
Help { argument: None } => write!(f, "Help"),
Help {
argument: Some(data),
} => write!(f, "Help({})", escape(data)),
Noop { argument: None } => write!(f, "Noop"),
Noop {
argument: Some(data),
} => write!(f, "Noop({})", escape(data)),
Quit => write!(f, "Quit"),
StartTLS => write!(f, "StartTLS"),
AuthLogin(data) => write!(f, "AuthLogin({:?})", data),
AuthPlain(data) => write!(f, "AuthPlain({:?})", data),
}
}
}
impl Command {
pub fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
use Command::*;
match self {
Helo {
fqdn_or_address_literal,
} => {
writer.write_all(b"HELO ")?;
writer.write_all(fqdn_or_address_literal)?;
}
Ehlo {
fqdn_or_address_literal,
} => {
writer.write_all(b"EHLO ")?;
writer.write_all(fqdn_or_address_literal)?;
}
Mail {
reverse_path,
parameters: None,
} => {
writer.write_all(b"MAIL FROM:<")?;
writer.write_all(reverse_path)?;
writer.write_all(b">")?;
}
Mail {
reverse_path,
parameters: Some(parameters),
} => {
writer.write_all(b"MAIL FROM:<")?;
writer.write_all(reverse_path)?;
writer.write_all(b"> ")?;
writer.write_all(parameters)?;
}
Rcpt {
forward_path,
parameters: None,
} => {
writer.write_all(b"RCPT TO:<")?;
writer.write_all(forward_path)?;
writer.write_all(b">")?;
}
Rcpt {
forward_path,
parameters: Some(parameters),
} => {
writer.write_all(b"RCPT TO:<")?;
writer.write_all(forward_path)?;
writer.write_all(b"> ")?;
writer.write_all(parameters)?;
}
Data => writer.write_all(b"DATA")?,
Rset => writer.write_all(b"RSET")?,
Vrfy { user_or_mailbox } => {
writer.write_all(b"VRFY ")?;
writer.write_all(user_or_mailbox)?;
}
Expn { mailing_list } => {
writer.write_all(b"EXPN ")?;
writer.write_all(mailing_list)?;
}
Help { argument: None } => writer.write_all(b"HELP")?,
Help {
argument: Some(data),
} => {
writer.write_all(b"HELP ")?;
writer.write_all(data)?;
}
Noop { argument: None } => writer.write_all(b"NOOP")?,
Noop {
argument: Some(data),
} => {
writer.write_all(b"NOOP ")?;
writer.write_all(data)?;
}
Quit => writer.write_all(b"QUIT")?,
StartTLS => writer.write_all(b"STARTTLS")?,
AuthLogin(None) => {
writer.write_all(b"AUTH LOGIN")?;
}
AuthLogin(Some(data)) => {
writer.write_all(b"AUTH LOGIN ")?;
writer.write_all(data.as_bytes())?;
}
AuthPlain(None) => {
writer.write_all(b"AUTH PLAIN")?;
}
AuthPlain(Some(data)) => {
writer.write_all(b"AUTH PLAIN ")?;
writer.write_all(data.as_bytes())?;
}
}
write!(writer, "\r\n")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Greeting {
pub domain: String,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EhloOkResp {
pub domain: String,
pub greet: Option<String>,
pub lines: Vec<EhloLine>,
}
pub type EhloLine = (String, Vec<String>);