1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// 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 //! Convenience functionality for regex searches of email data. use std::str; use regex::bytes::{RegexBuilder, RegexSetBuilder, SetMatches, Captures}; use crate::Result; /// Trait providing convenience methods for regular expression searching /// in emails. The trait methods can be use with the byte data returned by /// the `Email::header`, `Email::body` and `Email::data` methods. /// /// This trait treats and searches the email contents as bytes. The regular /// expression parsing is configured for case-insensitive and multi-line /// search (i.e., `^` and `$` match beginning and end of lines respectively). /// /// In addition to the single regular expression searching, a method for /// matching regular expression sets is provided. This can be more /// efficient than matching multiple regular expressions independently. /// /// All the trait methods will fail if the regular expression is /// invalid, or the searched email data isn't valid utf-8. pub trait EmailRegex { /// Returns whether the contents match a regular expression. /// /// # Example /// /// ```no_run /// use mda::{Email, EmailRegex}; /// let email = Email::from_stdin()?; /// if email.header().search(r"^To:.*me@example.com")? { /// email.deliver_to_maildir("/my/maildir/path")?; /// } /// # Ok::<(), Box<dyn std::error::Error>>(()) /// ``` fn search(&self, regex: &str) -> Result<bool>; /// Returns the capture groups matched from a regular expression. /// /// # Example /// /// ```no_run /// use std::path::Path; /// use mda::{Email, EmailRegex}; /// let email = Email::from_stdin()?; /// if let Some(captures) = email.header().search_with_captures(r"^X-Product: name=(\w+)")? { /// let name = std::str::from_utf8(captures.get(1).unwrap().as_bytes()).unwrap(); /// email.deliver_to_maildir(Path::new("/my/maildir/").join(name))?; /// } /// # Ok::<(), Box<dyn std::error::Error>>(()) /// ``` fn search_with_captures(&self, regex: &str) -> Result<Option<Captures>>; /// Returns the matches from a set of regular expression. This can be /// more efficient than matching multiple regular expressions independently. /// /// # Example /// /// ```no_run /// use mda::{Email, EmailRegex}; /// let email = Email::from_stdin()?; /// let matched_sets = email.header().search_set( /// &[ /// r"^To: confidential <confidential@example.com>", /// r"^X-Confidential: true", /// ] /// )?; /// if matched_sets.matched_any() { /// email.deliver_to_maildir("/my/mail/confidential/")?; /// } /// # Ok::<(), Box<dyn std::error::Error>>(()) /// ``` fn search_set(&self, regex_set: &[&str]) -> Result<SetMatches>; } impl EmailRegex for &[u8] { fn search(&self, regex: &str) -> Result<bool> { Ok( RegexBuilder::new(regex) .multi_line(true) .case_insensitive(true) .build()? .is_match(self) ) } fn search_with_captures(&self, regex: &str) -> Result<Option<Captures>> { Ok( RegexBuilder::new(regex) .multi_line(true) .case_insensitive(true) .build()? .captures(self) ) } fn search_set(&self, regex_set: &[&str]) -> Result<SetMatches> { Ok( RegexSetBuilder::new(regex_set) .multi_line(true) .case_insensitive(true) .build()? .matches(self) ) } }