diff --git a/Cargo.toml b/Cargo.toml index 248de6f..b7f3aa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "mailparse" version = "0.16.1" authors = ["Kartikaya Gupta"] -edition = "2018" +edition = "2024" license = "0BSD" description = "A simple parser for MIME e-mail messages" @@ -22,4 +22,4 @@ quoted_printable = "0.5.0" charset = "0.1.3" [dev-dependencies] -ouroboros = "0.17.0" +ouroboros = "0.18.0" diff --git a/examples/dump_mail.rs b/examples/dump_mail.rs index 30f83cb..a9b40de 100644 --- a/examples/dump_mail.rs +++ b/examples/dump_mail.rs @@ -1,5 +1,3 @@ -extern crate mailparse; - use std::env; use std::fs::File; use std::io::prelude::*; @@ -12,18 +10,18 @@ fn dump(pfx: &str, pm: &mailparse::ParsedMail) { println!(" [{}] => [{}]", h.get_key(), h.get_value()); } println!(">> Addresses from {} <<", pfx); - pm.headers - .get_first_value("From") - .map(|a| println!("{:?}", mailparse::addrparse(&a).unwrap())); - pm.headers - .get_first_value("To") - .map(|a| println!("{:?}", mailparse::addrparse(&a).unwrap())); - pm.headers - .get_first_value("Cc") - .map(|a| println!("{:?}", mailparse::addrparse(&a).unwrap())); - pm.headers - .get_first_value("Bcc") - .map(|a| println!("{:?}", mailparse::addrparse(&a).unwrap())); + if let Some(a) = pm.headers.get_first_value("From") { + println!("{:?}", mailparse::addrparse(&a).unwrap()) + } + if let Some(a) = pm.headers.get_first_value("To") { + println!("{:?}", mailparse::addrparse(&a).unwrap()) + } + if let Some(a) = pm.headers.get_first_value("Cc") { + println!("{:?}", mailparse::addrparse(&a).unwrap()) + } + if let Some(a) = pm.headers.get_first_value("Bcc") { + println!("{:?}", mailparse::addrparse(&a).unwrap()) + } println!(">> Body from {} <<", pfx); if pm.ctype.mimetype.starts_with("text/") { println!(" [{}]", pm.get_body().unwrap()); diff --git a/examples/owned.rs b/examples/owned.rs index 98daddd..5bc8188 100644 --- a/examples/owned.rs +++ b/examples/owned.rs @@ -1,7 +1,4 @@ -extern crate mailparse; -extern crate ouroboros; - -use mailparse::{parse_mail, ParsedMail}; +use mailparse::{ParsedMail, parse_mail}; use ouroboros::self_referencing; #[self_referencing] diff --git a/src/body.rs b/src/body.rs index b70e95b..dcb6a84 100644 --- a/src/body.rs +++ b/src/body.rs @@ -1,4 +1,4 @@ -use charset::{decode_ascii, Charset}; +use charset::{Charset, decode_ascii}; use crate::{MailParseError, ParsedContentType}; diff --git a/src/dateparse.rs b/src/dateparse.rs index a956f3b..eb35380 100644 --- a/src/dateparse.rs +++ b/src/dateparse.rs @@ -80,7 +80,7 @@ pub fn dateparse(date: &str) -> Result { let mut month = 0; let mut day_of_month = 0; let mut state = DateParseState::Date; - for tok in date.split(|c| c == ' ' || c == ':') { + for tok in date.split([' ', ':']) { if tok.is_empty() { continue; } @@ -157,7 +157,7 @@ pub fn dateparse(date: &str) -> Result { DateParseState::Timezone => { let (tz, tz_sign) = match tok.parse::() { Ok(v) if !(-2400..=2400).contains(&v) => { - return Err(MailParseError::Generic("Invalid timezone")) + return Err(MailParseError::Generic("Invalid timezone")); } Ok(v) if v < 0 => (-v, -1), Ok(v) => (v, 1), diff --git a/src/header.rs b/src/header.rs index b3e4c32..1fac06c 100644 --- a/src/header.rs +++ b/src/header.rs @@ -54,13 +54,12 @@ fn decode_word(encoded: &str) -> Option { // whitespace let to_decode = input.replace('_', " "); let trimmed = to_decode.trim_end(); - let mut d = quoted_printable::decode(trimmed, quoted_printable::ParseMode::Robust); - if d.is_ok() && to_decode.len() != trimmed.len() { - d.as_mut() - .unwrap() - .extend_from_slice(to_decode[trimmed.len()..].as_bytes()); + let mut decoded = + quoted_printable::decode(trimmed, quoted_printable::ParseMode::Robust).ok()?; + if to_decode.len() != trimmed.len() { + decoded.extend_from_slice(&to_decode.as_bytes()[trimmed.len()..]); } - d.ok()? + decoded } _ => return None, }; @@ -72,8 +71,8 @@ fn decode_word(encoded: &str) -> Option { /// Tokenizes a single line of the header and produces a vector of /// tokens. Because this only processes a single line, it will never /// generate `HeaderToken::Newline` tokens. -fn tokenize_header_line(line: &str) -> Vec { - fn maybe_whitespace(text: &str) -> HeaderToken { +fn tokenize_header_line(line: &str) -> Vec> { + fn maybe_whitespace(text: &str) -> HeaderToken<'_> { if text.trim_end().is_empty() { HeaderToken::Whitespace(text) } else { @@ -134,7 +133,7 @@ fn tokenize_header_line(line: &str) -> Vec { /// the newline will be in a separate `HeaderToken::Whitespace` or /// `HeaderToken::Text` token. Semantically the `HeaderToken::Newline` /// tokens that come out of this still represent the CRLF newline. -fn tokenize_header(value: &str) -> Vec { +fn tokenize_header(value: &str) -> Vec> { let mut tokens = Vec::new(); let mut lines = value.lines(); let mut first = true; @@ -216,7 +215,7 @@ fn normalize_header_whitespace(tokens: Vec) -> Vec { result } -pub fn normalized_tokens(raw_value: &str) -> Vec { +pub fn normalized_tokens(raw_value: &str) -> Vec> { normalize_header_whitespace(tokenize_header(raw_value)) } diff --git a/src/headers.rs b/src/headers.rs index e05365c..3937272 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -53,7 +53,7 @@ impl<'a> IntoIterator for Headers<'a> { type IntoIter = slice::Iter<'a, MailHeader<'a>>; fn into_iter(self) -> Self::IntoIter { - self.headers.into_iter() + self.headers.iter() } } @@ -96,7 +96,7 @@ impl<'a> MailHeaderMap for Headers<'a> { self.headers.get_first_value(key) } - fn get_first_header(&self, key: &str) -> Option<&MailHeader> { + fn get_first_header(&self, key: &str) -> Option<&MailHeader<'_>> { self.headers.get_first_header(key) } @@ -114,7 +114,7 @@ impl<'a> MailHeaderMap for Headers<'a> { self.headers.get_all_values(key) } - fn get_all_headers(&self, key: &str) -> Vec<&MailHeader> { + fn get_all_headers(&self, key: &str) -> Vec<&MailHeader<'_>> { self.headers.get_all_headers(key) } } diff --git a/src/lib.rs b/src/lib.rs index 6756948..d914823 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,11 @@ #![forbid(unsafe_code)] -extern crate charset; -extern crate data_encoding; -extern crate quoted_printable; - use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::error; use std::fmt; -use charset::{decode_latin1, Charset}; +use charset::{Charset, decode_latin1}; mod addrparse; pub mod body; @@ -19,13 +15,13 @@ pub mod headers; mod msgidparse; pub use crate::addrparse::{ - addrparse, addrparse_header, GroupInfo, MailAddr, MailAddrList, SingleInfo, + GroupInfo, MailAddr, MailAddrList, SingleInfo, addrparse, addrparse_header, }; use crate::body::Body; pub use crate::dateparse::dateparse; use crate::header::HeaderToken; use crate::headers::Headers; -pub use crate::msgidparse::{msgidparse, MessageIdList}; +pub use crate::msgidparse::{MessageIdList, msgidparse}; /// An error type that represents the different kinds of errors that may be /// encountered during message parsing. @@ -192,7 +188,7 @@ impl<'a> MailHeader<'a> { /// Get the name of the header, borrowing if it's ASCII-only. /// Note that header names are case-insensitive. - pub fn get_key_ref(&self) -> Cow { + pub fn get_key_ref(&self) -> Cow<'_, str> { decode_latin1(self.key) } @@ -332,7 +328,7 @@ enum HeaderParseState { /// assert_eq!(parsed.get_key(), "Subject"); /// assert_eq!(parsed.get_value(), "Hello, sir, I am multiline"); /// ``` -pub fn parse_header(raw_data: &[u8]) -> Result<(MailHeader, usize), MailParseError> { +pub fn parse_header(raw_data: &[u8]) -> Result<(MailHeader<'_>, usize), MailParseError> { let mut it = raw_data.iter(); let mut ix = 0; let mut c = match it.next() { @@ -451,7 +447,7 @@ pub trait MailHeaderMap { /// Similar to `get_first_value`, except it returns a reference to the /// MailHeader struct instead of just extracting the value. - fn get_first_header(&self, key: &str) -> Option<&MailHeader>; + fn get_first_header(&self, key: &str) -> Option<&MailHeader<'_>>; /// Look through the list of headers and return the values of all headers /// matching the provided key. Returns an empty vector if no matching headers @@ -473,7 +469,7 @@ pub trait MailHeaderMap { /// Similar to `get_all_values`, except it returns references to the /// MailHeader structs instead of just extracting the values. - fn get_all_headers(&self, key: &str) -> Vec<&MailHeader>; + fn get_all_headers(&self, key: &str) -> Vec<&MailHeader<'_>>; } impl<'a> MailHeaderMap for [MailHeader<'a>] { @@ -486,7 +482,7 @@ impl<'a> MailHeaderMap for [MailHeader<'a>] { None } - fn get_first_header(&self, key: &str) -> Option<&MailHeader> { + fn get_first_header(&self, key: &str) -> Option<&MailHeader<'_>> { self.iter() .find(|&x| x.get_key_ref().eq_ignore_ascii_case(key)) } @@ -501,7 +497,7 @@ impl<'a> MailHeaderMap for [MailHeader<'a>] { values } - fn get_all_headers(&self, key: &str) -> Vec<&MailHeader> { + fn get_all_headers(&self, key: &str) -> Vec<&MailHeader<'_>> { let mut headers: Vec<&MailHeader> = Vec::new(); for x in self { if x.get_key_ref().eq_ignore_ascii_case(key) { @@ -535,7 +531,7 @@ impl<'a> MailHeaderMap for [MailHeader<'a>] { /// assert_eq!(headers[1].get_key(), "From"); /// assert_eq!(headers.get_first_value("To"), Some("you@yourself.com".to_string())); /// ``` -pub fn parse_headers(raw_data: &[u8]) -> Result<(Vec, usize), MailParseError> { +pub fn parse_headers(raw_data: &[u8]) -> Result<(Vec>, usize), MailParseError> { let mut headers: Vec = Vec::new(); let mut ix = 0; loop { @@ -655,10 +651,11 @@ pub fn parse_content_type(header: &str) -> ParsedContentType { /// https://www.iana.org/assignments/cont-disp/cont-disp.xhtml. This library /// only enumerates the types most commonly found in email messages, and /// provides the `Extension` value for holding all other types. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Default)] pub enum DispositionType { /// Default value, indicating the content is to be displayed inline as /// part of the enclosing document. + #[default] Inline, /// A disposition indicating the content is not meant for inline display, /// but whose content can be accessed for use. @@ -669,12 +666,6 @@ pub enum DispositionType { Extension(String), } -impl Default for DispositionType { - fn default() -> Self { - DispositionType::Inline - } -} - /// Convert the string represented disposition type to enum. fn parse_disposition_type(disposition: &str) -> DispositionType { match &disposition.to_lowercase()[..] { @@ -815,7 +806,7 @@ impl<'a> ParsedMail<'a> { /// assert_eq!(body.get_decoded().unwrap(), b"hello world"); /// assert_eq!(body.get_decoded_as_string().unwrap(), "hello world"); /// }, - /// _ => assert!(false), + /// _ => panic!(), /// }; /// /// @@ -933,7 +924,7 @@ impl<'a> Iterator for PartsIterator<'a> { /// assert!(parsed.subparts[1].get_body().unwrap().starts_with("")); /// assert_eq!(dateparse(parsed.headers.get_first_value("Date").unwrap().as_str()).unwrap(), 1475417182); /// ``` -pub fn parse_mail(raw_data: &[u8]) -> Result { +pub fn parse_mail(raw_data: &[u8]) -> Result, MailParseError> { parse_mail_recursive(raw_data, false) } @@ -954,7 +945,7 @@ fn strip_trailing_crlf(raw_data: &[u8], ix_start: usize, mut ix: usize) -> usize fn parse_mail_recursive( raw_data: &[u8], in_multipart_digest: bool, -) -> Result { +) -> Result, MailParseError> { let (headers, ix_body) = parse_headers(raw_data)?; let ctype = headers .get_first_value("Content-Type") @@ -970,7 +961,7 @@ fn parse_mail_recursive( subparts: Vec::::new(), }; if result.ctype.mimetype.starts_with("multipart/") - && result.ctype.params.get("boundary").is_some() + && result.ctype.params.contains_key("boundary") && raw_data.len() > ix_body { let in_multipart_digest = result.ctype.mimetype == "multipart/digest"; @@ -1727,8 +1718,8 @@ mod tests { parse_param_content("attachment;\n\tfilename*0=\"X\";\n\tfilename*1=\"Y.pdf\""); assert_eq!(parsed.value, "attachment"); assert_eq!(parsed.params["filename"], "XY.pdf"); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*1"), false); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*1")); let parsed = parse_param_content( "attachment;\n\tfilename=XX.pdf;\n\tfilename*0=\"X\";\n\tfilename*1=\"Y.pdf\"", @@ -1740,76 +1731,80 @@ mod tests { let parsed = parse_param_content("attachment; filename*1=\"Y.pdf\""); assert_eq!(parsed.params["filename*1"], "Y.pdf"); - assert_eq!(parsed.params.contains_key("filename"), false); + assert!(!parsed.params.contains_key("filename")); } #[test] fn test_parameter_encodings() { - let parsed = parse_param_content("attachment;\n\tfilename*0*=us-ascii''%28X%29%20801%20-%20X;\n\tfilename*1*=%20%E2%80%93%20X%20;\n\tfilename*2*=X%20X%2Epdf"); + let parsed = parse_param_content( + "attachment;\n\tfilename*0*=us-ascii''%28X%29%20801%20-%20X;\n\tfilename*1*=%20%E2%80%93%20X%20;\n\tfilename*2*=X%20X%2Epdf", + ); // Note this is a real-world case from mutt, but it's wrong. The original filename had an en dash \u{2013} but mutt // declared us-ascii as the encoding instead of utf-8 for some reason. assert_eq!( parsed.params["filename"], "(X) 801 - X \u{00E2}\u{20AC}\u{201C} X X X.pdf" ); - assert_eq!(parsed.params.contains_key("filename*0*"), false); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*1*"), false); - assert_eq!(parsed.params.contains_key("filename*1"), false); - assert_eq!(parsed.params.contains_key("filename*2*"), false); - assert_eq!(parsed.params.contains_key("filename*2"), false); + assert!(!parsed.params.contains_key("filename*0*")); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*1*")); + assert!(!parsed.params.contains_key("filename*1")); + assert!(!parsed.params.contains_key("filename*2*")); + assert!(!parsed.params.contains_key("filename*2")); // Here is the corrected version. - let parsed = parse_param_content("attachment;\n\tfilename*0*=utf-8''%28X%29%20801%20-%20X;\n\tfilename*1*=%20%E2%80%93%20X%20;\n\tfilename*2*=X%20X%2Epdf"); + let parsed = parse_param_content( + "attachment;\n\tfilename*0*=utf-8''%28X%29%20801%20-%20X;\n\tfilename*1*=%20%E2%80%93%20X%20;\n\tfilename*2*=X%20X%2Epdf", + ); assert_eq!(parsed.params["filename"], "(X) 801 - X \u{2013} X X X.pdf"); - assert_eq!(parsed.params.contains_key("filename*0*"), false); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*1*"), false); - assert_eq!(parsed.params.contains_key("filename*1"), false); - assert_eq!(parsed.params.contains_key("filename*2*"), false); - assert_eq!(parsed.params.contains_key("filename*2"), false); + assert!(!parsed.params.contains_key("filename*0*")); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*1*")); + assert!(!parsed.params.contains_key("filename*1")); + assert!(!parsed.params.contains_key("filename*2*")); + assert!(!parsed.params.contains_key("filename*2")); let parsed = parse_param_content("attachment; filename*=utf-8'en'%e2%80%A1.bin"); assert_eq!(parsed.params["filename"], "\u{2021}.bin"); - assert_eq!(parsed.params.contains_key("filename*"), false); + assert!(!parsed.params.contains_key("filename*")); let parsed = parse_param_content("attachment; filename*='foo'%e2%80%A1.bin"); assert_eq!(parsed.params["filename*"], "'foo'%e2%80%A1.bin"); - assert_eq!(parsed.params.contains_key("filename"), false); + assert!(!parsed.params.contains_key("filename")); let parsed = parse_param_content("attachment; filename*=nonexistent'foo'%e2%80%a1.bin"); assert_eq!(parsed.params["filename*"], "nonexistent'foo'%e2%80%a1.bin"); - assert_eq!(parsed.params.contains_key("filename"), false); + assert!(!parsed.params.contains_key("filename")); let parsed = parse_param_content( "attachment; filename*0*=utf-8'en'%e2%80%a1; filename*1*=%e2%80%A1.bin", ); assert_eq!(parsed.params["filename"], "\u{2021}\u{2021}.bin"); - assert_eq!(parsed.params.contains_key("filename*0*"), false); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*1*"), false); - assert_eq!(parsed.params.contains_key("filename*1"), false); + assert!(!parsed.params.contains_key("filename*0*")); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*1*")); + assert!(!parsed.params.contains_key("filename*1")); let parsed = parse_param_content("attachment; filename*0*=utf-8'en'%e2%80%a1; filename*1=%20.bin"); assert_eq!(parsed.params["filename"], "\u{2021}%20.bin"); - assert_eq!(parsed.params.contains_key("filename*0*"), false); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*1*"), false); - assert_eq!(parsed.params.contains_key("filename*1"), false); + assert!(!parsed.params.contains_key("filename*0*")); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*1*")); + assert!(!parsed.params.contains_key("filename*1")); let parsed = parse_param_content("attachment; filename*0*=utf-8'en'%e2%80%a1; filename*2*=%20.bin"); assert_eq!(parsed.params["filename"], "\u{2021}"); assert_eq!(parsed.params["filename*2"], " .bin"); - assert_eq!(parsed.params.contains_key("filename*0*"), false); - assert_eq!(parsed.params.contains_key("filename*0"), false); - assert_eq!(parsed.params.contains_key("filename*2*"), false); + assert!(!parsed.params.contains_key("filename*0*")); + assert!(!parsed.params.contains_key("filename*0")); + assert!(!parsed.params.contains_key("filename*2*")); let parsed = parse_param_content("attachment; filename*0*=utf-8'en'%e2%80%a1; filename*0=foo.bin"); assert_eq!(parsed.params["filename"], "foo.bin"); assert_eq!(parsed.params["filename*0*"], "utf-8'en'%e2%80%a1"); - assert_eq!(parsed.params.contains_key("filename*0"), false); + assert!(!parsed.params.contains_key("filename*0")); } #[test] @@ -1821,7 +1816,7 @@ mod tests { assert_eq!(body.get_raw(), b"+JgM-"); assert_eq!(body.get_as_string().unwrap(), "\u{2603}"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1834,7 +1829,7 @@ mod tests { assert_eq!(body.get_raw(), b"+JgM-"); assert_eq!(body.get_as_string().unwrap(), "\u{2603}"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1847,7 +1842,7 @@ mod tests { assert_eq!(body.get_raw(), b"+JgM-"); assert_eq!(body.get_as_string().unwrap(), "\u{2603}"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1862,7 +1857,7 @@ mod tests { assert_eq!(body.get_decoded().unwrap(), b"+JgM-"); assert_eq!(body.get_decoded_as_string().unwrap(), "\u{2603}"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1876,7 +1871,7 @@ mod tests { assert_eq!(body.get_decoded().unwrap(), b"hello world"); assert_eq!(body.get_decoded_as_string().unwrap(), "hello world"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1892,7 +1887,7 @@ mod tests { assert_eq!(body.get_decoded().unwrap(), b"hello worldfoo\n"); assert_eq!(body.get_decoded_as_string().unwrap(), "hello worldfoo\n"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1904,7 +1899,7 @@ mod tests { Body::Binary(body) => { assert_eq!(body.get_raw(), b"######"); } - _ => assert!(false), + _ => panic!(), }; } @@ -1912,10 +1907,10 @@ mod tests { fn test_body_content_encoding_with_multipart() { let mail_filepath = "./tests/files/test_email_01.txt"; let mail = std::fs::read(mail_filepath) - .expect(&format!("Unable to open the file [{}]", mail_filepath)); + .unwrap_or_else(|_| panic!("Unable to open the file [{}]", mail_filepath)); let mail = parse_mail(&mail).unwrap(); - let subpart_0 = mail.subparts.get(0).unwrap(); + let subpart_0 = mail.subparts.first().unwrap(); match subpart_0.get_body_encoded() { Body::SevenBit(body) => { assert_eq!( @@ -1923,7 +1918,7 @@ mod tests { "Test with attachments" ); } - _ => assert!(false), + _ => panic!(), }; let subpart_1 = mail.subparts.get(1).unwrap(); @@ -1931,10 +1926,10 @@ mod tests { Body::Base64(body) => { let pdf_filepath = "./tests/files/test_email_01_sample.pdf"; let original_pdf = std::fs::read(pdf_filepath) - .expect(&format!("Unable to open the file [{}]", pdf_filepath)); + .unwrap_or_else(|_| panic!("Unable to open the file [{}]", pdf_filepath)); assert_eq!(body.get_decoded().unwrap(), original_pdf); } - _ => assert!(false), + _ => panic!(), }; let subpart_2 = mail.subparts.get(2).unwrap(); @@ -1945,7 +1940,7 @@ mod tests { "txt file context for email collector\n1234567890987654321\n" ); } - _ => assert!(false), + _ => panic!(), }; } @@ -1953,20 +1948,20 @@ mod tests { fn test_fuzzer_testcase() { const INPUT: &str = "U3ViamVjdDplcy1UeXBlOiBtdW50ZW50LVV5cGU6IW11bAAAAAAAAAAAamVjdDplcy1UeXBlOiBtdW50ZW50LVV5cGU6IG11bAAAAAAAAAAAAAAAAABTTUFZdWJqZf86OiP/dCBTdWJqZWN0Ol8KRGF0ZTog/////////////////////wAAAAAAAAAAAHQgYnJmAHQgYnJmZXItRW5jeXBlOnY9NmU3OjA2OgAAAAAAAAAAAAAAADEAAAAAAP/8mAAAAAAAAAAA+f///wAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAPT0/PzEAAAEAAA=="; - if let Ok(parsed) = parse_mail(&data_encoding::BASE64.decode(INPUT.as_bytes()).unwrap()) { - if let Some(date) = parsed.headers.get_first_value("Date") { - let _ = dateparse(&date); - } + if let Ok(parsed) = parse_mail(&data_encoding::BASE64.decode(INPUT.as_bytes()).unwrap()) + && let Some(date) = parsed.headers.get_first_value("Date") + { + let _ = dateparse(&date); } } #[test] fn test_fuzzer_testcase_2() { const INPUT: &str = "U3ViamVjdDogVGhpcyBpcyBhIHRlc3QgZW1haWwKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PczMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMZm9vYmFyCkRhdGU6IFN1biwgMDIgT2MKCi1TdWJqZWMtZm9vYmFydDo="; - if let Ok(parsed) = parse_mail(&data_encoding::BASE64.decode(INPUT.as_bytes()).unwrap()) { - if let Some(date) = parsed.headers.get_first_value("Date") { - let _ = dateparse(&date); - } + if let Ok(parsed) = parse_mail(&data_encoding::BASE64.decode(INPUT.as_bytes()).unwrap()) + && let Some(date) = parsed.headers.get_first_value("Date") + { + let _ = dateparse(&date); } } @@ -2159,7 +2154,7 @@ mod tests { assert_eq!(parts.next().unwrap().ctype.mimetype, "text/unknown"); assert!(parts.next().is_none()); - let mail = parse_mail(concat!("Content-Type: text/plain\n").as_bytes()).unwrap(); + let mail = parse_mail("Content-Type: text/plain\n".as_bytes()).unwrap(); let mut parts = mail.parts(); assert_eq!(parts.next().unwrap().ctype.mimetype, "text/plain");