diff options
author | Alexandros Frantzis <alf82@freemail.gr> | 2019-10-06 17:37:04 +0300 |
---|---|---|
committer | Alexandros Frantzis <alf82@freemail.gr> | 2019-10-06 17:37:34 +0300 |
commit | 97f322e31996861686cc13e5e43b8e5fc702330a (patch) | |
tree | b98fee87ff3a31eea8ee5e58c2503b6d21cd2aca | |
parent | a3a41464729cdbcd64d3e1fc9571e05b99d15ed5 (diff) |
Improve robustness of MIME part boundary handling
Handle more gracefully a few cases related to boundaries, that may
come up in malformed emails.
-rw-r--r-- | src/normalize.rs | 24 | ||||
-rw-r--r-- | tests/test_boundaries.rs | 49 |
2 files changed, 67 insertions, 6 deletions
diff --git a/src/normalize.rs b/src/normalize.rs index 3774f73..3312ee9 100644 --- a/src/normalize.rs +++ b/src/normalize.rs @@ -167,10 +167,18 @@ impl<'a> EmailParser<'a> { } fn end_part(&mut self) { - self.part_stack.pop(); - if let Some(part) = self.part_stack.last_mut() { - part.subpart_boundary = None; + match &self.part_stack.last().unwrap().subpart_boundary { + // If last part is top part (i.e., we just had a boundary end line + // without a preceding boundary start line) do nothing. + Some(b) if b == &self.active_boundary => {}, + // Otherwise, remove the active part. + _ => { self.part_stack.pop(); } } + + // Remove boundary info from top part. + self.part_stack.last_mut().unwrap().subpart_boundary = None; + self.active_boundary.clear(); + for p in self.part_stack.iter().rev() { if let Some(b) = &p.subpart_boundary { self.active_boundary = b.clone(); @@ -223,9 +231,13 @@ fn slice_trim_end_newline(mut line: &[u8]) -> &[u8] { /// Returns whether a line of bytes is a multi-part boundary line for the /// specified boundary string. fn is_boundary_line(line: &[u8], boundary: &[u8]) -> bool { - line.starts_with(b"--") && - !boundary.is_empty() && - line[2..].starts_with(&boundary) + if line.starts_with(b"--") && !boundary.is_empty() { + let line = slice_trim_end_newline(&line); + let line = if line.ends_with(b"--") { &line[..line.len()-2] } else { &line[..] }; + return line.len() > 2 && &line[2..] == boundary; + } + + false } diff --git a/tests/test_boundaries.rs b/tests/test_boundaries.rs new file mode 100644 index 0000000..f898d2a --- /dev/null +++ b/tests/test_boundaries.rs @@ -0,0 +1,49 @@ +// Copyright 2019 Alexandros Frantzis +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// SPDX-License-Identifier: MPL-2.0 + +use mda::{Email, EmailRegex}; + +static TEST_EMAIL_FAKE_BOUNDARY: &'static str = r#"Return-Path: <me@source.com> +To: Destination <someone.else@destination.com> +Content-type: multipart/alternative; boundary="QWFCYkN" + +--QWFCYkN +Content-transfer-encoding: base64 + +--QWFCYkNj + +--QWFCYkN +"#; + +static TEST_EMAIL_BOUNDARY_BEGIN_AFTER_END: &'static str = r#"Return-Path: <me@source.com> +To: Destination <someone.else@destination.com> +Content-type: multipart/alternative; boundary="XtT01VFrJIenjlg+ZCXSSWq4" + +--XtT01VFrJIenjlg+ZCXSSWq4-- + +--XtT01VFrJIenjlg+ZCXSSWq4 +"#; + +#[test] +fn only_exact_boundary_lines_are_parsed() { + // The "--QWFCYkNj" line should be parsed as part of the body not as a boundary. + let email = + Email::from_vec( + TEST_EMAIL_FAKE_BOUNDARY.to_string().into_bytes() + ).unwrap(); + assert!(email.body().search("AaBbCc").unwrap()); +} + +#[test] +fn boundary_begin_after_end_is_parsed() { + assert!( + Email::from_vec( + TEST_EMAIL_BOUNDARY_BEGIN_AFTER_END.to_string().into_bytes() + ).is_ok() + ); +} |