mail_parser/parsers/fields/
raw.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use crate::{parsers::MessageStream, HeaderValue};
8
9impl<'x> MessageStream<'x> {
10    pub fn parse_raw(&mut self) -> HeaderValue<'x> {
11        let mut token_start: usize = 0;
12        let mut token_end: usize = 0;
13
14        while let Some(ch) = self.next() {
15            match ch {
16                b'\n' => {
17                    if !self.try_next_is_space() {
18                        break;
19                    } else {
20                        continue;
21                    }
22                }
23                b' ' | b'\t' | b'\r' => continue,
24                _ => (),
25            }
26
27            if token_start == 0 {
28                token_start = self.offset();
29            }
30
31            token_end = self.offset();
32        }
33
34        if token_start > 0 {
35            HeaderValue::Text(String::from_utf8_lossy(
36                self.bytes(token_start - 1..token_end),
37            ))
38        } else {
39            HeaderValue::Empty
40        }
41    }
42
43    pub fn parse_and_ignore(&mut self) {
44        while let Some(&ch) = self.next() {
45            if ch == b'\n' && !self.try_next_is_space() {
46                break;
47            }
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::{parsers::MessageStream, MessageParser};
55
56    #[test]
57    fn parse_raw_text() {
58        let inputs = [
59            ("Saying Hello\nMessage-Id", "Saying Hello"),
60            ("Re: Saying Hello\r\n \r\nFrom:", "Re: Saying Hello"),
61            (
62                concat!(
63                    " from x.y.test\n      by example.net\n      via TCP\n",
64                    "      with ESMTP\n      id ABC12345\n      ",
65                    "for <mary@example.net>;  21 Nov 1997 10:05:43 -0600\n"
66                ),
67                concat!(
68                    "from x.y.test\n      by example.net\n      via TCP\n",
69                    "      with ESMTP\n      id ABC12345\n      ",
70                    "for <mary@example.net>;  21 Nov 1997 10:05:43 -0600"
71                ),
72            ),
73            ("Re: Saying Hello", "Re: Saying Hello"), // No newline test
74        ];
75
76        for (input, expected) in inputs {
77            assert_eq!(
78                MessageStream::new(input.as_bytes())
79                    .parse_raw()
80                    .unwrap_text(),
81                expected,
82                "Failed for '{:?}'",
83                input
84            );
85        }
86    }
87
88    #[test]
89    fn ordered_raw_headers() {
90        let input = br#"From: Art Vandelay <art@vandelay.com>
91To: jane@example.com
92Date: Sat, 20 Nov 2021 14:22:01 -0800
93Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
94Content-Type: multipart/mixed; boundary="festivus";
95
96Here's a message body.
97"#;
98        let message = MessageParser::default().parse(input).unwrap();
99        let mut iter = message.headers_raw();
100        assert_eq!(
101            iter.next().unwrap(),
102            ("From", " Art Vandelay <art@vandelay.com>\n")
103        );
104        assert_eq!(iter.next().unwrap(), ("To", " jane@example.com\n"));
105        assert_eq!(
106            iter.next().unwrap(),
107            ("Date", " Sat, 20 Nov 2021 14:22:01 -0800\n")
108        );
109        assert_eq!(
110            iter.next().unwrap(),
111            (
112                "Subject",
113                " Why not both importing AND exporting? =?utf-8?b?4pi6?=\n"
114            )
115        );
116        assert_eq!(
117            iter.next().unwrap(),
118            ("Content-Type", " multipart/mixed; boundary=\"festivus\";\n")
119        );
120    }
121}