diff --git a/src/aheader.rs b/src/aheader.rs index 609f70ee9..1fed1a3cf 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -8,6 +8,7 @@ use std::{fmt, str}; use crate::contact::*; use crate::context::Context; +use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::key::{DcKey, SignedPublicKey}; /// Possible values for encryption preference @@ -74,9 +75,7 @@ impl Aheader { wanted_from: &str, headers: &[mailparse::MailHeader<'_>], ) -> Option { - use mailparse::MailHeaderMap; - - if let Ok(Some(value)) = headers.get_first_value("Autocrypt") { + if let Ok(Some(value)) = headers.get_headerdef(HeaderDef::Autocrypt) { match Self::from_str(&value) { Ok(header) => { if addr_cmp(&header.addr, wanted_from) { diff --git a/src/e2ee.rs b/src/e2ee.rs index d19b9e965..6be9d9cb2 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use std::convert::TryFrom; -use mailparse::{MailHeaderMap, ParsedMail}; +use mailparse::ParsedMail; use num_traits::FromPrimitive; use crate::aheader::*; @@ -11,6 +11,7 @@ use crate::config::Config; use crate::context::Context; use crate::dc_tools::EmailAddress; use crate::error::*; +use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::key::{self, Key, KeyPairUse, SignedPublicKey}; use crate::keyring::*; use crate::peerstate::*; @@ -124,7 +125,7 @@ pub fn try_decrypt( ) -> Result<(Option>, HashSet)> { let from = mail .headers - .get_first_value("From")? + .get_headerdef(HeaderDef::From_)? .and_then(|from_addr| mailparse::addrparse(&from_addr).ok()) .and_then(|from| from.extract_single_info()) .map(|from| from.addr) diff --git a/src/headerdef.rs b/src/headerdef.rs index 0bf70caf5..a7b16b664 100644 --- a/src/headerdef.rs +++ b/src/headerdef.rs @@ -1,4 +1,5 @@ use crate::strum::AsStaticRef; +use mailparse::{MailHeader, MailHeaderMap, MailParseError}; #[derive(Debug, Display, Clone, PartialEq, Eq, EnumVariantNames, AsStaticStr)] #[strum(serialize_all = "kebab_case")] @@ -34,6 +35,7 @@ pub enum HeaderDef { ChatContent, ChatDuration, ChatDispositionNotificationTo, + Autocrypt, AutocryptSetupMessage, SecureJoin, SecureJoinGroup, @@ -50,6 +52,16 @@ impl HeaderDef { } } +pub trait HeaderDefMap { + fn get_headerdef(&self, headerdef: HeaderDef) -> Result, MailParseError>; +} + +impl HeaderDefMap for [MailHeader<'_>] { + fn get_headerdef(&self, headerdef: HeaderDef) -> Result, MailParseError> { + self.get_first_value(headerdef.get_headername()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -61,4 +73,22 @@ mod tests { assert_eq!(HeaderDef::_TestHeader.get_headername(), "test-header"); } + + #[test] + /// Test that headers are parsed case-insensitively + fn headerdef_case() { + let (headers, _) = + mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap(); + assert_eq!( + headers + .get_headerdef(HeaderDef::AutocryptSetupMessage) + .unwrap(), + Some("v99".to_string()) + ); + assert_eq!( + headers.get_headerdef(HeaderDef::From_).unwrap(), + Some("Bob".to_string()) + ); + assert_eq!(headers.get_headerdef(HeaderDef::Autocrypt).unwrap(), None); + } } diff --git a/src/imap/mod.rs b/src/imap/mod.rs index f6b5d21a6..7094473db 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -14,8 +14,6 @@ use async_imap::{ use async_std::sync::{Mutex, RwLock}; use async_std::task; -use mailparse::MailHeaderMap; - use crate::config::*; use crate::constants::*; use crate::context::Context; @@ -23,7 +21,7 @@ use crate::dc_receive_imf::{ dc_receive_imf, from_field_to_contact_id, is_msgrmsg_rfc724_mid_in_list, }; use crate::events::Event; -use crate::headerdef::HeaderDef; +use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::imap_client::*; use crate::job::{job_add, Action}; use crate::login_param::{CertificateChecks, LoginParam}; @@ -1304,31 +1302,24 @@ fn get_fetch_headers(prefetch_msg: &Fetch) -> Result> } fn prefetch_get_message_id(headers: &[mailparse::MailHeader]) -> Result { - if let Some(message_id) = headers.get_first_value(&HeaderDef::MessageId.get_headername())? { + if let Some(message_id) = headers.get_headerdef(HeaderDef::MessageId)? { Ok(parse_message_id(&message_id)?) } else { Err(Error::Other("prefetch: No message ID found".to_string())) } } -/// Checks if fetch result contains a header -fn prefetch_has_header(headers: &[mailparse::MailHeader], headerdef: HeaderDef) -> Result { - Ok(headers - .get_first_value(&headerdef.get_headername())? - .is_some()) -} - fn prefetch_is_reply_to_chat_message( context: &Context, headers: &[mailparse::MailHeader], ) -> Result { - if let Some(value) = headers.get_first_value(&HeaderDef::InReplyTo.get_headername())? { + if let Some(value) = headers.get_headerdef(HeaderDef::InReplyTo)? { if is_msgrmsg_rfc724_mid_in_list(context, &value) { return Ok(true); } } - if let Some(value) = headers.get_first_value(&HeaderDef::References.get_headername())? { + if let Some(value) = headers.get_headerdef(HeaderDef::References)? { if is_msgrmsg_rfc724_mid_in_list(context, &value) { return Ok(true); } @@ -1342,16 +1333,15 @@ fn prefetch_should_download( headers: &[mailparse::MailHeader], show_emails: ShowEmails, ) -> Result { - let is_chat_message = prefetch_has_header(&headers, HeaderDef::ChatVersion)?; + let is_chat_message = headers.get_headerdef(HeaderDef::ChatVersion)?.is_some(); let is_reply_to_chat_message = prefetch_is_reply_to_chat_message(context, &headers)?; // Autocrypt Setup Message should be shown even if it is from non-chat client. - let is_autocrypt_setup_message = - prefetch_has_header(&headers, HeaderDef::AutocryptSetupMessage)?; + let is_autocrypt_setup_message = headers + .get_headerdef(HeaderDef::AutocryptSetupMessage)? + .is_some(); - let from_field = headers - .get_first_value(&HeaderDef::From_.get_headername())? - .unwrap_or_default(); + let from_field = headers.get_headerdef(HeaderDef::From_)?.unwrap_or_default(); let (_contact_id, blocked_contact, origin) = from_field_to_contact_id(context, &from_field)?; let accepted_contact = origin.is_known(); diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 5e733a667..b4448a47a 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -15,7 +15,7 @@ use crate::dehtml::dehtml; use crate::e2ee; use crate::error::Result; use crate::events::Event; -use crate::headerdef::HeaderDef; +use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::job::{job_add, Action}; use crate::location; use crate::message; @@ -96,7 +96,7 @@ impl MimeMessage { let message_time = mail .headers - .get_first_value("Date")? + .get_headerdef(HeaderDef::Date)? .and_then(|v| mailparse::dateparse(&v).ok()) .unwrap_or_default(); @@ -781,16 +781,19 @@ impl MimeMessage { let (report_fields, _) = mailparse::parse_headers(&report_body)?; // must be present - let disp = HeaderDef::Disposition.get_headername(); - if let Some(_disposition) = report_fields.get_first_value(&disp).ok().flatten() { + if let Some(_disposition) = report_fields + .get_headerdef(HeaderDef::Disposition) + .ok() + .flatten() + { if let Some(original_message_id) = report_fields - .get_first_value(&HeaderDef::OriginalMessageId.get_headername()) + .get_headerdef(HeaderDef::OriginalMessageId) .ok() .flatten() .and_then(|v| parse_message_id(&v)) { let additional_message_ids = report_fields - .get_first_value(&HeaderDef::AdditionalMessageIds.get_headername()) + .get_headerdef(HeaderDef::AdditionalMessageIds) .ok() .flatten() .map_or_else(Vec::new, |v| { @@ -806,7 +809,7 @@ impl MimeMessage { warn!( context, "ignoring unknown disposition-notification, Message-Id: {:?}", - report_fields.get_first_value("Message-ID").ok() + report_fields.get_headerdef(HeaderDef::MessageId).ok() ); Ok(None)