summaryrefslogtreecommitdiffstats
path: root/melib/src/email/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'melib/src/email/parser.rs')
-rw-r--r--melib/src/email/parser.rs140
1 files changed, 130 insertions, 10 deletions
diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs
index 28a3c940..9938b10b 100644
--- a/melib/src/email/parser.rs
+++ b/melib/src/email/parser.rs
@@ -28,6 +28,17 @@ pub(super) use nom::{ErrorKind, IResult, Needed};
use encoding::all::*;
use std;
+macro_rules! is_ctl_or_space {
+ ($var:ident) => {
+ /* <any ASCII control character and DEL> */
+ $var < 33 || $var == 127
+ };
+ ($var:expr) => {
+ /* <any ASCII control character and DEL> */
+ $var < 33 || $var == 127
+ };
+}
+
macro_rules! is_whitespace {
($var:ident) => {
$var == b' ' || $var == b'\t' || $var == b'\n' || $var == b'\r'
@@ -138,11 +149,14 @@ fn header_with_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
}
let mut ptr = 0;
let mut name: &[u8] = &input[0..0];
+ /* field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> */
for (i, x) in input.iter().enumerate() {
if *x == b':' {
name = &input[0..i];
ptr = i + 1;
break;
+ } else if is_ctl_or_space!(*x) {
+ return IResult::Error(error_code!(ErrorKind::Custom(43)));
}
}
if name.is_empty() {
@@ -184,6 +198,8 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
}
let mut ptr = 0;
let mut name: &[u8] = &input[0..0];
+ let mut has_colon = false;
+ /* field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> */
for (i, x) in input.iter().enumerate() {
if input[i..].starts_with(b"\r\n") {
name = &input[0..i];
@@ -191,8 +207,11 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
break;
} else if *x == b':' || *x == b'\n' {
name = &input[0..i];
+ has_colon = true;
ptr = i;
break;
+ } else if is_ctl_or_space!(*x) {
+ return IResult::Error(error_code!(ErrorKind::Custom(43)));
}
}
if name.is_empty() {
@@ -200,10 +219,16 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
}
if input[ptr] == b':' {
ptr += 1;
+ has_colon = true;
if ptr >= input.len() {
return IResult::Incomplete(Needed::Unknown);
}
}
+
+ if !has_colon {
+ return IResult::Incomplete(Needed::Unknown);
+ }
+
while input[ptr] == b' ' {
ptr += 1;
if ptr >= input.len() {
@@ -265,10 +290,10 @@ named!(pub body_raw<&[u8]>,
named!(pub mail<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
separated_pair!(headers, alt_complete!(tag!(b"\n") | tag!(b"\r\n")), take_while!(call!(|_| true))));
+
named!(pub attachment<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
do_parse!(
- opt!(is_a!(" \n\t\r")) >>
- pair: pair!(many0!(complete!(header)), take_while!(call!(|_| true))) >>
+ pair: separated_pair!(many0!(complete!(header)), alt_complete!(tag!(b"\n") | tag!(b"\r\n")), take_while!(call!(|_| true))) >>
( { pair } )));
/* Header parsers */
@@ -636,9 +661,73 @@ fn message_id_peek(input: &[u8]) -> IResult<&[u8], &[u8]> {
named!(pub references<Vec<&[u8]>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
-fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<&'a [u8]>> {
+pub fn multipart_parts<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<StrBuilder>> {
+ let mut ret: Vec<_> = Vec::new();
+ let mut input = input;
+ let mut offset = 0;
+ loop {
+ let b_start = if let Some(v) = input.find(boundary) {
+ v
+ } else {
+ return IResult::Error(error_code!(ErrorKind::Custom(39)));
+ };
+
+ if b_start < 2 {
+ return IResult::Error(error_code!(ErrorKind::Custom(40)));
+ }
+ offset += b_start - 2;
+ input = &input[b_start - 2..];
+ if &input[0..2] == b"--" {
+ offset += 2 + boundary.len();
+ input = &input[2 + boundary.len()..];
+ if input[0] == b'\n' {
+ offset += 1;
+ input = &input[1..];
+ } else if input[0..].starts_with(b"\r\n") {
+ offset += 2;
+ input = &input[2..];
+ } else {
+ continue;
+ }
+ break;
+ }
+ }
+
+ loop {
+ if input.len() < boundary.len() + 4 {
+ return IResult::Error(error_code!(ErrorKind::Custom(41)));
+ }
+ if let Some(end) = input.find(boundary) {
+ if &input[end - 2..end] != b"--" {
+ return IResult::Error(error_code!(ErrorKind::Custom(42)));
+ }
+ ret.push(StrBuilder {
+ offset,
+ length: end - 2,
+ });
+ offset += end + boundary.len();
+ input = &input[end + boundary.len()..];
+ if input.len() < 2 || input[0] != b'\n' || &input[0..2] == b"--" {
+ break;
+ }
+ if input[0] == b'\n' {
+ offset += 1;
+ input = &input[1..];
+ } else if input[0..].starts_with(b"\r\n") {
+ offset += 2;
+ input = &input[2..];
+ }
+ continue;
+ } else {
+ return IResult::Error(error_code!(ErrorKind::Custom(43)));
+ }
+ }
+ IResult::Done(input, ret)
+}
+
+fn parts_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<&'a [u8]>> {
let mut ret: Vec<&[u8]> = Vec::new();
- let mut input = input.ltrim();
+ let mut input = input;
loop {
let b_start = if let Some(v) = input.find(boundary) {
v
@@ -652,10 +741,13 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<
input = &input[b_start - 2..];
if &input[0..2] == b"--" {
input = &input[2 + boundary.len()..];
- if input[0] != b'\n' && !input[0..].starts_with(b"\r\n") {
+ if input[0] == b'\n' {
+ input = &input[1..];
+ } else if input[0..].starts_with(b"\r\n") {
+ input = &input[2..];
+ } else {
continue;
}
- input = &input[1..];
break;
}
}
@@ -672,7 +764,11 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<
if input.len() < 2 || input[0] != b'\n' || &input[0..2] == b"--" {
break;
}
- input = &input[1..];
+ if input[0] == b'\n' {
+ input = &input[1..];
+ } else if input[0..].starts_with(b"\r\n") {
+ input = &input[2..];
+ }
continue;
} else {
return IResult::Error(error_code!(ErrorKind::Custom(43)));
@@ -681,8 +777,8 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<
IResult::Done(input, ret)
}
-named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
- alt_complete!(call!(attachments_f, boundary) | do_parse!(
+named_args!(pub parts<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
+ alt_complete!(call!(parts_f, boundary) | do_parse!(
take_until_and_consume!(&b"--"[..]) >>
take_until_and_consume!(boundary) >>
( { Vec::<&[u8]>::new() } ))
@@ -890,6 +986,30 @@ pub fn mailto(mut input: &[u8]) -> IResult<&[u8], Mailto> {
)
}
+//alt_complete!(call!(header_without_val) | call!(header_with_val))
+
+pub struct HeaderIterator<'a>(pub &'a [u8]);
+
+impl<'a> Iterator for HeaderIterator<'a> {
+ type Item = (&'a [u8], &'a [u8]);
+ fn next(&mut self) -> Option<(&'a [u8], &'a [u8])> {
+ if self.0.is_empty() {
+ return None;
+ }
+
+ match header(self.0) {
+ IResult::Done(rest, value) => {
+ self.0 = rest;
+ Some(value)
+ }
+ _ => {
+ self.0 = &[];
+ None
+ }
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
@@ -1009,7 +1129,7 @@ mod tests {
Ok(v) => v,
Err(_) => panic!(),
};
- let attachments = attachments(body, boundary).to_full_result().unwrap();
+ let attachments = parts(body, boundary).to_full_result().unwrap();
assert_eq!(attachments.len(), 4);
let v: Vec<&str> = attachments
.iter()