use crate::{
receiver::ReceiverContext, smtp_sasl::CallbackWrap, AcceptArgs, AuthArgs, AuthError, EhloArgs,
Error, HeloArgs, MailFromArgs, ParseArgsError, RcptToArgs, UnparsedArgs, Verb,
};
use tokio_rustls::rustls;
use vsmtp_common::ContextFinished;
use vsmtp_common::{Reply, Stage};
use vsmtp_mail_parser::MessageBody;
#[async_trait::async_trait]
pub trait ReceiverHandler {
fn get_stage(&self) -> Stage;
fn generate_sasl_callback(&self) -> CallbackWrap;
async fn on_accept(&mut self, ctx: &mut ReceiverContext, args: AcceptArgs) -> Reply;
async fn on_starttls(&mut self, ctx: &mut ReceiverContext) -> Reply;
async fn on_post_tls_handshake(
&mut self,
sni: Option<String>,
protocol_version: rustls::ProtocolVersion,
cipher_suite: rustls::CipherSuite,
peer_certificates: Option<Vec<rustls::Certificate>>,
alpn_protocol: Option<Vec<u8>>,
) -> Reply;
async fn on_auth(&mut self, ctx: &mut ReceiverContext, args: AuthArgs) -> Option<Reply>;
async fn on_post_auth(
&mut self,
ctx: &mut ReceiverContext,
result: Result<(), AuthError>,
) -> Reply;
async fn on_helo(&mut self, ctx: &mut ReceiverContext, args: HeloArgs) -> Reply;
async fn on_ehlo(&mut self, ctx: &mut ReceiverContext, args: EhloArgs) -> Reply;
async fn on_mail_from(&mut self, ctx: &mut ReceiverContext, args: MailFromArgs) -> Reply;
async fn on_rcpt_to(&mut self, ctx: &mut ReceiverContext, args: RcptToArgs) -> Reply;
async fn on_message(
&mut self,
ctx: &mut ReceiverContext,
stream: impl tokio_stream::Stream<Item = Result<Vec<u8>, Error>> + Send + Unpin,
) -> (Reply, Option<Vec<(ContextFinished, MessageBody)>>);
async fn on_message_completed(
&mut self,
ctx: ContextFinished,
msg: MessageBody,
) -> Option<Reply>;
async fn on_hard_error(&mut self, ctx: &mut ReceiverContext, reply: Reply) -> Reply;
async fn on_soft_error(&mut self, ctx: &mut ReceiverContext, reply: Reply) -> Reply;
async fn on_rset(&mut self) -> Reply;
#[inline]
async fn on_data(&mut self) -> Reply {
#[allow(clippy::expect_used)]
"354 Start mail input; end with <CRLF>.<CRLF>\r\n"
.parse()
.expect("valid syntax")
}
#[inline]
async fn on_quit(&mut self) -> Reply {
#[allow(clippy::expect_used)]
"221 Service closing transmission channel"
.parse()
.expect("valid syntax")
}
#[inline]
async fn on_noop(&mut self) -> Reply {
#[allow(clippy::expect_used)]
"250 Ok\r\n".parse().expect("valid syntax")
}
#[inline]
async fn on_help(&mut self, _: UnparsedArgs) -> Reply {
#[allow(clippy::expect_used)]
"214 joining us https://viridit.com/support"
.parse()
.expect("valid syntax")
}
#[inline]
async fn on_unknown(&mut self, buffer: Vec<u8>) -> Reply {
let unimplemented_command = [b"VRFY".as_slice(), b"EXPN".as_slice(), b"TURN".as_slice()];
#[allow(clippy::expect_used)]
if unimplemented_command.iter().any(|c| {
buffer.len() >= c.len()
&& buffer
.get(..c.len())
.expect("range checked before")
.eq_ignore_ascii_case(c)
}) {
"502 Command not implemented\r\n"
.parse()
.expect("valid syntax")
} else {
"500 Syntax error command unrecognized\r\n"
.parse()
.expect("valid syntax")
}
}
#[inline]
async fn on_bad_sequence(&mut self, _: (Verb, Stage)) -> Reply {
#[allow(clippy::expect_used)]
"503 Bad sequence of commands\r\n"
.parse()
.expect("valid syntax")
}
#[inline]
async fn on_args_error(&mut self, _: ParseArgsError) -> Reply {
#[allow(clippy::expect_used)]
"501 Syntax error in parameters or arguments\r\n"
.parse()
.expect("valid syntax")
}
}