Skip to main content

mail_parser/fields/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7pub(crate) mod address;
8pub(crate) mod content_type;
9pub(crate) mod date;
10pub(crate) mod id;
11pub(crate) mod list;
12pub(crate) mod raw;
13pub(crate) mod received;
14pub(crate) mod thread;
15pub(crate) mod unstructured;
16
17#[cfg(test)]
18pub(crate) mod tests;
19
20use crate::{
21    HeaderValue,
22    scan::Kernel,
23    store::{
24        AddressEntry, ContentTypeEntry, GROUP_BIT, MessageData, ParamEntry, ReceivedEntry, Span,
25        Str, Value,
26    },
27    view::Resolver,
28};
29use std::{fmt, ops::Range};
30
31/// How a header value is parsed.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33#[repr(u8)]
34pub enum HeaderForm {
35    /// The value as written, without leading and trailing whitespace.
36    Raw,
37    /// Unstructured text with RFC 2047 encoded words decoded.
38    Text,
39    /// An address list: mailboxes and groups in document order.
40    Addresses,
41    /// A list of message identifiers.
42    MessageIds,
43    /// A comma-separated list of phrases.
44    CommaList,
45    /// An RFC 5322 date.
46    Date,
47    /// A Content-Type or Content-Disposition value.
48    ContentType,
49    /// A Received trace field.
50    Received,
51    /// Not parsed; the header is kept with an empty value.
52    Ignore,
53}
54
55impl HeaderForm {
56    /// Parses a standalone header value: the bytes after the colon, up to
57    /// the end of the field (a line break not followed by white space).
58    ///
59    /// ```
60    /// use mail_parser::HeaderForm;
61    ///
62    /// let parsed = HeaderForm::Addresses.parse(b" Jane <jane@example.com>, bob@example.com\r\n");
63    /// let addresses = parsed.value().as_address().expect("addresses");
64    /// assert_eq!(addresses.mailboxes().count(), 2);
65    ///
66    /// let parsed = HeaderForm::Date.parse(b" Sat, 20 Nov 2021 14:22:01 -0800\r\n");
67    /// let date = parsed.value().as_datetime().expect("date");
68    /// assert_eq!(date.to_timestamp(), 1_637_446_921);
69    /// ```
70    pub fn parse(self, value: &[u8]) -> ParsedValue<'_> {
71        ParsedValue::new(self, value, 0..value.len())
72    }
73
74    pub(crate) fn parse_field(self, ctx: &mut FieldCtx<'_>, value: Range<usize>) -> Value {
75        match self {
76            HeaderForm::Raw => ctx.parse_raw(value),
77            HeaderForm::Text => ctx.parse_unstructured(value),
78            HeaderForm::Addresses => ctx.parse_address(value),
79            HeaderForm::MessageIds => ctx.parse_id(value),
80            HeaderForm::CommaList => ctx.parse_comma_list(value),
81            HeaderForm::Date => ctx.parse_date(value),
82            HeaderForm::ContentType => ctx.parse_content_type(value),
83            HeaderForm::Received => ctx.parse_received(value),
84            HeaderForm::Ignore => Value::Empty,
85        }
86    }
87}
88
89/// A header value parsed on its own, outside a message, by
90/// [`HeaderForm::parse`] or [`crate::Header::parse_as`]. It borrows the
91/// input and owns a small store only when the value needs one (a raw,
92/// single-line text or date value allocates nothing). Parsed values compare
93/// by their value.
94#[derive(Clone)]
95pub struct ParsedValue<'x> {
96    source: &'x [u8],
97    data: Option<MessageData>,
98    value: Value,
99}
100
101static EMPTY_DATA: MessageData = MessageData::EMPTY;
102
103impl<'x> ParsedValue<'x> {
104    pub(crate) fn new(form: HeaderForm, source: &'x [u8], range: Range<usize>) -> Self {
105        let range = first_field(source, range);
106        let value = match form {
107            HeaderForm::Ignore => Value::Empty,
108            HeaderForm::Date => date::parse_bytes(source.get(range).unwrap_or_default())
109                .map_or(Value::Empty, Value::DateTime),
110            HeaderForm::Raw => match raw::borrowed(source, range.clone()) {
111                Some(value) => value,
112                None => return Self::stored(source, |ctx| ctx.parse_raw(range)),
113            },
114            HeaderForm::Text => match unstructured::standalone(source, range) {
115                Ok(value) => value,
116                Err(pending) => return Self::stored(source, |ctx| pending.parse(ctx)),
117            },
118            HeaderForm::Addresses
119            | HeaderForm::MessageIds
120            | HeaderForm::CommaList
121            | HeaderForm::ContentType
122            | HeaderForm::Received => {
123                return Self::stored(source, |ctx| form.parse_field(ctx, range));
124            }
125        };
126        ParsedValue {
127            source,
128            data: None,
129            value,
130        }
131    }
132
133    fn stored(source: &'x [u8], parse: impl FnOnce(&mut FieldCtx<'_>) -> Value) -> Self {
134        let mut data = MessageData::default();
135        let value = parse(&mut FieldCtx::new(source, &mut data));
136        ParsedValue {
137            source,
138            data: Some(data),
139            value,
140        }
141    }
142
143    /// The parsed value.
144    pub fn value(&self) -> HeaderValue<'_> {
145        let data = self.data.as_ref().unwrap_or(&EMPTY_DATA);
146        HeaderValue::new(Resolver::new(self.source, &data.strings), data, self.value)
147    }
148}
149
150impl PartialEq for ParsedValue<'_> {
151    fn eq(&self, other: &Self) -> bool {
152        self.value() == other.value()
153    }
154}
155
156impl Eq for ParsedValue<'_> {}
157
158impl fmt::Debug for ParsedValue<'_> {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.debug_struct("ParsedValue")
161            .field("value", &self.value())
162            .finish()
163    }
164}
165
166pub(crate) fn first_field(source: &[u8], range: Range<usize>) -> Range<usize> {
167    let end = Kernel::MEMCHR
168        .field_end(source, range.start)
169        .map_or(range.end, |newline| range.end.min(newline + 1));
170    range.start..end
171}
172
173const FIRST_ADDRESSES: usize = 16;
174const FIRST_PARAMS: usize = 16;
175const FIRST_CONTENT_TYPES: usize = 16;
176const FIRST_RECEIVED: usize = 8;
177
178fn push_sized<T>(items: &mut Vec<T>, item: T, first: usize) {
179    if items.capacity() == 0 {
180        items.reserve_exact(first);
181    }
182    items.push(item);
183}
184
185pub(crate) fn push_utf8_lossy(out: &mut String, bytes: &[u8]) {
186    for chunk in bytes.utf8_chunks() {
187        out.push_str(chunk.valid());
188        if !chunk.invalid().is_empty() {
189            out.push(char::REPLACEMENT_CHARACTER);
190        }
191    }
192}
193
194pub(crate) fn is_fws(byte: u8) -> bool {
195    matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
196}
197
198pub(crate) fn trim_fws(src: &[u8], range: Range<usize>) -> Range<usize> {
199    let bytes = src.get(range.clone()).unwrap_or_default();
200    let start = bytes.iter().position(|&byte| !is_fws(byte));
201    let end = bytes.iter().rposition(|&byte| !is_fws(byte));
202    match (start, end) {
203        (Some(start), Some(end)) => range.start + start..range.start + end + 1,
204        _ => range.start..range.start,
205    }
206}
207
208pub(crate) struct FieldCtx<'a> {
209    src: &'a [u8],
210    data: &'a mut MessageData,
211    kernel: Kernel,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq)]
215pub(crate) struct GroupMark(u32);
216
217impl<'a> FieldCtx<'a> {
218    pub(crate) fn new(src: &'a [u8], data: &'a mut MessageData) -> Self {
219        FieldCtx::with_kernel(src, data, Kernel::best())
220    }
221
222    pub(crate) fn with_kernel(src: &'a [u8], data: &'a mut MessageData, kernel: Kernel) -> Self {
223        FieldCtx { src, data, kernel }
224    }
225
226    pub(crate) fn src(&self) -> &'a [u8] {
227        self.src
228    }
229
230    pub(crate) fn kernel(&self) -> Kernel {
231        self.kernel
232    }
233
234    pub(crate) fn bytes(&self, range: Range<usize>) -> &'a [u8] {
235        self.src.get(range).unwrap_or_default()
236    }
237
238    pub(crate) fn resolve(&self, text: Str) -> &str {
239        text.resolve(self.src, &self.data.strings)
240    }
241
242    pub(crate) fn try_borrow(&self, range: Range<usize>) -> Option<Str> {
243        Str::borrow(self.src, range)
244    }
245
246    pub(crate) fn borrow(&mut self, range: Range<usize>) -> Str {
247        match Str::borrow(self.src, range.clone()) {
248            Some(text) => text,
249            None => self.push_lossy(self.bytes(range)),
250        }
251    }
252
253    pub(crate) fn push_str(&mut self, text: &str) -> Str {
254        Str::push(&mut self.data.strings, text)
255    }
256
257    pub(crate) fn push_lossy(&mut self, bytes: &[u8]) -> Str {
258        Str::push_with(&mut self.data.strings, |pool| push_utf8_lossy(pool, bytes))
259    }
260
261    pub(crate) fn push_with(&mut self, write: impl FnOnce(&mut String)) -> Str {
262        Str::push_with(&mut self.data.strings, write)
263    }
264
265    pub(crate) fn lowercase(&mut self, range: Range<usize>) -> Str {
266        let upper = self.bytes(range.clone()).iter().any(u8::is_ascii_uppercase);
267        self.lowercase_scanned(range, upper)
268    }
269
270    pub(crate) fn lowercase_scanned(&mut self, range: Range<usize>, upper: bool) -> Str {
271        if !upper && let Some(text) = Str::borrow(self.src, range.clone()) {
272            return text;
273        }
274        let bytes = self.bytes(range);
275        Str::push_with(&mut self.data.strings, |pool| {
276            let start = pool.len();
277            push_utf8_lossy(pool, bytes);
278            if let Some(pushed) = pool.get_mut(start..) {
279                pushed.make_ascii_lowercase();
280            }
281        })
282    }
283
284    pub(crate) fn trim_fws(&self, range: Range<usize>) -> Range<usize> {
285        trim_fws(self.src, range)
286    }
287
288    pub(crate) fn take_text_scratch(&mut self) -> String {
289        let mut text = std::mem::take(&mut self.data.scratch.text);
290        text.clear();
291        text
292    }
293
294    pub(crate) fn put_text_scratch(&mut self, text: String) {
295        self.data.scratch.text = text;
296    }
297
298    pub(crate) fn take_bytes_scratch(&mut self) -> Vec<u8> {
299        let mut bytes = std::mem::take(&mut self.data.scratch.bytes);
300        bytes.clear();
301        bytes
302    }
303
304    pub(crate) fn put_bytes_scratch(&mut self, bytes: Vec<u8>) {
305        self.data.scratch.bytes = bytes;
306    }
307
308    pub(crate) fn text_items_mark(&self) -> u32 {
309        self.data.text_items.len() as u32
310    }
311
312    pub(crate) fn push_text_item(&mut self, item: Str) {
313        self.data.push_text_item(item);
314    }
315
316    pub(crate) fn text_list(&self, mark: u32) -> Value {
317        match (self.data.text_items.len() as u32).saturating_sub(mark) {
318            0 => Value::Empty,
319            len => Value::TextList(Span { start: mark, len }),
320        }
321    }
322
323    pub(crate) fn address_mark(&self) -> u32 {
324        self.data.address_items.len() as u32
325    }
326
327    pub(crate) fn push_mailbox(&mut self, name: Option<Str>, address: Option<Str>) {
328        self.data.address_items.push(
329            AddressEntry {
330                name: Str::from_option(name),
331                address: Str::from_option(address),
332                group: 0,
333            },
334            FIRST_ADDRESSES,
335        );
336    }
337
338    pub(crate) fn push_group(&mut self, name: Option<Str>) -> GroupMark {
339        let mark = GroupMark(self.data.address_items.len() as u32);
340        self.data.address_items.push(
341            AddressEntry {
342                name: Str::from_option(name),
343                address: Str::NONE,
344                group: GROUP_BIT,
345            },
346            FIRST_ADDRESSES,
347        );
348        mark
349    }
350
351    pub(crate) fn end_group(&mut self, group: GroupMark) {
352        let members = (self.data.address_items.len() as u32).saturating_sub(group.0 + 1);
353        if let Some(entry) = self.data.address_items.get_mut(group.0 as usize) {
354            entry.group = GROUP_BIT | members;
355        }
356    }
357
358    pub(crate) fn address_list(&self, mark: u32) -> Value {
359        let len = (self.data.address_items.len() as u32).saturating_sub(mark);
360        if len == 0 {
361            Value::Empty
362        } else {
363            Value::Address(Span { start: mark, len })
364        }
365    }
366
367    pub(crate) fn params_mark(&self) -> u32 {
368        self.data.params.len() as u32
369    }
370
371    pub(crate) fn push_param(&mut self, name: Str, value: Str) {
372        self.data
373            .params
374            .push(ParamEntry { name, value }, FIRST_PARAMS);
375    }
376
377    pub(crate) fn params_since(&self, mark: u32) -> &[ParamEntry] {
378        self.data.params.get(mark as usize..).unwrap_or_default()
379    }
380
381    pub(crate) fn set_param_value(&mut self, index: u32, value: Str) {
382        if let Some(param) = self.data.params.get_mut(index as usize) {
383            param.value = value;
384        }
385    }
386
387    pub(crate) fn rollback_params(&mut self, mark: u32) {
388        self.data.params.truncate(mark as usize);
389    }
390
391    pub(crate) fn remove_param(&mut self, index: u32) {
392        if let Some(param) = self.data.params.get_mut(index as usize) {
393            param.name = Str::NONE;
394        }
395    }
396
397    pub(crate) fn drop_removed_params(&mut self, mark: u32) {
398        let params = &mut self.data.params;
399        let mut kept = mark as usize;
400        for index in mark as usize..params.len() {
401            let Some(&param) = params.get(index) else {
402                break;
403            };
404            if param.name.is_some() {
405                if let Some(slot) = params.get_mut(kept) {
406                    *slot = param;
407                }
408                kept += 1;
409            }
410        }
411        params.truncate(kept);
412    }
413
414    pub(crate) fn content_type(&mut self, ctype: Str, subtype: Option<Str>, params: u32) -> Value {
415        let index = self.data.content_types.len() as u32;
416        let params = Span {
417            start: params,
418            len: (self.data.params.len() as u32).saturating_sub(params),
419        };
420        self.data.content_types.push(
421            ContentTypeEntry {
422                ctype,
423                subtype: Str::from_option(subtype),
424                params,
425            },
426            FIRST_CONTENT_TYPES,
427        );
428        Value::ContentType(index)
429    }
430
431    pub(crate) fn received(&mut self, entry: ReceivedEntry) -> Value {
432        let index = self.data.received.len() as u32;
433        push_sized(&mut self.data.received, entry, FIRST_RECEIVED);
434        Value::Received(index)
435    }
436}