use crate::smtp::SmtpReply::*;
use std::fmt;
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum SmtpReply {
None,
CommandSyntaxFailure,
ParameterSyntaxFailure,
CommandNotImplementedFailure,
CommandSequenceFailure,
UnexpectedParameterFailure,
StatusInfo(String),
HelpInfo(String),
ServiceReadyInfo(String),
ClosingConnectionInfo(String),
ServiceNotAvailableError(String),
MailNotAcceptedByHostFailure,
OkInfo,
OkMessageInfo(String),
OkHeloInfo {
local: String,
remote: String,
extensions: Vec<String>,
},
UserNotLocalInfo(String),
CannotVerifyUserInfo,
StartMailInputChallenge,
MailboxNotAvailableError,
ProcesingError,
StorageError,
ParametersNotAccommodatedError,
MailboxNotAvailableFailure,
UserNotLocalFailure(String),
StorageFailure,
MailboxNameInvalidFailure,
TransactionFailure,
UnknownMailParametersFailure,
MailNotAcceptedByDomainFailure,
}
impl SmtpReply {
pub fn code(&self) -> u16 {
match *self {
None => 0,
CommandSyntaxFailure => 500,
ParameterSyntaxFailure => 501,
CommandNotImplementedFailure => 502,
CommandSequenceFailure => 503,
UnexpectedParameterFailure => 504,
StatusInfo(_) => 211,
HelpInfo(_) => 214,
ServiceReadyInfo(_) => 220,
ClosingConnectionInfo(_) => 221,
ServiceNotAvailableError(_) => 421,
MailNotAcceptedByHostFailure => 521,
OkInfo => 250,
OkMessageInfo(_) => 250,
OkHeloInfo { .. } => 250,
UserNotLocalInfo(_) => 251,
CannotVerifyUserInfo => 252,
StartMailInputChallenge => 354,
MailboxNotAvailableError => 450,
ProcesingError => 451,
StorageError => 452,
ParametersNotAccommodatedError => 455,
MailboxNotAvailableFailure => 550,
UserNotLocalFailure(_) => 551,
StorageFailure => 552,
MailboxNameInvalidFailure => 553,
TransactionFailure => 554,
UnknownMailParametersFailure => 555,
MailNotAcceptedByDomainFailure => 556,
}
}
pub fn text(&self) -> String {
match *self {
None => "".to_owned(),
CommandSyntaxFailure => "Syntax error, command unrecognized".to_owned(),
ParameterSyntaxFailure => "Syntax error in parameters or arguments".to_owned(),
CommandNotImplementedFailure => "Command not implemented".to_owned(),
CommandSequenceFailure => "Bad sequence of commands".to_owned(),
UnexpectedParameterFailure => "Command parameter not implemented".to_owned(),
StatusInfo(ref text) => text.to_string(),
HelpInfo(ref text) => text.to_string(),
ServiceReadyInfo(ref domain) => format!("Service ready: {}", domain),
ClosingConnectionInfo(ref domain) => {
format!("{} Service closing transmission channel", domain)
}
ServiceNotAvailableError(ref domain) => format!(
"{} Service not available, closing transmission channel",
domain
),
MailNotAcceptedByHostFailure => "Host does not accept mail".to_owned(),
OkInfo => "Ok".to_owned(),
OkMessageInfo(ref text) => text.to_string(),
OkHeloInfo {
ref local,
ref remote,
..
} => format!("{} greets {}", local, remote),
UserNotLocalInfo(ref forward_path) => {
format!("User not local, will forward to {}", forward_path)
}
CannotVerifyUserInfo => {
"Cannot VFRY user, but will accept message and attempt delivery".to_owned()
}
StartMailInputChallenge => "Start mail input, end with <CRLF>.<CRLF>".to_owned(),
MailboxNotAvailableError => {
"Requested mail action not taken: mailbox unavailable".to_owned()
}
ProcesingError => "Requested action aborted: error in processing".to_owned(),
StorageError => "Requested action not taken: insufficient system storage".to_owned(),
ParametersNotAccommodatedError => "Server unable to accommodate parameters".to_owned(),
MailboxNotAvailableFailure => {
"Requested action not taken: mailbox unavailable".to_owned()
}
UserNotLocalFailure(ref forward_path) => {
format!("User not local; please try {}", forward_path)
}
StorageFailure => {
"Requested mail action aborted: exceeded storage allocation".to_owned()
}
MailboxNameInvalidFailure => {
"Requested action not taken: mailbox name not allowed".to_owned()
}
TransactionFailure => "Transaction failed".to_owned(),
UnknownMailParametersFailure => {
"MAIL FROM/RCPT TO parameters not recognized or not implemented".to_owned()
}
MailNotAcceptedByDomainFailure => "Domain does not accept mail".to_owned(),
}
}
pub fn items(&self) -> Vec<String> {
match *self {
OkHeloInfo { ref extensions, .. } => extensions.iter().map(|e| e.to_string()).collect(),
_ => vec![],
}
}
pub fn class(&self) -> SmtpReplyClass {
match self.code() {
0..=299 => SmtpReplyClass::Info,
300..=399 => SmtpReplyClass::Challenge,
400..=499 => SmtpReplyClass::Error,
_ => SmtpReplyClass::Failure,
}
}
pub fn category(&self) -> SmtpReplyCategory {
match self.code() % 100 {
0..=9 => SmtpReplyCategory::Syntax,
10..=19 => SmtpReplyCategory::Information,
20..=29 => SmtpReplyCategory::Connections,
30..=39 => SmtpReplyCategory::Reserved3,
40..=49 => SmtpReplyCategory::Reserved4,
_ => SmtpReplyCategory::System,
}
}
pub fn digit(&self) -> SmtpReplyDigit {
match self.code() % 10 {
0 => SmtpReplyDigit::D0,
1 => SmtpReplyDigit::D1,
2 => SmtpReplyDigit::D2,
3 => SmtpReplyDigit::D3,
4 => SmtpReplyDigit::D4,
5 => SmtpReplyDigit::D5,
6 => SmtpReplyDigit::D6,
7 => SmtpReplyDigit::D7,
8 => SmtpReplyDigit::D8,
_ => SmtpReplyDigit::D9,
}
}
}
impl fmt::Display for SmtpReply {
fn fmt<'a>(&self, mut buf: &'a mut fmt::Formatter) -> Result<(), fmt::Error> {
let code = self.code();
let text = self.text();
let items = self.items();
if items.is_empty() {
write_reply_end(&mut buf, code, &text)?;
} else {
write_reply_continued(&mut buf, code, &text)?;
for i in 0..items.len() {
if i == items.len() - 1 {
write_reply_end(&mut buf, code, &items[i])?;
} else {
write_reply_continued(&mut buf, code, &items[i])?;
}
}
}
Ok(())
}
}
fn write_reply_end(buf: &mut dyn fmt::Write, code: u16, text: &str) -> Result<(), fmt::Error> {
write!(buf, "{} {}\r\n", code, text)
}
fn write_reply_continued(
buf: &mut dyn fmt::Write,
code: u16,
text: &str,
) -> Result<(), fmt::Error> {
write!(buf, "{}-{}\r\n", code, text)
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SmtpReplyClass {
Info = 200,
Challenge = 300,
Error = 400,
Failure = 500,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SmtpReplyCategory {
Syntax = 0,
Information = 10,
Connections = 20,
Reserved3 = 30,
Reserved4 = 40,
System = 50,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum SmtpReplyDigit {
D0 = 0,
D1 = 1,
D2 = 2,
D3 = 3,
D4 = 4,
D5 = 5,
D6 = 6,
D7 = 7,
D8 = 8,
D9 = 9,
}