Skip to content

Commit 8796c5f

Browse files
authored
Added mail_parser::Message to Message conversion (#30)
1 parent 17a45f5 commit 8796c5f

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ doctest = false
1818
smtp-proto = { version = "0.1" }
1919
mail-auth = { version = "0.3", optional = true }
2020
mail-builder = { version = "0.3", optional = true }
21+
mail-parser = { version = "0.9", optional = true }
2122
base64 = "0.21"
2223
rand = { version = "0.8.5", optional = true }
2324
md5 = { version = "0.7.0", optional = true }
@@ -33,8 +34,9 @@ tokio = { version = "1.16", features = ["net", "io-util", "time", "rt-multi-thre
3334
env_logger = "0.10.0"
3435

3536
[features]
36-
default = ["digest-md5", "cram-md5", "builder", "dkim"]
37+
default = ["digest-md5", "cram-md5", "builder", "parser", "dkim"]
3738
builder = ["mail-builder"]
39+
parser = ["mail-parser"]
3840
dkim = ["mail-auth"]
3941
digest-md5 = ["md5", "rand"]
4042
cram-md5 = ["md5"]

src/smtp/message.rs

+67
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use mail_builder::{
1818
headers::{address, HeaderType},
1919
MessageBuilder,
2020
};
21+
#[cfg(feature = "parser")]
22+
use mail_parser::{HeaderName, HeaderValue};
2123
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
2224

2325
use crate::SmtpClient;
@@ -348,3 +350,68 @@ impl<'x, 'y> IntoMessage<'x> for MessageBuilder<'y> {
348350
})
349351
}
350352
}
353+
354+
#[cfg(feature = "parser")]
355+
impl<'x> IntoMessage<'x> for mail_parser::Message<'x> {
356+
fn into_message(self) -> crate::Result<Message<'x>> {
357+
let mut mail_from = None;
358+
let mut rcpt_to = std::collections::HashSet::new();
359+
360+
let find_address = |addr: &mail_parser::Addr| -> Option<String> {
361+
addr.address
362+
.as_ref()
363+
.filter(|address| !address.trim().is_empty())
364+
.map(|address| address.trim().to_string())
365+
};
366+
367+
for header in self.headers() {
368+
match &header.name {
369+
HeaderName::From => match header.value() {
370+
HeaderValue::Address(mail_parser::Address::List(addrs)) => {
371+
if let Some(email) = addrs.iter().find_map(find_address) {
372+
mail_from = email.to_string().into();
373+
}
374+
}
375+
HeaderValue::Address(mail_parser::Address::Group(groups)) => {
376+
if let Some(grps) = groups.first() {
377+
if let Some(email) = grps.addresses.iter().find_map(find_address) {
378+
mail_from = email.to_string().into();
379+
}
380+
}
381+
}
382+
_ => (),
383+
},
384+
HeaderName::To | HeaderName::Cc | HeaderName::Bcc => match header.value() {
385+
HeaderValue::Address(mail_parser::Address::List(addrs)) => {
386+
rcpt_to.extend(addrs.iter().filter_map(find_address));
387+
}
388+
HeaderValue::Address(mail_parser::Address::Group(grps)) => {
389+
rcpt_to.extend(
390+
grps.iter()
391+
.flat_map(|grp| grp.addresses.iter())
392+
.filter_map(find_address),
393+
);
394+
}
395+
_ => (),
396+
},
397+
_ => (),
398+
};
399+
}
400+
401+
if rcpt_to.is_empty() {
402+
return Err(crate::Error::MissingRcptTo);
403+
}
404+
405+
Ok(Message {
406+
mail_from: mail_from.ok_or(crate::Error::MissingMailFrom)?.into(),
407+
rcpt_to: rcpt_to
408+
.into_iter()
409+
.map(|email| Address {
410+
email: email.into(),
411+
parameters: Parameters::default(),
412+
})
413+
.collect(),
414+
body: self.raw_message,
415+
})
416+
}
417+
}

0 commit comments

Comments
 (0)