remove lookup_field in favor of get(HeaderDef::...) with all headers defined in headerdef.rs

This commit is contained in:
holger krekel
2019-12-08 12:34:11 +01:00
committed by Alexander Krotov
parent 2c4dbe6e68
commit e4155e0e16
5 changed files with 184 additions and 76 deletions

View File

@@ -12,6 +12,7 @@ use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::Result; use crate::error::Result;
use crate::events::Event; use crate::events::Event;
use crate::headerdef::HeaderDef;
use crate::job::*; use crate::job::*;
use crate::location; use crate::location;
use crate::message::{self, MessageState, MsgId}; use crate::message::{self, MessageState, MsgId};
@@ -60,9 +61,9 @@ pub fn dc_receive_imf(
mime_parser.unwrap() mime_parser.unwrap()
}; };
if mime_parser.header.is_empty() { if mime_parser.get(HeaderDef::From_).is_none() {
// Error - even adding an empty record won't help as we do not know the message ID // Error - even adding an empty record won't help as we do not know the sender
warn!(context, "No header."); warn!(context, "No From header.");
return; return;
} }
@@ -114,7 +115,7 @@ pub fn dc_receive_imf(
} }
}; };
if let Some(value) = mime_parser.lookup_field("Date") { if let Some(value) = mime_parser.get(HeaderDef::Date) {
// is not yet checked against bad times! we do this later if we have the database information. // is not yet checked against bad times! we do this later if we have the database information.
sent_timestamp = mailparse::dateparse(value).unwrap_or_default(); sent_timestamp = mailparse::dateparse(value).unwrap_or_default();
} }
@@ -122,7 +123,7 @@ pub fn dc_receive_imf(
// get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass) // get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass)
// or if From: is equal to SELF (in this case, it is any outgoing messages, // or if From: is equal to SELF (in this case, it is any outgoing messages,
// we do not check Return-Path any more as this is unreliable, see issue #150 // we do not check Return-Path any more as this is unreliable, see issue #150
if let Some(field_from) = mime_parser.lookup_field("From") { if let Some(field_from) = mime_parser.get(HeaderDef::From_) {
let mut check_self = false; let mut check_self = false;
let mut from_list = Vec::with_capacity(16); let mut from_list = Vec::with_capacity(16);
dc_add_or_lookup_contacts_by_address_list( dc_add_or_lookup_contacts_by_address_list(
@@ -147,7 +148,7 @@ pub fn dc_receive_imf(
} }
// Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass) // Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass)
if let Some(field) = mime_parser.lookup_field("To") { if let Some(field) = mime_parser.get(HeaderDef::To) {
dc_add_or_lookup_contacts_by_address_list( dc_add_or_lookup_contacts_by_address_list(
context, context,
&field, &field,
@@ -307,7 +308,7 @@ fn add_parts(
// collect the rest information, CC: is added to the to-list, BCC: is ignored // collect the rest information, CC: is added to the to-list, BCC: is ignored
// (we should not add BCC to groups as this would split groups. We could add them as "known contacts", // (we should not add BCC to groups as this would split groups. We could add them as "known contacts",
// however, the benefit is very small and this may leak data that is expected to be hidden) // however, the benefit is very small and this may leak data that is expected to be hidden)
if let Some(fld_cc) = mime_parser.lookup_field("Cc") { if let Some(fld_cc) = mime_parser.get(HeaderDef::Cc) {
dc_add_or_lookup_contacts_by_address_list( dc_add_or_lookup_contacts_by_address_list(
context, context,
fld_cc, fld_cc,
@@ -372,7 +373,7 @@ fn add_parts(
// handshake messages must be processed _before_ chats are created // handshake messages must be processed _before_ chats are created
// (eg. contacs may be marked as verified) // (eg. contacs may be marked as verified)
if mime_parser.lookup_field("Secure-Join").is_some() { if mime_parser.get(HeaderDef::SecureJoin).is_some() {
// avoid discarding by show_emails setting // avoid discarding by show_emails setting
msgrmsg = 1; msgrmsg = 1;
*chat_id = 0; *chat_id = 0;
@@ -581,11 +582,11 @@ fn add_parts(
// if the mime-headers should be saved, find out its size // if the mime-headers should be saved, find out its size
// (the mime-header ends with an empty line) // (the mime-header ends with an empty line)
let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders); let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders);
if let Some(raw) = mime_parser.lookup_field("In-Reply-To") { if let Some(raw) = mime_parser.get(HeaderDef::InReplyTo) {
mime_in_reply_to = raw.clone(); mime_in_reply_to = raw.clone();
} }
if let Some(raw) = mime_parser.lookup_field("References") { if let Some(raw) = mime_parser.get(HeaderDef::References) {
mime_references = raw.clone(); mime_references = raw.clone();
} }
@@ -797,7 +798,6 @@ fn create_or_lookup_group(
to_ids: &[u32], to_ids: &[u32],
) -> Result<(u32, Blocked)> { ) -> Result<(u32, Blocked)> {
let mut chat_id_blocked = Blocked::Not; let mut chat_id_blocked = Blocked::Not;
let mut grpname = None;
let to_ids_cnt = to_ids.len(); let to_ids_cnt = to_ids.len();
let mut recreate_member_list = false; let mut recreate_member_list = false;
let mut send_EVENT_CHAT_MODIFIED = false; let mut send_EVENT_CHAT_MODIFIED = false;
@@ -814,20 +814,21 @@ fn create_or_lookup_group(
set_better_msg(mime_parser, &better_msg); set_better_msg(mime_parser, &better_msg);
let mut grpid = "".to_string(); let mut grpid = "".to_string();
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-ID") { if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupId) {
grpid = optional_field.clone(); grpid = optional_field.clone();
} }
if grpid.is_empty() { if grpid.is_empty() {
if let Some(value) = mime_parser.lookup_field("Message-ID") { if let Some(value) = mime_parser.get(HeaderDef::MessageId) {
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(&value) { if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(&value) {
grpid = extracted_grpid.to_string(); grpid = extracted_grpid.to_string();
} }
} }
if grpid.is_empty() { if grpid.is_empty() {
if let Some(extracted_grpid) = get_grpid_from_list(mime_parser, "In-Reply-To") { if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::InReplyTo) {
grpid = extracted_grpid; grpid = extracted_grpid;
} else if let Some(extracted_grpid) = get_grpid_from_list(mime_parser, "References") { } else if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::References)
{
grpid = extracted_grpid; grpid = extracted_grpid;
} else { } else {
return create_or_lookup_adhoc_group( return create_or_lookup_adhoc_group(
@@ -846,13 +847,9 @@ fn create_or_lookup_group(
} }
} }
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Name").cloned() { let grpname = mime_parser.get(HeaderDef::ChatGroupName).cloned();
grpname = Some(optional_field);
} if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupMemberRemoved).cloned() {
let field = mime_parser
.lookup_field("Chat-Group-Member-Removed")
.cloned();
if let Some(optional_field) = field {
X_MrRemoveFromGrp = Some(optional_field); X_MrRemoveFromGrp = Some(optional_field);
mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup; mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup;
let left_group = Contact::lookup_id_by_addr(context, X_MrRemoveFromGrp.as_ref().unwrap()) let left_group = Contact::lookup_id_by_addr(context, X_MrRemoveFromGrp.as_ref().unwrap())
@@ -868,11 +865,11 @@ fn create_or_lookup_group(
from_id as u32, from_id as u32,
) )
} else { } else {
let field = mime_parser.lookup_field("Chat-Group-Member-Added").cloned(); let field = mime_parser.get(HeaderDef::ChatGroupMemberAdded).cloned();
if let Some(optional_field) = field { if let Some(optional_field) = field {
X_MrAddToGrp = Some(optional_field); X_MrAddToGrp = Some(optional_field);
mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; mime_parser.is_system_message = SystemMessage::MemberAddedToGroup;
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image").cloned() { if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupImage).cloned() {
X_MrGrpImageChanged = optional_field; X_MrGrpImageChanged = optional_field;
} }
better_msg = context.stock_system_msg( better_msg = context.stock_system_msg(
@@ -882,7 +879,7 @@ fn create_or_lookup_group(
from_id as u32, from_id as u32,
) )
} else { } else {
let field = mime_parser.lookup_field("Chat-Group-Name-Changed"); let field = mime_parser.get(HeaderDef::ChatGroupNameChanged);
if let Some(field) = field { if let Some(field) = field {
X_MrGrpNameChanged = true; X_MrGrpNameChanged = true;
better_msg = context.stock_system_msg( better_msg = context.stock_system_msg(
@@ -897,8 +894,7 @@ fn create_or_lookup_group(
); );
mime_parser.is_system_message = SystemMessage::GroupNameChanged; mime_parser.is_system_message = SystemMessage::GroupNameChanged;
} else if let Some(optional_field) = } else if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupImage).cloned()
mime_parser.lookup_field("Chat-Group-Image").cloned()
{ {
// fld_value is a pointer somewhere into mime_parser, must not be freed // fld_value is a pointer somewhere into mime_parser, must not be freed
X_MrGrpImageChanged = optional_field; X_MrGrpImageChanged = optional_field;
@@ -954,7 +950,7 @@ fn create_or_lookup_group(
|| X_MrAddToGrp.is_some() && addr_cmp(&self_addr, X_MrAddToGrp.as_ref().unwrap())) || X_MrAddToGrp.is_some() && addr_cmp(&self_addr, X_MrAddToGrp.as_ref().unwrap()))
{ {
// group does not exist but should be created // group does not exist but should be created
let create_verified = if mime_parser.lookup_field("Chat-Verified").is_some() { let create_verified = if mime_parser.get(HeaderDef::ChatVerified).is_some() {
if let Err(err) = if let Err(err) =
check_verified_properties(context, mime_parser, from_id as u32, to_ids) check_verified_properties(context, mime_parser, from_id as u32, to_ids)
{ {
@@ -1130,8 +1126,8 @@ fn create_or_lookup_group(
} }
/// try extract a grpid from a message-id list header value /// try extract a grpid from a message-id list header value
fn get_grpid_from_list(mime_parser: &MimeParser, header_key: &str) -> Option<String> { fn extract_grpid(mime_parser: &MimeParser, headerdef: HeaderDef) -> Option<String> {
if let Some(value) = mime_parser.lookup_field(header_key) { if let Some(value) = mime_parser.get(headerdef) {
for part in value.split(',').map(str::trim) { for part in value.split(',').map(str::trim) {
if !part.is_empty() { if !part.is_empty() {
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(part) { if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(part) {
@@ -1486,13 +1482,13 @@ fn is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) -> boo
/* check if the message is a reply to a known message; the replies are identified by the Message-ID from /* check if the message is a reply to a known message; the replies are identified by the Message-ID from
`In-Reply-To`/`References:` (to support non-Delta-Clients) */ `In-Reply-To`/`References:` (to support non-Delta-Clients) */
if let Some(field) = mime_parser.lookup_field("In-Reply-To") { if let Some(field) = mime_parser.get(HeaderDef::InReplyTo) {
if is_known_rfc724_mid_in_list(context, &field) { if is_known_rfc724_mid_in_list(context, &field) {
return true; return true;
} }
} }
if let Some(field) = mime_parser.lookup_field("References") { if let Some(field) = mime_parser.get(HeaderDef::References) {
if is_known_rfc724_mid_in_list(context, &field) { if is_known_rfc724_mid_in_list(context, &field) {
return true; return true;
} }
@@ -1537,14 +1533,14 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: &mailparse::MailAddr) -> b
/// - checks also if any of the referenced IDs are send by a messenger /// - checks also if any of the referenced IDs are send by a messenger
/// - it is okay, if the referenced messages are moved to trash here /// - it is okay, if the referenced messages are moved to trash here
/// - no check for the Chat-* headers (function is only called if it is no messenger message itself) /// - no check for the Chat-* headers (function is only called if it is no messenger message itself)
fn is_reply_to_messenger_message(context: &Context, mime_parser: &MimeParser) -> bool { fn dc_is_reply_to_messenger_message(context: &Context, mime_parser: &MimeParser) -> bool {
if let Some(value) = mime_parser.lookup_field("In-Reply-To") { if let Some(value) = mime_parser.get(HeaderDef::InReplyTo) {
if is_msgrmsg_rfc724_mid_in_list(context, &value) { if is_msgrmsg_rfc724_mid_in_list(context, &value) {
return true; return true;
} }
} }
if let Some(value) = mime_parser.lookup_field("References") { if let Some(value) = mime_parser.get(HeaderDef::References) {
if is_msgrmsg_rfc724_mid_in_list(context, &value) { if is_msgrmsg_rfc724_mid_in_list(context, &value) {
return true; return true;
} }
@@ -1683,9 +1679,9 @@ mod tests {
\n\ \n\
hello\x00"; hello\x00";
let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap(); let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap();
assert_eq!(get_grpid_from_list(&mimeparser, "In-Reply-To"), None); assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), None);
let grpid = Some("HcxyMARjyJy".to_string()); let grpid = Some("HcxyMARjyJy".to_string());
assert_eq!(get_grpid_from_list(&mimeparser, "References"), grpid); assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
} }
#[test] #[test]
@@ -1699,7 +1695,7 @@ mod tests {
hello\x00"; hello\x00";
let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap(); let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap();
let grpid = Some("HcxyMARjyJy".to_string()); let grpid = Some("HcxyMARjyJy".to_string());
assert_eq!(get_grpid_from_list(&mimeparser, "In-Reply-To"), grpid); assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), grpid);
assert_eq!(get_grpid_from_list(&mimeparser, "References"), grpid); assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
} }
} }

104
src/headerdef.rs Normal file
View File

@@ -0,0 +1,104 @@
use crate::strum::EnumProperty;
#[derive(Debug, Clone, PartialEq, Eq, EnumProperty)]
pub enum HeaderDef {
#[strum(props(header = "message-id"))]
MessageId,
#[strum(props(header = "subject"))]
Subject,
#[strum(props(header = "date"))]
Date,
#[strum(props(header = "from"))]
From_,
#[strum(props(header = "to"))]
To,
#[strum(props(header = "cc"))]
Cc,
#[strum(props(header = "disposition"))]
Disposition,
#[strum(props(header = "original-message-id"))]
OriginalMessageId,
#[strum(props(header = "list-id"))]
ListId,
#[strum(props(header = "references"))]
References,
#[strum(props(header = "in-reply-to"))]
InReplyTo,
#[strum(props(header = "precedence"))]
Precedence,
#[strum(props(header = "chat-version"))]
ChatVersion,
#[strum(props(header = "chat-group-id"))]
ChatGroupId,
#[strum(props(header = "chat-group-name"))]
ChatGroupName,
#[strum(props(header = "chat-group-name-changed"))]
ChatGroupNameChanged,
#[strum(props(header = "chat-verified"))]
ChatVerified,
#[strum(props(header = "chat-group-image"))]
ChatGroupImage,
#[strum(props(header = "chat-voice-message"))]
ChatVoiceMessage,
#[strum(props(header = "chat-group-member-removed"))]
ChatGroupMemberRemoved,
#[strum(props(header = "chat-group-member-added"))]
ChatGroupMemberAdded,
#[strum(props(header = "chat-content"))]
ChatContent,
#[strum(props(header = "chat-duration"))]
ChatDuration,
#[strum(props(header = "chat-disposition-notification-to"))]
ChatDispositionNotificationTo,
#[strum(props(header = "autocrypt-setup-message"))]
AutocryptSetupMessage,
#[strum(props(header = "secure-join"))]
SecureJoin,
#[strum(props(header = "secure-join-group"))]
SecureJoinGroup,
#[strum(props(header = "secure-join-fingerprint"))]
SecureJoinFingerprint,
#[strum(props(header = "secure-join-invitenumber"))]
SecureJoinInvitenumber,
#[strum(props(header = "secure-join-group"))]
SecureJoinAuth,
#[strum(props(header = "test-header"))]
_TestHeader,
}
impl HeaderDef {
/// Returns the corresponding Event id.
pub fn get_headername(&self) -> &str {
self.get_str("header").expect("missing header definition")
}
}

View File

@@ -33,6 +33,8 @@ mod log;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
pub mod headerdef;
pub(crate) mod events; pub(crate) mod events;
pub use events::*; pub use events::*;

View File

@@ -14,6 +14,7 @@ use crate::dc_simplify::*;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::e2ee; use crate::e2ee;
use crate::error::Result; use crate::error::Result;
use crate::headerdef::HeaderDef;
use crate::job::{job_add, Action}; use crate::job::{job_add, Action};
use crate::location; use crate::location;
use crate::message; use crate::message;
@@ -28,7 +29,7 @@ use crate::{bail, ensure};
pub struct MimeParser<'a> { pub struct MimeParser<'a> {
pub context: &'a Context, pub context: &'a Context,
pub parts: Vec<Part>, pub parts: Vec<Part>,
pub header: HashMap<String, String>, header: HashMap<String, String>,
pub decrypting_failed: bool, pub decrypting_failed: bool,
pub signatures: HashSet<String>, pub signatures: HashSet<String>,
pub gossipped_addr: HashSet<String>, pub gossipped_addr: HashSet<String>,
@@ -144,7 +145,7 @@ impl<'a> MimeParser<'a> {
} }
fn parse_headers(&mut self) -> Result<()> { fn parse_headers(&mut self) -> Result<()> {
if self.lookup_field("Autocrypt-Setup-Message").is_some() { if self.get(HeaderDef::AutocryptSetupMessage).is_some() {
let has_setup_file = self.parts.iter().any(|p| { let has_setup_file = self.parts.iter().any(|p| {
p.mimetype.is_some() && p.mimetype.as_ref().unwrap().as_ref() == MIME_AC_SETUP_FILE p.mimetype.is_some() && p.mimetype.as_ref().unwrap().as_ref() == MIME_AC_SETUP_FILE
}); });
@@ -175,12 +176,12 @@ impl<'a> MimeParser<'a> {
} }
} }
} }
} else if let Some(value) = self.lookup_field("Chat-Content") { } else if let Some(value) = self.get(HeaderDef::ChatContent) {
if value == "location-streaming-enabled" { if value == "location-streaming-enabled" {
self.is_system_message = SystemMessage::LocationStreamingEnabled; self.is_system_message = SystemMessage::LocationStreamingEnabled;
} }
} }
if self.lookup_field("Chat-Group-Image").is_some() && !self.parts.is_empty() { if self.get(HeaderDef::ChatGroupImage).is_some() && !self.parts.is_empty() {
let textpart = &self.parts[0]; let textpart = &self.parts[0];
if textpart.typ == Viewtype::Text && self.parts.len() >= 2 { if textpart.typ == Viewtype::Text && self.parts.len() >= 2 {
let imgpart = &mut self.parts[1]; let imgpart = &mut self.parts[1];
@@ -255,13 +256,13 @@ impl<'a> MimeParser<'a> {
} }
if self.parts.len() == 1 { if self.parts.len() == 1 {
if self.parts[0].typ == Viewtype::Audio if self.parts[0].typ == Viewtype::Audio
&& self.lookup_field("Chat-Voice-Message").is_some() && self.get(HeaderDef::ChatVoiceMessage).is_some()
{ {
let part_mut = &mut self.parts[0]; let part_mut = &mut self.parts[0];
part_mut.typ = Viewtype::Voice; part_mut.typ = Viewtype::Voice;
} }
if self.parts[0].typ == Viewtype::Image { if self.parts[0].typ == Viewtype::Image {
if let Some(value) = self.lookup_field("Chat-Content") { if let Some(value) = self.get(HeaderDef::ChatContent) {
if value == "sticker" { if value == "sticker" {
let part_mut = &mut self.parts[0]; let part_mut = &mut self.parts[0];
part_mut.typ = Viewtype::Sticker; part_mut.typ = Viewtype::Sticker;
@@ -273,7 +274,7 @@ impl<'a> MimeParser<'a> {
|| part.typ == Viewtype::Voice || part.typ == Viewtype::Voice
|| part.typ == Viewtype::Video || part.typ == Viewtype::Video
{ {
if let Some(field_0) = self.lookup_field("Chat-Duration") { if let Some(field_0) = self.get(HeaderDef::ChatDuration) {
let duration_ms = field_0.parse().unwrap_or_default(); let duration_ms = field_0.parse().unwrap_or_default();
if duration_ms > 0 && duration_ms < 24 * 60 * 60 * 1000 { if duration_ms > 0 && duration_ms < 24 * 60 * 60 * 1000 {
let part_mut = &mut self.parts[0]; let part_mut = &mut self.parts[0];
@@ -283,12 +284,12 @@ impl<'a> MimeParser<'a> {
} }
} }
if !self.decrypting_failed { if !self.decrypting_failed {
if let Some(dn_field) = self.lookup_field("Chat-Disposition-Notification-To") { if let Some(dn_field) = self.get(HeaderDef::ChatDispositionNotificationTo) {
if self.get_last_nonmeta().is_some() { if self.get_last_nonmeta().is_some() {
let addrs = mailparse::addrparse(&dn_field).unwrap(); let addrs = mailparse::addrparse(&dn_field).unwrap();
if let Some(dn_to_addr) = addrs.first() { if let Some(dn_to_addr) = addrs.first() {
if let Some(from_field) = self.lookup_field("From") { if let Some(from_field) = self.get(HeaderDef::From_) {
let from_addrs = mailparse::addrparse(&from_field).unwrap(); let from_addrs = mailparse::addrparse(&from_field).unwrap();
if let Some(from_addr) = from_addrs.first() { if let Some(from_addr) = from_addrs.first() {
@@ -338,7 +339,7 @@ impl<'a> MimeParser<'a> {
} }
pub(crate) fn get_subject(&self) -> Option<String> { pub(crate) fn get_subject(&self) -> Option<String> {
if let Some(s) = self.header.get("subject") { if let Some(s) = self.get(HeaderDef::Subject) {
if s.is_empty() { if s.is_empty() {
return None; return None;
} }
@@ -348,8 +349,8 @@ impl<'a> MimeParser<'a> {
} }
} }
pub fn lookup_field(&self, field_name: &str) -> Option<&String> { pub fn get(&self, headerdef: HeaderDef) -> Option<&String> {
self.header.get(&field_name.to_lowercase()) self.header.get(headerdef.get_headername())
} }
fn parse_mime_recursive(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> { fn parse_mime_recursive(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result<bool> {
@@ -649,11 +650,11 @@ impl<'a> MimeParser<'a> {
} }
pub fn is_mailinglist_message(&self) -> bool { pub fn is_mailinglist_message(&self) -> bool {
if self.lookup_field("List-Id").is_some() { if self.get(HeaderDef::ListId).is_some() {
return true; return true;
} }
if let Some(precedence) = self.lookup_field("Precedence") { if let Some(precedence) = self.get(HeaderDef::Precedence) {
precedence == "list" || precedence == "bulk" precedence == "list" || precedence == "bulk"
} else { } else {
false false
@@ -662,7 +663,7 @@ impl<'a> MimeParser<'a> {
pub fn sender_equals_recipient(&self) -> bool { pub fn sender_equals_recipient(&self) -> bool {
/* get From: and check there is exactly one sender */ /* get From: and check there is exactly one sender */
if let Some(field) = self.lookup_field("From") { if let Some(field) = self.get(HeaderDef::From_) {
if let Ok(addrs) = mailparse::addrparse(field) { if let Ok(addrs) = mailparse::addrparse(field) {
if addrs.len() != 1 { if addrs.len() != 1 {
return false; return false;
@@ -693,12 +694,12 @@ impl<'a> MimeParser<'a> {
} }
pub fn get_rfc724_mid(&self) -> Option<String> { pub fn get_rfc724_mid(&self) -> Option<String> {
// get Message-ID from header if let Some(msgid) = self.get(HeaderDef::MessageId) {
if let Some(field) = self.lookup_field("Message-ID") { parse_message_id(msgid)
return parse_message_id(field); } else {
}
None None
} }
}
fn hash_header(&mut self, fields: &[mailparse::MailHeader<'_>]) { fn hash_header(&mut self, fields: &[mailparse::MailHeader<'_>]) {
for field in fields { for field in fields {
@@ -727,9 +728,10 @@ impl<'a> MimeParser<'a> {
let (report_fields, _) = mailparse::parse_headers(&report_body)?; let (report_fields, _) = mailparse::parse_headers(&report_body)?;
// must be present // must be present
if let Some(_disposition) = report_fields.get_first_value("Disposition").ok().flatten() { let disp = HeaderDef::Disposition.get_headername();
if let Some(_disposition) = report_fields.get_first_value(disp).ok().flatten() {
if let Some(original_message_id) = report_fields if let Some(original_message_id) = report_fields
.get_first_value("Original-Message-ID") .get_first_value(HeaderDef::OriginalMessageId.get_headername())
.ok() .ok()
.flatten() .flatten()
.and_then(|v| parse_message_id(&v)) .and_then(|v| parse_message_id(&v))
@@ -1108,14 +1110,14 @@ mod tests {
let raw = b"From: hello\n\ let raw = b"From: hello\n\
Content-Type: multipart/mixed; boundary=\"==break==\";\n\ Content-Type: multipart/mixed; boundary=\"==break==\";\n\
Subject: outer-subject\n\ Subject: outer-subject\n\
X-Special-A: special-a\n\ Secure-Join-Group: no\n\
Foo: Bar\nChat-Version: 0.0\n\ Test-Header: Bar\nChat-Version: 0.0\n\
\n\ \n\
--==break==\n\ --==break==\n\
Content-Type: text/plain; protected-headers=\"v1\";\n\ Content-Type: text/plain; protected-headers=\"v1\";\n\
Subject: inner-subject\n\ Subject: inner-subject\n\
X-Special-B: special-b\n\ SecureBar-Join-Group: yes\n\
Foo: Xy\n\ Test-Header: Xy\n\
Chat-Version: 1.0\n\ Chat-Version: 1.0\n\
\n\ \n\
test1\n\ test1\n\
@@ -1125,15 +1127,18 @@ mod tests {
\x00"; \x00";
let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap(); let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap();
// test that we treat Subject as a protected header that can
// bubble upwards
assert_eq!(mimeparser.get_subject(), Some("inner-subject".into())); assert_eq!(mimeparser.get_subject(), Some("inner-subject".into()));
let of = mimeparser.lookup_field("X-Special-A").unwrap(); let of = mimeparser.get(HeaderDef::SecureJoinGroup).unwrap();
assert_eq!(of, "special-a"); assert_eq!(of, "no");
let of = mimeparser.lookup_field("Foo").unwrap(); // unprotected headers do not bubble upwards
let of = mimeparser.get(HeaderDef::_TestHeader).unwrap();
assert_eq!(of, "Bar"); assert_eq!(of, "Bar");
let of = mimeparser.lookup_field("Chat-Version").unwrap(); let of = mimeparser.get(HeaderDef::ChatVersion).unwrap();
assert_eq!(of, "1.0"); assert_eq!(of, "1.0");
assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts.len(), 1);
} }

View File

@@ -11,6 +11,7 @@ use crate::context::Context;
use crate::e2ee::*; use crate::e2ee::*;
use crate::error::Error; use crate::error::Error;
use crate::events::Event; use crate::events::Event;
use crate::headerdef::HeaderDef;
use crate::key::*; use crate::key::*;
use crate::lot::LotState; use crate::lot::LotState;
use crate::message::Message; use crate::message::Message;
@@ -350,7 +351,7 @@ pub(crate) fn handle_securejoin_handshake(
"handle_securejoin_handshake(): called with special contact id" "handle_securejoin_handshake(): called with special contact id"
); );
let step = mimeparser let step = mimeparser
.lookup_field("Secure-Join") .get(HeaderDef::SecureJoin)
.ok_or_else(|| format_err!("This message is not a Secure-Join message"))?; .ok_or_else(|| format_err!("This message is not a Secure-Join message"))?;
info!( info!(
@@ -378,7 +379,7 @@ pub(crate) fn handle_securejoin_handshake(
// it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it, // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here. // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code // verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
let invitenumber = match mimeparser.lookup_field("Secure-Join-Invitenumber") { let invitenumber = match mimeparser.get(HeaderDef::SecureJoinInvitenumber) {
Some(n) => n, Some(n) => n,
None => { None => {
warn!(context, "Secure-join denied (invitenumber missing).",); warn!(context, "Secure-join denied (invitenumber missing).",);
@@ -467,7 +468,7 @@ pub(crate) fn handle_securejoin_handshake(
==== Step 6 in "Out-of-band verified groups" protocol ==== ==== Step 6 in "Out-of-band verified groups" protocol ====
============================================================ */ ============================================================ */
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
let fingerprint = match mimeparser.lookup_field("Secure-Join-Fingerprint") { let fingerprint = match mimeparser.get(HeaderDef::SecureJoinFingerprint) {
Some(fp) => fp, Some(fp) => fp,
None => { None => {
could_not_establish_secure_connection( could_not_establish_secure_connection(
@@ -496,7 +497,7 @@ pub(crate) fn handle_securejoin_handshake(
} }
info!(context, "Fingerprint verified.",); info!(context, "Fingerprint verified.",);
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
let auth_0 = match mimeparser.lookup_field("Secure-Join-Auth") { let auth_0 = match mimeparser.get(HeaderDef::SecureJoinAuth) {
Some(auth) => auth, Some(auth) => auth,
None => { None => {
could_not_establish_secure_connection( could_not_establish_secure_connection(
@@ -526,7 +527,7 @@ pub(crate) fn handle_securejoin_handshake(
inviter_progress!(context, contact_id, 600); inviter_progress!(context, contact_id, 600);
if join_vg { if join_vg {
let field_grpid = mimeparser let field_grpid = mimeparser
.lookup_field("Secure-Join-Group") .get(HeaderDef::SecureJoinGroup)
.map(|s| s.as_str()) .map(|s| s.as_str())
.unwrap_or_else(|| ""); .unwrap_or_else(|| "");
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, field_grpid); let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, field_grpid);
@@ -600,7 +601,7 @@ pub(crate) fn handle_securejoin_handshake(
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined);
emit_event!(context, Event::ContactsChanged(None)); emit_event!(context, Event::ContactsChanged(None));
let cg_member_added = mimeparser let cg_member_added = mimeparser
.lookup_field("Chat-Group-Member-Added") .get(HeaderDef::ChatGroupMemberAdded)
.map(|s| s.as_str()) .map(|s| s.as_str())
.unwrap_or_else(|| ""); .unwrap_or_else(|| "");
if join_vg && !addr_equals_self(context, cg_member_added) { if join_vg && !addr_equals_self(context, cg_member_added) {
@@ -635,7 +636,7 @@ pub(crate) fn handle_securejoin_handshake(
inviter_progress!(context, contact_id, 800); inviter_progress!(context, contact_id, 800);
inviter_progress!(context, contact_id, 1000); inviter_progress!(context, contact_id, 1000);
let field_grpid = mimeparser let field_grpid = mimeparser
.lookup_field("Secure-Join-Group") .get(HeaderDef::SecureJoinGroup)
.map(|s| s.as_str()) .map(|s| s.as_str())
.unwrap_or_else(|| ""); .unwrap_or_else(|| "");
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid); let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);