summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandros Frantzis <alf82@freemail.gr>2019-10-06 17:37:04 +0300
committerAlexandros Frantzis <alf82@freemail.gr>2019-10-06 17:37:34 +0300
commit97f322e31996861686cc13e5e43b8e5fc702330a (patch)
treeb98fee87ff3a31eea8ee5e58c2503b6d21cd2aca
parenta3a41464729cdbcd64d3e1fc9571e05b99d15ed5 (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.rs24
-rw-r--r--tests/test_boundaries.rs49
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()
+ );
+}