Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_net/src/smtp/handler.rs

2.8 KiB, 58 runs

created by r1870400018:597, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Application-level hooks the SMTP server calls on accepted messages.
2//!
3//! A `SmtpHandler` decides what to do with a fully-received RFC 5322
4//! message after the server has already enforced the protocol: receive
5//! path delivery to a local mailbox, submission path enqueue for outbound
6//! delivery, etc. The trait is split into two methods so the same handler
7//! type can serve both ports with different policies.
8//!
9//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
10//! Anthropic Claude
11
12use crate::mail::store::MailUser;
13
14use oxedyne_fe2o3_core::prelude::*;
15
16use std::net::SocketAddr;
17
18
19/// The server fills this in from the commands received before `DATA`, then
20/// hands it to a `SmtpHandler` with the raw message bytes.
21#[derive(Clone, Debug)]
22pub struct SmtpTransaction {
23 pub mail_from: String, // empty is the null reverse-path `<>`, RFC 5321
24 pub rcpt_to: Vec<String>, // in the order received
25 pub helo_domain: String,
26 pub auth_user: Option<MailUser>, // None on the port 25 receive path
27 pub peer: SocketAddr,
28 pub tls: bool, // implicit TLS or an in-session STARTTLS upgrade
29 // Dot-unstuffed and CRLF-preserving, without the terminating `<CRLF>.<CRLF>`.
30 pub raw_message: Vec<u8>,
31}
32
33/// The queue id in `Accepted` reaches the client on the `250 OK` line, so an
34/// administrator can grep the logs by it.
35#[derive(Clone, Debug)]
36pub enum HandlerOutcome {
37 Accepted(String), // queue id, echoed in the `250`
38 RejectPermanent(String), // reason, returned in a `550`
39 RejectTemporary(String), // reason, returned in a `451`
40}
41
42/// Implementations are expected to be cheap to clone, typically via an internal
43/// `Arc`, so one handler serves every accept loop without contention. Both
44/// methods are synchronous: the server calls them inside a
45/// `tokio::task::spawn_blocking` so the underlying I/O does not block the
46/// runtime.
47pub trait SmtpHandler: Clone + Send + Sync + 'static {
48 /// The port 25 path: deliver into every local mailbox `rcpt_to` names. A
49 /// recipient that does not resolve locally is refused at `RCPT` time, so
50 /// by here every recipient has already been accepted.
51 fn deliver_inbound(&self, txn: SmtpTransaction) -> Outcome<HandlerOutcome>;
52
53 /// The port 587 path: enqueue for outbound delivery, typically through
54 /// `crate::smtp::client::OutboundClient`, after DKIM signing where a key
55 /// is configured.
56 fn submit_outbound(&self, txn: SmtpTransaction) -> Outcome<HandlerOutcome>;
57
58 /// Will this `RCPT TO` be taken? Asked on the receive path only:
59 /// submission listeners skip the check, since an authenticated client may
60 /// relay anywhere.
61 fn rcpt_acceptable(&self, address: &str) -> bool;
62}