mail_parser/parsers/
mime.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 std::borrow::Cow;
8
9use super::MessageStream;
10
11impl<'x> MessageStream<'x> {
12    pub fn seek_next_part(&mut self, boundary: &[u8]) -> bool {
13        if !boundary.is_empty() {
14            let mut last_ch = 0;
15
16            self.checkpoint();
17
18            while let Some(&ch) = self.next() {
19                if ch == b'-' && last_ch == b'-' && self.try_skip(boundary) {
20                    return true;
21                }
22
23                last_ch = ch;
24            }
25
26            self.restore();
27        }
28
29        false
30    }
31
32    pub fn seek_next_part_offset(&mut self, boundary: &[u8]) -> Option<usize> {
33        let mut last_ch = b'\n';
34        let mut offset_pos = self.offset();
35        self.checkpoint();
36
37        while let Some(&ch) = self.next() {
38            if ch == b'\n' {
39                offset_pos = if last_ch == b'\r' {
40                    self.offset() - 2
41                } else {
42                    self.offset() - 1
43                };
44            } else if ch == b'-' && last_ch == b'-' && self.try_skip(boundary) {
45                return offset_pos.into();
46            }
47
48            last_ch = ch;
49        }
50
51        self.restore();
52
53        None
54    }
55
56    pub fn mime_part(&mut self, boundary: &[u8]) -> (usize, Cow<'x, [u8]>) {
57        let mut last_ch = b'\n';
58        let mut before_last_ch = 0;
59        let start_pos = self.offset();
60        let mut end_pos = self.offset();
61
62        self.checkpoint();
63
64        while let Some(&ch) = self.next() {
65            if ch == b'\n' {
66                end_pos = if last_ch == b'\r' {
67                    self.offset() - 2
68                } else {
69                    self.offset() - 1
70                };
71            } else if ch == b'-'
72                && !boundary.is_empty()
73                && last_ch == b'-'
74                && self.try_skip(boundary)
75            {
76                if before_last_ch != b'\n' {
77                    end_pos = self.offset() - boundary.len() - 2;
78                }
79                return (end_pos, self.bytes(start_pos..end_pos).into());
80            }
81
82            before_last_ch = last_ch;
83            last_ch = ch;
84        }
85
86        (
87            if boundary.is_empty() {
88                self.offset()
89            } else {
90                self.restore();
91                usize::MAX
92            },
93            self.bytes(start_pos..self.len()).into(),
94        )
95    }
96
97    pub fn seek_part_end(&mut self, boundary: Option<&[u8]>) -> (usize, bool) {
98        let mut last_ch = b'\n';
99        let mut before_last_ch = 0;
100        let mut end_pos = self.offset();
101
102        if let Some(boundary) = boundary {
103            while let Some(&ch) = self.next() {
104                if ch == b'\n' {
105                    end_pos = if last_ch == b'\r' {
106                        self.offset() - 2
107                    } else {
108                        self.offset() - 1
109                    };
110                } else if ch == b'-' && last_ch == b'-' && self.try_skip(boundary) {
111                    if before_last_ch != b'\n' {
112                        end_pos = self.offset() - boundary.len() - 2;
113                    }
114                    return (end_pos, true);
115                }
116
117                before_last_ch = last_ch;
118                last_ch = ch;
119            }
120
121            (self.offset(), false)
122        } else {
123            self.seek_end();
124            (self.offset(), true)
125        }
126    }
127
128    pub fn is_multipart_end(&mut self) -> bool {
129        self.checkpoint();
130
131        match (self.next(), self.peek()) {
132            (Some(b'\r'), Some(b'\n')) => {
133                self.next();
134                false
135            }
136            (Some(b'-'), Some(b'-')) => {
137                self.next();
138                true
139            }
140            (Some(b'\n'), _) => false,
141            (Some(&a), _) if a.is_ascii_whitespace() => {
142                self.skip_crlf();
143                false
144            }
145            _ => {
146                self.restore();
147                false
148            }
149        }
150    }
151
152    #[inline(always)]
153    pub fn skip_crlf(&mut self) {
154        while let Some(ch) = self.peek() {
155            match ch {
156                b'\r' | b' ' | b'\t' => {
157                    self.next();
158                }
159                b'\n' => {
160                    self.next();
161                    break;
162                }
163                _ => break,
164            }
165        }
166    }
167}