Skip to main content

io_smtp/rfc3461/
parameter.rs

1//! DSN ESMTP parameter constructors for MAIL FROM and RCPT TO.
2//!
3//! # Example
4//!
5//! ```rust,ignore
6//! use io_smtp::{
7//!     rfc3461::parameter::{SmtpDsnNotify, SmtpDsnRet},
8//!     rfc5321::{mail::SmtpMail, rcpt::SmtpRcpt, types::parameter::SmtpParameter},
9//! };
10//!
11//! // NOTE: MAIL FROM with RET=HDRS and ENVID
12//! let params = vec![SmtpDsnRet::Hdrs.into_parameter(), SmtpParameter::envid("abc123")];
13//! let coroutine = SmtpMail::new(reverse_path, params);
14//!
15//! // NOTE: RCPT TO with NOTIFY=SUCCESS,FAILURE
16//! let params = vec![(SmtpDsnNotify::SUCCESS | SmtpDsnNotify::FAILURE).into_parameter()];
17//! let coroutine = SmtpRcpt::new(forward_path, params);
18//! ```
19
20use core::ops::BitOr;
21
22use alloc::{borrow::Cow, string::String, vec::Vec};
23
24use crate::rfc5321::{SmtpAtom, SmtpParameter};
25
26/// The value of the `RET` ESMTP parameter on `MAIL FROM`.
27///
28/// Controls how much of the original message is included in a DSN.
29///
30/// # Reference
31///
32/// RFC 3461 §4.3
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum SmtpDsnRet {
35    /// Include the full original message in any DSN.
36    Full,
37    /// Include only the headers of the original message in any DSN.
38    Hdrs,
39}
40
41impl SmtpDsnRet {
42    /// Build the `RET=FULL` or `RET=HDRS` [`SmtpParameter`] for `MAIL FROM`.
43    pub fn into_parameter(self) -> SmtpParameter<'static> {
44        let value = match self {
45            Self::Full => "FULL",
46            Self::Hdrs => "HDRS",
47        };
48        SmtpParameter {
49            keyword: SmtpAtom(Cow::Borrowed("RET")),
50            value: Some(Cow::Borrowed(value)),
51        }
52    }
53}
54
55/// DSN parameter constructors for `MAIL FROM` and `RCPT TO`
56/// (RFC 3461).
57impl SmtpParameter<'static> {
58    /// Build the `ENVID=<id>` parameter for `MAIL FROM`.
59    ///
60    /// The envelope identifier is an opaque string chosen by the
61    /// sender that uniquely identifies this mail transaction. It is
62    /// included in any DSN generated for the message.
63    ///
64    /// The value must contain only printable US-ASCII characters
65    /// excluding `=` and whitespace (xtext encoding, RFC 3461 §4).
66    ///
67    /// # Reference
68    ///
69    /// RFC 3461 §4.4
70    pub fn envid(id: impl Into<String>) -> SmtpParameter<'static> {
71        SmtpParameter {
72            keyword: SmtpAtom(Cow::Borrowed("ENVID")),
73            value: Some(Cow::Owned(id.into())),
74        }
75    }
76}
77
78/// The `NOTIFY` conditions for a single `RCPT TO`.
79///
80/// Flags may be combined (e.g. `SmtpDsnNotify::SUCCESS |
81/// SmtpDsnNotify::FAILURE`), except that `NEVER` must not be combined
82/// with any other value.
83///
84/// # Reference
85///
86/// RFC 3461 §4.1
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub struct SmtpDsnNotify(u8);
89
90impl SmtpDsnNotify {
91    /// Never send a DSN for this recipient.
92    pub const NEVER: Self = Self(0);
93    /// Send a DSN on successful delivery.
94    pub const SUCCESS: Self = Self(1);
95    /// Send a DSN on delivery failure.
96    pub const FAILURE: Self = Self(2);
97    /// Send a DSN if delivery is delayed.
98    pub const DELAY: Self = Self(4);
99
100    /// Combine two notify flags.
101    #[must_use]
102    pub const fn or(self, other: Self) -> Self {
103        Self(self.0 | other.0)
104    }
105
106    /// Build the `NOTIFY=...` [`SmtpParameter`] for `RCPT TO`.
107    pub fn into_parameter(self) -> SmtpParameter<'static> {
108        let value = if self.0 == 0 {
109            Cow::Borrowed("NEVER")
110        } else {
111            let mut parts: Vec<&str> = Vec::new();
112            if self.0 & 1 != 0 {
113                parts.push("SUCCESS");
114            }
115            if self.0 & 2 != 0 {
116                parts.push("FAILURE");
117            }
118            if self.0 & 4 != 0 {
119                parts.push("DELAY");
120            }
121            Cow::Owned(parts.join(","))
122        };
123        SmtpParameter {
124            keyword: SmtpAtom(Cow::Borrowed("NOTIFY")),
125            value: Some(value),
126        }
127    }
128}
129
130impl BitOr for SmtpDsnNotify {
131    type Output = Self;
132    fn bitor(self, rhs: Self) -> Self {
133        Self(self.0 | rhs.0)
134    }
135}
136
137/// Original recipient parameter constructor for `RCPT TO`
138/// (RFC 3461).
139impl SmtpParameter<'static> {
140    /// Build the `ORCPT=rfc822;<address>` parameter for `RCPT TO`.
141    ///
142    /// Specifies the original recipient address, before any aliasing
143    /// or forwarding, so that DSNs can reference it.
144    ///
145    /// # Reference
146    ///
147    /// RFC 3461 §4.2
148    pub fn orcpt_rfc822(address: impl Into<String>) -> SmtpParameter<'static> {
149        let value = {
150            let mut s = String::from("rfc822;");
151            s.push_str(&address.into());
152            s
153        };
154        SmtpParameter {
155            keyword: SmtpAtom(Cow::Borrowed("ORCPT")),
156            value: Some(Cow::Owned(value)),
157        }
158    }
159}