tiny_mailcatcher/
email.rs

1use 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}