mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
remove lookup_field in favor of get(HeaderDef::...) with all headers defined in headerdef.rs
This commit is contained in:
committed by
Alexander Krotov
parent
2c4dbe6e68
commit
e4155e0e16
@@ -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
104
src/headerdef.rs
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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::*;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user