Replace the mailparse crate by a custom header pseudo-parser
This commit is contained in:
parent
5a403eccae
commit
9821a9888e
4 changed files with 91 additions and 25 deletions
|
@ -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 }
|
||||
|
|
|
@ -5,6 +5,7 @@ mod entry;
|
|||
mod handshake;
|
||||
mod logs;
|
||||
mod message;
|
||||
mod parsed_message;
|
||||
mod stdin_reader;
|
||||
|
||||
use algorithm::Algorithm;
|
||||
|
|
|
@ -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
66
src/parsed_message.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue