use std::{borrow::Cow, fmt::Write as _, str::FromStr};
use smtp_proto::{
RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, RcptTo,
};
use crate::{
network::AsyncReadWrite,
smtp::{
RecipientPolicy, RecipientResolveError,
address::EmailAddress,
inbound::{Session, SessionResult},
},
};
impl<S: AsyncReadWrite> Session<S> {
pub async fn handle_rcpt_to(&mut self, to: RcptTo<Cow<'_, str>>) -> SessionResult<()> {
let Some(mail_from) = &self.data.mail_from else {
return self
.reply("503", "5.5.1", "MAIL FROM is required first.")
.await;
};
if (to.flags
& (RCPT_NOTIFY_DELAY | RCPT_NOTIFY_NEVER | RCPT_NOTIFY_SUCCESS | RCPT_NOTIFY_FAILURE))
!= 0
|| to.orcpt.is_some()
{
return self.ext_unsupported("DSN").await;
}
let Ok(address) = EmailAddress::from_str(&to.address) else {
return self.reply("550", "5.1.2", "Incorrect address.").await;
};
if self.data.rcpt_to.contains(&address) {
return self.reply("250", "2.1.5", "OK").await;
}
if self.data.rcpt_to.len() >= self.cfg.max_recipients {
return self.reply("455", "4.5.3", "Too many recipients.").await;
}
match self
.cfg
.recipient_resolver
.resolve_recipient(mail_from, &address)
.await
{
Ok(v) => match v {
RecipientPolicy::Accept => {
self.data.rcpt_to.push(address);
}
RecipientPolicy::Rewrite(new_address) => {
self.data.rcpt_to.push(new_address);
}
RecipientPolicy::Expand(additional_addresses) => {
self.data.rcpt_to.push(address);
self.data.rcpt_to.extend(additional_addresses);
}
},
Err(e) => {
return match e {
RecipientResolveError::UnknownDomain => {
self.reply("550", "5.1.1", "Unknown recipient domain.")
.await
}
RecipientResolveError::UnknownRecipient => {
self.reply("550", "5.1.2", "Mailbox does not exist.").await
}
RecipientResolveError::Temporary(v) => {
self.reply_with("451", "4.4.3", |buf| write!(buf, "Temporary error: {v}"))
.await
}
RecipientResolveError::Permanent(v) => {
self.reply_with("550", "5.1.3", |buf| write!(buf, "Permanent error: {v}"))
.await
}
};
}
}
self.reply("250", "2.1.5", "OK").await
}
}