tiny_mailcatcher/
email.rs1use crate::repository::{Message, MessagePart};
2use chrono::Utc;
3use mailparse::{parse_mail, DispositionType, MailHeaderMap, MailParseError, ParsedMail};
4
5pub fn parse_message(
6 sender: &Option<String>,
7 recipients: &[String],
8 raw_email: &[u8],
9) -> Result<Message, MailParseError> {
10 let parsed_email = parse_mail(raw_email)?;
11
12 let sender = sender.clone();
13 let headers = parsed_email.get_headers();
14
15 let subject = headers.get_first_value("Subject");
16
17 let typ = parsed_email.ctype.mimetype.clone();
18
19 let mut parsed_parts = vec![];
20 flatten_parts_into(&mut parsed_parts, &parsed_email);
21
22 let mut parts = vec![];
23 let mut part_id = 1;
24 for parsed_part in parsed_parts {
25 let body = parsed_part.get_body_raw().unwrap().clone();
26 parts.push(MessagePart {
27 cid: format!("{}.mail", part_id),
28 typ: parsed_part.ctype.mimetype.clone(),
29 filename: parsed_part
30 .ctype
31 .params
32 .get("name")
33 .cloned()
34 .unwrap_or_else(|| format!("part{}", part_id)),
35 size: body.len(),
36 charset: parsed_part.ctype.charset.clone(),
37 body,
38 is_attachment: parsed_part.get_content_disposition().disposition
39 == DispositionType::Attachment,
40 });
41 part_id += 1;
42 }
43
44 Ok(Message {
45 id: None,
46 size: raw_email.len(),
47 charset: parsed_email.ctype.charset.clone(),
48 subject,
49 sender,
50 recipients: recipients.to_owned(),
51 created_at: Utc::now(),
52 typ,
53 parts,
54 source: raw_email.to_vec(),
55 })
56}
57
58fn flatten_parts_into<'e>(vec: &mut Vec<&'e ParsedMail<'e>>, parsed_email: &'e ParsedMail<'e>) {
59 for part in &parsed_email.subparts {
60 vec.push(part);
61 flatten_parts_into(vec, part);
62 }
63}