opensmtpd-filter-dkimout/src/parsed_message.rs

71 lines
1.6 KiB
Rust
Raw Normal View History

2023-04-10 16:29:36 +02:00
use anyhow::{anyhow, Result};
pub struct ParsedMessage<'a> {
pub headers: Vec<ParsedHeader<'a>>,
pub body: &'a [u8],
}
impl<'a> ParsedMessage<'a> {
2023-04-10 16:29:36 +02:00
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..]),
2023-04-10 16:29:36 +02:00
None => return Err(anyhow!("message body not found")),
};
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'
}
2023-04-10 16:29:36 +02:00
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")
2023-04-10 16:29:36 +02:00
.ok_or(anyhow!("end of header not found"))?
+ 2;
2023-03-27 23:29:53 +02:00
if ret >= max_len {
return Ok(max_len);
}
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> {
2023-04-10 16:29:36 +02:00
fn from_bytes(data: &'a [u8]) -> Result<Self> {
let colon_pos = data
.iter()
.position(|&w| w == b':')
.ok_or(anyhow!("colon not found in header"))?;
let name = &data[..colon_pos];
let value = &data[colon_pos + 1..];
Ok(Self {
name,
2023-04-10 16:29:36 +02:00
name_lower: String::from_utf8(name.to_vec())?.to_lowercase(),
value,
raw: data,
})
}
}