Skip to content

Commit d932c74

Browse files
committed
Refactor message: send_msg(), sign()
1 parent 962518a commit d932c74

File tree

3 files changed

+87
-29
lines changed

3 files changed

+87
-29
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
mail-send unreleased
2+
================================
3+
- New `send_msg(&mut self, message: &Message<'x>)`
4+
- New `sign(&mut self, signer: &DkimSigner<_>)` method on `Message`
5+
16
mail-send 0.5.0
27
================================
38
- Bump `mail-parser` dependency to 0.10

src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ pub enum Error {
178178

179179
/// STARTTLS not available
180180
MissingStartTls,
181+
182+
/// Message is already signed
183+
MessageDkimSigned,
181184
}
182185

183186
impl std::error::Error for Error {
@@ -249,6 +252,7 @@ impl Display for Error {
249252
),
250253
Error::Timeout => write!(f, "Connection timeout"),
251254
Error::MissingStartTls => write!(f, "STARTTLS extension unavailable"),
255+
Error::MessageDkimSigned => write!(f, "Message is already signed"),
252256
}
253257
}
254258
}

src/smtp/message.rs

+78-29
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,30 @@ use std::{
1515

1616
#[cfg(feature = "builder")]
1717
use mail_builder::{
18-
headers::{address, HeaderType},
1918
MessageBuilder,
19+
headers::{HeaderType, address},
2020
};
2121
#[cfg(feature = "parser")]
2222
use mail_parser::{HeaderName, HeaderValue};
2323
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
2424

2525
use crate::SmtpClient;
2626

27+
#[cfg(feature = "dkim")]
28+
#[derive(Debug, Default, Clone, PartialEq)]
29+
enum Dkim {
30+
#[default]
31+
Unsigned,
32+
Signed,
33+
}
34+
2735
#[derive(Debug, Default, Clone)]
2836
pub struct Message<'x> {
2937
pub mail_from: Address<'x>,
3038
pub rcpt_to: Vec<Address<'x>>,
3139
pub body: Cow<'x, [u8]>,
40+
#[cfg(feature = "dkim")]
41+
dkim: Dkim,
3242
}
3343

3444
#[derive(Debug, Default, Clone)]
@@ -49,10 +59,10 @@ pub struct Parameter<'x> {
4959
}
5060

5161
impl<T: AsyncRead + AsyncWrite + Unpin> SmtpClient<T> {
52-
/// Sends a message to the server.
53-
pub async fn send<'x>(&mut self, message: impl IntoMessage<'x>) -> crate::Result<()> {
62+
/// Send headers
63+
#[inline]
64+
async fn send_headers<'x>(&mut self, message: &Message<'x>) -> crate::Result<()> {
5465
// Send mail-from
55-
let message = message.into_message()?;
5666
self.mail_from(
5767
message.mail_from.email.as_ref(),
5868
&message.mail_from.parameters,
@@ -64,42 +74,38 @@ impl<T: AsyncRead + AsyncWrite + Unpin> SmtpClient<T> {
6474
self.rcpt_to(rcpt.email.as_ref(), &rcpt.parameters).await?;
6575
}
6676

77+
Ok(())
78+
}
79+
80+
/// Convert into message and send that message to the server.
81+
pub async fn send<'x>(&mut self, message: impl IntoMessage<'x>) -> crate::Result<()> {
82+
// Send mail-from
83+
let message = message.into_message()?;
84+
85+
self.send_msg(&message).await
86+
}
87+
88+
/// Send a message to the server.
89+
#[inline]
90+
pub async fn send_msg<'x>(&mut self, message: &Message<'x>) -> crate::Result<()> {
91+
self.send_headers(message).await?;
92+
6793
// Send message
6894
self.data(message.body.as_ref()).await
6995
}
7096

71-
/// Sends a message to the server.
97+
/// Convert into message, sign and send that message to the server.
7298
#[cfg(feature = "dkim")]
7399
pub async fn send_signed<'x, V: mail_auth::common::crypto::SigningKey>(
74100
&mut self,
75101
message: impl IntoMessage<'x>,
76102
signer: &mail_auth::dkim::DkimSigner<V, mail_auth::dkim::Done>,
77103
) -> crate::Result<()> {
78-
// Send mail-from
79-
80-
use mail_auth::common::headers::HeaderWriter;
81-
let message = message.into_message()?;
82-
self.mail_from(
83-
message.mail_from.email.as_ref(),
84-
&message.mail_from.parameters,
85-
)
86-
.await?;
87-
88-
// Send rcpt-to
89-
for rcpt in &message.rcpt_to {
90-
self.rcpt_to(rcpt.email.as_ref(), &rcpt.parameters).await?;
91-
}
92-
93-
// Sign message
94-
let signature = signer
95-
.sign(message.body.as_ref())
96-
.map_err(|_| crate::Error::MissingCredentials)?;
97-
let mut signed_message = Vec::with_capacity(message.body.len() + 64);
98-
signature.write_header(&mut signed_message);
99-
signed_message.extend_from_slice(message.body.as_ref());
104+
let mut message = message.into_message()?;
105+
message.sign(signer)?;
100106

101107
// Send message
102-
self.data(&signed_message).await
108+
self.send_msg(&message).await
103109
}
104110

105111
pub async fn write_message(&mut self, message: &[u8]) -> tokio::io::Result<()> {
@@ -145,6 +151,8 @@ impl<'x> Message<'x> {
145151
mail_from: from.into(),
146152
rcpt_to: to.into_iter().map(Into::into).collect(),
147153
body: body.into(),
154+
#[cfg(feature = "dkim")]
155+
dkim: Dkim::Unsigned,
148156
}
149157
}
150158

@@ -154,26 +162,56 @@ impl<'x> Message<'x> {
154162
mail_from: Address::default(),
155163
rcpt_to: Vec::new(),
156164
body: Default::default(),
165+
#[cfg(feature = "dkim")]
166+
dkim: Dkim::Unsigned,
157167
}
158168
}
159169

160170
/// Set the sender of the message.
171+
#[inline]
161172
pub fn from(mut self, address: impl Into<Address<'x>>) -> Self {
162173
self.mail_from = address.into();
163174
self
164175
}
165176

166177
/// Add a message recipient.
178+
#[inline]
167179
pub fn to(mut self, address: impl Into<Address<'x>>) -> Self {
168180
self.rcpt_to.push(address.into());
169181
self
170182
}
171183

172184
/// Set the message body.
185+
#[inline]
173186
pub fn body(mut self, body: impl Into<Cow<'x, [u8]>>) -> Self {
174187
self.body = body.into();
175188
self
176189
}
190+
191+
/// Sign a message
192+
#[cfg(feature = "dkim")]
193+
#[inline]
194+
pub fn sign<V: mail_auth::common::crypto::SigningKey>(
195+
&mut self,
196+
signer: &mail_auth::dkim::DkimSigner<V, mail_auth::dkim::Done>,
197+
) -> crate::Result<()> {
198+
use mail_auth::common::headers::HeaderWriter;
199+
200+
let signature = signer
201+
.sign(self.body.as_ref())
202+
.map_err(|_| crate::Error::MissingCredentials)?;
203+
let mut signed_message = Vec::with_capacity(self.body.len() + 64);
204+
signature.write_header(&mut signed_message);
205+
signed_message.extend_from_slice(self.body.as_ref());
206+
207+
if self.dkim == Dkim::Signed {
208+
return Err(crate::Error::MessageDkimSigned);
209+
}
210+
self.body = signed_message.into();
211+
self.dkim = Dkim::Signed;
212+
213+
Ok(())
214+
}
177215
}
178216

179217
impl<'x> From<&'x str> for Address<'x> {
@@ -204,17 +242,20 @@ impl<'x> Address<'x> {
204242
}
205243

206244
impl<'x> Parameters<'x> {
245+
#[inline]
207246
pub fn new() -> Self {
208247
Self { params: Vec::new() }
209248
}
210249

250+
#[inline]
211251
pub fn add(&mut self, param: impl Into<Parameter<'x>>) -> &mut Self {
212252
self.params.push(param.into());
213253
self
214254
}
215255
}
216256

217257
impl<'x> From<&'x str> for Parameter<'x> {
258+
#[inline]
218259
fn from(value: &'x str) -> Self {
219260
Parameter {
220261
key: value.into(),
@@ -224,6 +265,7 @@ impl<'x> From<&'x str> for Parameter<'x> {
224265
}
225266

226267
impl<'x> From<(&'x str, &'x str)> for Parameter<'x> {
268+
#[inline]
227269
fn from(value: (&'x str, &'x str)) -> Self {
228270
Parameter {
229271
key: value.0.into(),
@@ -233,6 +275,7 @@ impl<'x> From<(&'x str, &'x str)> for Parameter<'x> {
233275
}
234276

235277
impl From<(String, String)> for Parameter<'_> {
278+
#[inline]
236279
fn from(value: (String, String)) -> Self {
237280
Parameter {
238281
key: value.0.into(),
@@ -242,6 +285,7 @@ impl From<(String, String)> for Parameter<'_> {
242285
}
243286

244287
impl From<String> for Parameter<'_> {
288+
#[inline]
245289
fn from(value: String) -> Self {
246290
Parameter {
247291
key: value.into(),
@@ -272,11 +316,12 @@ impl Display for Parameter<'_> {
272316
}
273317
}
274318

275-
pub trait IntoMessage<'x> {
319+
pub trait IntoMessage<'x>: Sized {
276320
fn into_message(self) -> crate::Result<Message<'x>>;
277321
}
278322

279323
impl<'x> IntoMessage<'x> for Message<'x> {
324+
#[inline(always)]
280325
fn into_message(self) -> crate::Result<Message<'x>> {
281326
Ok(self)
282327
}
@@ -347,6 +392,8 @@ impl<'x> IntoMessage<'x> for MessageBuilder<'_> {
347392
})
348393
.collect(),
349394
body: self.write_to_vec()?.into(),
395+
#[cfg(feature = "dkim")]
396+
dkim: Dkim::Unsigned,
350397
})
351398
}
352399
}
@@ -412,6 +459,8 @@ impl<'x> IntoMessage<'x> for mail_parser::Message<'x> {
412459
})
413460
.collect(),
414461
body: self.raw_message,
462+
#[cfg(feature = "dkim")]
463+
dkim: Dkim::Unsigned,
415464
})
416465
}
417466
}

0 commit comments

Comments
 (0)