1use std::mem;
2
3use new_tokio_smtp::send_mail::{
4 self as smtp,
5 MailAddress,
6 EnvelopData
7};
8
9use mail_internals::{
10 MailType,
11 encoder::{EncodingBuffer, EncodableInHeader},
12 error::EncodingError
13};
14use headers::{
15 headers::{Sender, _From, _To},
16 header_components::Mailbox,
17 error::{BuildInValidationError}
18};
19use mail::{
20 Mail,
21 error::{MailError, OtherValidationError}
22};
23
24use ::error::{ OtherValidationError as AnotherOtherValidationError };
25
26#[derive(Clone, Debug)]
36pub struct MailRequest {
37 mail: Mail,
38 envelop_data: Option<EnvelopData>
39}
40
41impl From<Mail> for MailRequest {
42 fn from(mail: Mail) -> Self {
43 MailRequest::new(mail)
44 }
45}
46
47
48
49impl MailRequest {
50
51 pub fn new(mail: Mail) -> Self {
53 MailRequest { mail, envelop_data: None }
54 }
55
56 pub fn new_with_envelop(mail: Mail, envelop: EnvelopData) -> Self {
63 MailRequest { mail, envelop_data: Some(envelop) }
64 }
65
66 pub fn override_envelop(&mut self, envelop: EnvelopData) -> Option<EnvelopData> {
68 mem::replace(&mut self.envelop_data, Some(envelop))
69 }
70
71 pub fn _into_mail_with_envelop(self) -> Result<(Mail, EnvelopData), MailError> {
72 let envelop =
73 if let Some(envelop) = self.envelop_data { envelop }
74 else { derive_envelop_data_from_mail(&self.mail)? };
75
76 Ok((self.mail, envelop))
77 }
78
79 #[cfg(not(feature="extended-api"))]
80 #[inline(always)]
81 pub(crate) fn into_mail_with_envelop(self) -> Result<(Mail, EnvelopData), MailError> {
82 self._into_mail_with_envelop()
83 }
84
85 #[cfg(feature="extended-api")]
91 #[inline(always)]
92 pub fn into_mail_with_envelop(self) -> Result<(Mail, EnvelopData), MailError> {
93 self._into_mail_with_envelop()
94 }
95}
96
97fn mailaddress_from_mailbox(mailbox: &Mailbox) -> Result<MailAddress, EncodingError> {
98 let email = &mailbox.email;
99 let needs_smtputf8 = email.check_if_internationalized();
100 let mt = if needs_smtputf8 { MailType::Internationalized } else { MailType::Ascii };
101 let mut buffer = EncodingBuffer::new(mt);
102 {
103 let mut writer = buffer.writer();
104 email.encode(&mut writer)?;
105 writer.commit_partial_header();
106 }
107 let raw: Vec<u8> = buffer.into();
108 let address = String::from_utf8(raw).expect("[BUG] encoding Email produced non utf8 data");
109 Ok(MailAddress::new_unchecked(address, needs_smtputf8))
110}
111
112pub fn derive_envelop_data_from_mail(mail: &Mail)
132 -> Result<smtp::EnvelopData, MailError>
133{
134 let headers = mail.headers();
135 let smtp_from =
136 if let Some(sender) = headers.get_single(Sender) {
137 let sender = sender?;
138 mailaddress_from_mailbox(sender)?
140 } else {
141 let from = headers.get_single(_From)
142 .ok_or(OtherValidationError::NoFrom)??;
143
144 if from.len() > 1 {
145 return Err(BuildInValidationError::MultiMailboxFromWithoutSender.into());
146 }
147
148 mailaddress_from_mailbox(from.first())?
149 };
150
151 let smtp_to =
152 if let Some(to) = headers.get_single(_To) {
153 let to = to?;
154 to.try_mapped_ref(mailaddress_from_mailbox)?
155 } else {
156 return Err(AnotherOtherValidationError::NoTo.into());
157 };
158
159 Ok(EnvelopData {
162 from: Some(smtp_from),
163 to: smtp_to
164 })
165}
166
167#[cfg(test)]
168mod test {
169
170 mod derive_envelop_data_from_mail {
171 use super::super::derive_envelop_data_from_mail;
172 use mail::{
173 Mail,
174 Resource,
175 test_utils::CTX
176 };
177 use headers::{
178 headers::{_From, _To, Sender},
179 };
180
181
182 fn mock_resource() -> Resource {
183 Resource::plain_text("abcd↓efg", CTX.unwrap())
184 }
185
186 #[test]
187 fn use_sender_if_given() {
188 let mut mail = Mail::new_singlepart_mail(mock_resource());
189
190 mail.insert_headers(headers! {
191 Sender: "strange@caffe.test",
192 _From: ["ape@caffe.test", "epa@caffe.test"],
193 _To: ["das@ding.test"]
194 }.unwrap());
195
196 let envelop_data = derive_envelop_data_from_mail(&mail).unwrap();
197
198 assert_eq!(
199 envelop_data.from.as_ref().unwrap().as_str(),
200 "strange@caffe.test"
201 );
202 }
203
204 #[test]
205 fn use_from_if_no_sender_given() {
206 let mut mail = Mail::new_singlepart_mail(mock_resource());
207 mail.insert_headers(headers! {
208 _From: ["ape@caffe.test"],
209 _To: ["das@ding.test"]
210 }.unwrap());
211
212 let envelop_data = derive_envelop_data_from_mail(&mail).unwrap();
213
214 assert_eq!(
215 envelop_data.from.as_ref().unwrap().as_str(),
216 "ape@caffe.test"
217 );
218 }
219
220 #[test]
221 fn fail_if_no_sender_but_multi_mailbox_from() {
222 let mut mail = Mail::new_singlepart_mail(mock_resource());
223 mail.insert_headers(headers! {
224 _From: ["ape@caffe.test", "a@b.test"],
225 _To: ["das@ding.test"]
226 }.unwrap());
227
228 let envelop_data = derive_envelop_data_from_mail(&mail);
229
230 envelop_data.unwrap_err();
232 }
233
234 #[test]
235 fn use_to() {
236 let mut mail = Mail::new_singlepart_mail(mock_resource());
237 mail.insert_headers(headers! {
238 _From: ["ape@caffe.test"],
239 _To: ["das@ding.test"]
240 }.unwrap());
241
242 let envelop_data = derive_envelop_data_from_mail(&mail).unwrap();
243
244 assert_eq!(
245 envelop_data.to.first().as_str(),
246 "das@ding.test"
247 );
248 }
249 }
250
251 mod mailaddress_from_mailbox {
252 use headers::{
253 HeaderTryFrom,
254 header_components::{Mailbox, Email}
255 };
256 use super::super::mailaddress_from_mailbox;
257
258 #[test]
259 #[cfg_attr(not(feature="test-with-traceing"), ignore)]
260 fn does_not_panic_with_tracing_enabled() {
261 let mb = Mailbox::try_from("hy@b").unwrap();
262 mailaddress_from_mailbox(&mb).unwrap();
263 }
264
265 #[test]
266 fn correctly_converts_mailbox() {
267 let mb = Mailbox::from(Email::new("tast@tost.test").unwrap());
268 let address = mailaddress_from_mailbox(&mb).unwrap();
269 assert_eq!(address.as_str(), "tast@tost.test");
270 assert_eq!(address.needs_smtputf8(), false);
271 }
272
273 #[test]
274 fn tracks_if_smtputf8_is_needed() {
275 let mb = Mailbox::from(Email::new("tüst@tost.test").unwrap());
276 let address = mailaddress_from_mailbox(&mb).unwrap();
277 assert_eq!(address.as_str(), "tüst@tost.test");
278 assert_eq!(address.needs_smtputf8(), true);
279 }
280
281 #[test]
282 fn puny_encodes_domain_if_smtputf8_is_not_needed() {
283 let mb = Mailbox::from(Email::new("tast@tüst.test").unwrap());
284 let address = mailaddress_from_mailbox(&mb).unwrap();
285 assert_eq!(address.as_str(), "tast@xn--tst-hoa.test");
286 assert_eq!(address.needs_smtputf8(), false);
287 }
288
289 #[test]
290 fn does_not_puny_encodes_domain_if_smtputf8_is_needed() {
291 let mb = Mailbox::from(Email::new("töst@tüst.test").unwrap());
292 let address = mailaddress_from_mailbox(&mb).unwrap();
293 assert_eq!(address.as_str(), "töst@tüst.test");
294 assert_eq!(address.needs_smtputf8(), true);
295 }
296 }
297}