Replace the mailparse crate by a custom header pseudo-parser

This commit is contained in:
Rodolphe Bréard 2023-03-27 22:46:17 +02:00
parent 5a403eccae
commit 9821a9888e
4 changed files with 91 additions and 25 deletions

View file

@ -11,5 +11,4 @@ publish = false
clap = { version = "4.1.13", default-features = false, features = ["std", "derive"] }
env_logger = { version = "0.10.0", default-features = false }
log = { version = "0.4.17", default-features = false }
mailparse = { version = "0.14.0", default-features = false }
nom = { version = "7.1.3", default-features = false }

View file

@ -5,6 +5,7 @@ mod entry;
mod handshake;
mod logs;
mod message;
mod parsed_message;
mod stdin_reader;
use algorithm::Algorithm;

View file

@ -1,6 +1,6 @@
use crate::config::Config;
use crate::entry::Entry;
use mailparse::parse_mail;
use crate::parsed_message::ParsedMessage;
use std::io::{BufWriter, Write};
pub const RETURN_SEP: &[u8] = b"|";
@ -52,35 +52,35 @@ impl Message {
}
pub fn sign_and_return(&self, cnf: &Config) {
log::trace!("content:\n{}", crate::display_bytes!(&self.content));
match parse_mail(&self.content) {
log::trace!("content: {}", crate::display_bytes!(&self.content));
match ParsedMessage::from_bytes(&self.content) {
Ok(parsed_msg) => {
log::trace!("mail parsed");
for h in parsed_msg.get_headers() {
log::trace!("{:?}", h);
}
match self.get_body() {
Some(body) => {
log::trace!("MailBody:\n{}", crate::display_bytes!(body));
// TODO: sign the message using DKIM
}
None => {
log::error!("{}: unable to find the body", self.session_id);
}
for h in &parsed_msg.headers {
log::trace!(
"ParsedMessage: header: raw: {}",
crate::display_bytes!(h.raw)
);
log::trace!(
"ParsedMessage: header: name: {}",
crate::display_bytes!(h.name)
);
log::trace!(
"ParsedMessage: header: value: {}",
crate::display_bytes!(h.value)
);
}
log::trace!(
"ParsedMessage: body: {}",
crate::display_bytes!(parsed_msg.body)
);
// TODO: sign the message using DKIM
}
Err(e) => {
log::error!("{}: unable to parse message: {e}", self.session_id);
Err(_) => {
log::error!("{}: unable to parse message", self.session_id);
}
};
self.print_msg();
}
fn get_body(&self) -> Option<&[u8]> {
match self.content.windows(4).position(|w| w == b"\r\n\r\n") {
Some(body_index) => Some(&self.content[body_index + 4..]),
None => None,
}
self.print_msg();
}
fn print_msg(&self) {

66
src/parsed_message.rs Normal file
View file

@ -0,0 +1,66 @@
pub struct ParsedMessage<'a> {
pub headers: Vec<ParsedHeader<'a>>,
pub body: &'a [u8],
}
impl<'a> ParsedMessage<'a> {
pub fn from_bytes(data: &'a [u8]) -> Result<Self, ()> {
let (mut raw_headers, body) = match data.windows(4).position(|w| w == b"\r\n\r\n") {
Some(body_index) => (&data[..body_index + 2], &data[body_index + 4..]),
None => return Err(()),
};
let mut headers = Vec::with_capacity(128);
while !raw_headers.is_empty() {
let end_index = header_end_pos(raw_headers)?;
let h = ParsedHeader::from_bytes(&raw_headers[..end_index])?;
headers.push(h);
raw_headers = &raw_headers[end_index..];
}
headers.shrink_to_fit();
Ok(Self { headers, body })
}
}
fn is_wsp(c: u8) -> bool {
c == b' ' || c == b'\t'
}
fn header_end_pos(data: &[u8]) -> Result<usize, ()> {
let mut ret = 0;
let max_len = data.len();
loop {
ret += data[ret..]
.windows(2)
.position(|w| w == b"\r\n")
.ok_or(())? + 2;
if ret == max_len {
return Ok(ret);
}
if !is_wsp(data[ret]) {
return Ok(ret);
}
}
}
pub struct ParsedHeader<'a> {
pub name: &'a [u8],
pub name_lower: String,
pub value: &'a [u8],
pub raw: &'a [u8],
}
impl<'a> ParsedHeader<'a> {
fn from_bytes(data: &'a [u8]) -> Result<Self, ()> {
let colon_pos = data.iter().position(|&w| w == b':').ok_or(())?;
let name = &data[..colon_pos];
let value = &data[colon_pos + 1..];
Ok(Self {
name,
name_lower: String::from_utf8(name.to_vec())
.map_err(|_| ())?
.to_lowercase(),
value,
raw: data,
})
}
}