mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
fix: Put Message-ID into hidden headers and take it from there on receiver (#4798)
Put a copy of Message-ID into hidden headers and prefer it over the one in the IMF header section that servers mess up with. This also reverts "Set X-Microsoft-Original-Message-ID on outgoing emails for amazonaws (#3077)".
This commit is contained in:
@@ -5904,7 +5904,7 @@ mod tests {
|
||||
// Alice has an SMTP-server replacing the `Message-ID:`-header (as done eg. by outlook.com).
|
||||
let sent_msg = alice.pop_sent_msg().await;
|
||||
let msg = sent_msg.payload();
|
||||
assert_eq!(msg.match_indices("Message-ID: <Gr.").count(), 1);
|
||||
assert_eq!(msg.match_indices("Message-ID: <Gr.").count(), 2);
|
||||
assert_eq!(msg.match_indices("References: <Gr.").count(), 1);
|
||||
let msg = msg.replace("Message-ID: <Gr.", "Message-ID: <XXX");
|
||||
assert_eq!(msg.match_indices("Message-ID: <Gr.").count(), 0);
|
||||
|
||||
@@ -124,7 +124,8 @@ struct MessageHeaders {
|
||||
/// Headers that MUST NOT go into IMF header section.
|
||||
///
|
||||
/// These are large headers which may hit the header section size limit on the server, such as
|
||||
/// Chat-User-Avatar with a base64-encoded image inside.
|
||||
/// Chat-User-Avatar with a base64-encoded image inside. Also there are headers duplicated here
|
||||
/// that servers mess up with in the IMF header section, like Message-ID.
|
||||
pub hidden: Vec<Header>,
|
||||
}
|
||||
|
||||
@@ -560,24 +561,9 @@ impl<'a> MimeFactory<'a> {
|
||||
Loaded::Mdn { .. } => create_outgoing_rfc724_mid(None, &self.from_addr),
|
||||
};
|
||||
let rfc724_mid_headervalue = render_rfc724_mid(&rfc724_mid);
|
||||
|
||||
// Amazon's SMTP servers change the `Message-ID`, just as Outlook's SMTP servers do.
|
||||
// Outlook's servers add an `X-Microsoft-Original-Message-ID` header with the original `Message-ID`,
|
||||
// and when downloading messages we look for this header in order to correctly identify
|
||||
// messages.
|
||||
// Amazon's servers do not add such a header, so we just add it ourselves.
|
||||
if let Some(server) = context.get_config(Config::ConfiguredSendServer).await? {
|
||||
if server.ends_with(".amazonaws.com") {
|
||||
headers.unprotected.push(Header::new(
|
||||
"X-Microsoft-Original-Message-ID".into(),
|
||||
rfc724_mid_headervalue.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
headers
|
||||
.unprotected
|
||||
.push(Header::new("Message-ID".into(), rfc724_mid_headervalue));
|
||||
let rfc724_mid_header = Header::new("Message-ID".into(), rfc724_mid_headervalue);
|
||||
headers.unprotected.push(rfc724_mid_header.clone());
|
||||
headers.hidden.push(rfc724_mid_header);
|
||||
|
||||
// Reply headers as in <https://datatracker.ietf.org/doc/html/rfc5322#appendix-A.2>.
|
||||
if !self.in_reply_to.is_empty() {
|
||||
@@ -783,19 +769,14 @@ impl<'a> MimeFactory<'a> {
|
||||
)
|
||||
.header(("Subject".to_string(), "...".to_string()))
|
||||
} else {
|
||||
let message = if headers.hidden.is_empty() {
|
||||
message
|
||||
} else {
|
||||
// Store hidden headers in the inner unencrypted message.
|
||||
let message = headers
|
||||
.hidden
|
||||
.into_iter()
|
||||
.fold(message, |message, header| message.header(header));
|
||||
|
||||
PartBuilder::new()
|
||||
.message_type(MimeMultipartType::Mixed)
|
||||
.child(message.build())
|
||||
};
|
||||
// Store hidden headers in the inner unencrypted message.
|
||||
let message = headers
|
||||
.hidden
|
||||
.into_iter()
|
||||
.fold(message, |message, header| message.header(header));
|
||||
let message = PartBuilder::new()
|
||||
.message_type(MimeMultipartType::Mixed)
|
||||
.child(message.build());
|
||||
|
||||
// Store protected headers in the outer message.
|
||||
let message = headers
|
||||
@@ -803,9 +784,7 @@ impl<'a> MimeFactory<'a> {
|
||||
.iter()
|
||||
.fold(message, |message, header| message.header(header.clone()));
|
||||
|
||||
if self.should_skip_autocrypt()
|
||||
|| !context.get_config_bool(Config::SignUnencrypted).await?
|
||||
{
|
||||
if skip_autocrypt || !context.get_config_bool(Config::SignUnencrypted).await? {
|
||||
let protected: HashSet<Header> = HashSet::from_iter(headers.protected.into_iter());
|
||||
for h in headers.unprotected.split_off(0) {
|
||||
if !protected.contains(&h) {
|
||||
@@ -2165,33 +2144,37 @@ mod tests {
|
||||
let body = payload.next().unwrap();
|
||||
|
||||
assert_eq!(outer.match_indices("multipart/mixed").count(), 1);
|
||||
assert_eq!(outer.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(outer.match_indices("Subject:").count(), 1);
|
||||
assert_eq!(outer.match_indices("Autocrypt:").count(), 1);
|
||||
assert_eq!(outer.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
|
||||
assert_eq!(inner.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(inner.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(inner.match_indices("Chat-User-Avatar:").count(), 1);
|
||||
assert_eq!(inner.match_indices("Subject:").count(), 0);
|
||||
|
||||
assert_eq!(body.match_indices("this is the text!").count(), 1);
|
||||
|
||||
// if another message is sent, that one must not contain the avatar
|
||||
// and no artificial multipart/mixed nesting
|
||||
let sent_msg = t.send_msg(chat.id, &mut msg).await;
|
||||
let mut payload = sent_msg.payload().splitn(2, "\r\n\r\n");
|
||||
let mut payload = sent_msg.payload().splitn(3, "\r\n\r\n");
|
||||
let outer = payload.next().unwrap();
|
||||
let inner = payload.next().unwrap();
|
||||
let body = payload.next().unwrap();
|
||||
|
||||
assert_eq!(outer.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(outer.match_indices("multipart/mixed").count(), 1);
|
||||
assert_eq!(outer.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(outer.match_indices("Subject:").count(), 1);
|
||||
assert_eq!(outer.match_indices("Autocrypt:").count(), 1);
|
||||
assert_eq!(outer.match_indices("multipart/mixed").count(), 0);
|
||||
assert_eq!(outer.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
|
||||
assert_eq!(inner.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(inner.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(inner.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
assert_eq!(inner.match_indices("Subject:").count(), 0);
|
||||
|
||||
assert_eq!(body.match_indices("this is the text!").count(), 1);
|
||||
assert_eq!(body.match_indices("text/plain").count(), 0);
|
||||
assert_eq!(body.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
assert_eq!(body.match_indices("Subject:").count(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2223,6 +2206,7 @@ mod tests {
|
||||
let part = payload.next().unwrap();
|
||||
assert_eq!(part.match_indices("multipart/signed").count(), 1);
|
||||
assert_eq!(part.match_indices("From:").count(), 1);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 0);
|
||||
assert_eq!(part.match_indices("Autocrypt:").count(), 1);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
@@ -2234,6 +2218,7 @@ mod tests {
|
||||
1
|
||||
);
|
||||
assert_eq!(part.match_indices("From:").count(), 1);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 0);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 1);
|
||||
assert_eq!(part.match_indices("Autocrypt:").count(), 0);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
@@ -2241,6 +2226,7 @@ mod tests {
|
||||
let part = payload.next().unwrap();
|
||||
assert_eq!(part.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(part.match_indices("From:").count(), 0);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 1);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 0);
|
||||
|
||||
@@ -2261,31 +2247,38 @@ mod tests {
|
||||
.is_some());
|
||||
|
||||
// if another message is sent, that one must not contain the avatar
|
||||
// and no artificial multipart/mixed nesting
|
||||
let sent_msg = t.send_msg(chat.id, &mut msg).await;
|
||||
let mut payload = sent_msg.payload().splitn(3, "\r\n\r\n");
|
||||
let mut payload = sent_msg.payload().splitn(4, "\r\n\r\n");
|
||||
|
||||
let part = payload.next().unwrap();
|
||||
assert_eq!(part.match_indices("multipart/signed").count(), 1);
|
||||
assert_eq!(part.match_indices("From:").count(), 1);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 0);
|
||||
assert_eq!(part.match_indices("Autocrypt:").count(), 1);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
|
||||
let part = payload.next().unwrap();
|
||||
assert_eq!(part.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(
|
||||
part.match_indices("multipart/mixed; protected-headers=\"v1\"")
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert_eq!(part.match_indices("From:").count(), 1);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 0);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 1);
|
||||
assert_eq!(part.match_indices("Autocrypt:").count(), 0);
|
||||
assert_eq!(part.match_indices("multipart/mixed").count(), 0);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
|
||||
let part = payload.next().unwrap();
|
||||
assert_eq!(part.match_indices("text/plain").count(), 1);
|
||||
assert_eq!(body.match_indices("From:").count(), 0);
|
||||
assert_eq!(part.match_indices("Message-ID:").count(), 1);
|
||||
assert_eq!(part.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
assert_eq!(part.match_indices("Subject:").count(), 0);
|
||||
|
||||
let body = payload.next().unwrap();
|
||||
assert_eq!(body.match_indices("this is the text!").count(), 1);
|
||||
assert_eq!(body.match_indices("text/plain").count(), 0);
|
||||
assert_eq!(body.match_indices("From:").count(), 0);
|
||||
assert_eq!(body.match_indices("Chat-User-Avatar:").count(), 0);
|
||||
assert_eq!(body.match_indices("Subject:").count(), 0);
|
||||
|
||||
bob.recv_msg(&sent_msg).await;
|
||||
let alice_contact = Contact::get_by_id(&bob.ctx, alice_id).await.unwrap();
|
||||
|
||||
@@ -184,28 +184,49 @@ pub(crate) async fn receive_imf_inner(
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, "Receiving message {rfc724_mid:?}, seen={seen}...");
|
||||
let rfc724_mid_orig = &mime_parser
|
||||
.get_rfc724_mid()
|
||||
.unwrap_or(rfc724_mid.to_string());
|
||||
info!(
|
||||
context,
|
||||
"Receiving message {rfc724_mid_orig:?}, seen={seen}...",
|
||||
);
|
||||
|
||||
// check, if the mail is already in our database.
|
||||
// make sure, this check is done eg. before securejoin-processing.
|
||||
let (replace_msg_id, replace_chat_id) =
|
||||
if let Some(old_msg_id) = message::rfc724_mid_exists(context, rfc724_mid).await? {
|
||||
let msg = Message::load_from_db(context, old_msg_id).await?;
|
||||
if msg.download_state() != DownloadState::Done && is_partial_download.is_none() {
|
||||
// the message was partially downloaded before and is fully downloaded now.
|
||||
info!(
|
||||
context,
|
||||
"Message already partly in DB, replacing by full message."
|
||||
);
|
||||
(Some(old_msg_id), Some(msg.chat_id))
|
||||
} else {
|
||||
// the message was probably moved around.
|
||||
info!(context, "Message already in DB, doing nothing.");
|
||||
return Ok(None);
|
||||
}
|
||||
let (replace_msg_id, replace_chat_id);
|
||||
if let Some(old_msg_id) = message::rfc724_mid_exists(context, rfc724_mid).await? {
|
||||
if is_partial_download.is_some() {
|
||||
info!(
|
||||
context,
|
||||
"Got a partial download and message is already in DB."
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
let msg = Message::load_from_db(context, old_msg_id).await?;
|
||||
replace_msg_id = Some(old_msg_id);
|
||||
replace_chat_id = if msg.download_state() != DownloadState::Done {
|
||||
// the message was partially downloaded before and is fully downloaded now.
|
||||
info!(
|
||||
context,
|
||||
"Message already partly in DB, replacing by full message."
|
||||
);
|
||||
Some(msg.chat_id)
|
||||
} else {
|
||||
(None, None)
|
||||
None
|
||||
};
|
||||
} else {
|
||||
replace_msg_id = if rfc724_mid_orig != rfc724_mid {
|
||||
message::rfc724_mid_exists(context, rfc724_mid_orig).await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
replace_chat_id = None;
|
||||
}
|
||||
if replace_msg_id.is_some() && replace_chat_id.is_none() {
|
||||
info!(context, "Message is already downloaded.");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let prevent_rename =
|
||||
mime_parser.is_mailinglist_message() || mime_parser.get_header(HeaderDef::Sender).is_some();
|
||||
@@ -301,7 +322,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
imf_raw,
|
||||
incoming,
|
||||
&to_ids,
|
||||
rfc724_mid,
|
||||
rfc724_mid_orig,
|
||||
from_id,
|
||||
seen || replace_msg_id.is_some(),
|
||||
is_partial_download,
|
||||
@@ -421,20 +442,30 @@ pub(crate) async fn receive_imf_inner(
|
||||
let delete_server_after = context.get_config_delete_server_after().await?;
|
||||
|
||||
if !received_msg.msg_ids.is_empty() {
|
||||
if received_msg.needs_delete_job
|
||||
let target = if received_msg.needs_delete_job
|
||||
|| (delete_server_after == Some(0) && is_partial_download.is_none())
|
||||
{
|
||||
let target = context.get_delete_msgs_target().await?;
|
||||
Some(context.get_delete_msgs_target().await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if target.is_some() || rfc724_mid_orig != rfc724_mid {
|
||||
let target_subst = match &target {
|
||||
Some(target) => format!("target='{target}',"),
|
||||
None => "".to_string(),
|
||||
};
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE imap SET target=? WHERE rfc724_mid=?",
|
||||
(target, rfc724_mid),
|
||||
&format!("UPDATE imap SET {target_subst} rfc724_mid=?1 WHERE rfc724_mid=?2"),
|
||||
(rfc724_mid_orig, rfc724_mid),
|
||||
)
|
||||
.await?;
|
||||
} else if !mime_parser.mdn_reports.is_empty() && mime_parser.has_chat_version() {
|
||||
}
|
||||
if target.is_none() && !mime_parser.mdn_reports.is_empty() && mime_parser.has_chat_version()
|
||||
{
|
||||
// This is a Delta Chat MDN. Mark as read.
|
||||
markseen_on_imap_table(context, rfc724_mid).await?;
|
||||
markseen_on_imap_table(context, rfc724_mid_orig).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +559,10 @@ async fn add_parts(
|
||||
prevent_rename: bool,
|
||||
verified_encryption: VerifiedEncryption,
|
||||
) -> Result<ReceivedMsg> {
|
||||
let rfc724_mid_orig = &mime_parser
|
||||
.get_rfc724_mid()
|
||||
.unwrap_or(rfc724_mid.to_string());
|
||||
|
||||
let mut chat_id = None;
|
||||
let mut chat_id_blocked = Blocked::Not;
|
||||
|
||||
@@ -1308,7 +1343,7 @@ RETURNING id
|
||||
"#)?;
|
||||
let row_id: MsgId = stmt.query_row(params![
|
||||
replace_msg_id,
|
||||
rfc724_mid,
|
||||
rfc724_mid_orig,
|
||||
if trash { DC_CHAT_ID_TRASH } else { chat_id },
|
||||
if trash { ContactId::UNDEFINED } else { from_id },
|
||||
if trash { ContactId::UNDEFINED } else { to_id },
|
||||
|
||||
@@ -3223,6 +3223,22 @@ async fn test_thunderbird_unsigned_with_unencrypted_subject() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that DC takes the correct Message-ID from the encrypted message part, not the unencrypted
|
||||
/// one messed up by the server.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_messed_up_message_id() -> Result<()> {
|
||||
let t = TestContext::new_bob().await;
|
||||
|
||||
let raw = include_bytes!("../../test-data/message/messed_up_message_id.eml");
|
||||
receive_imf(&t, raw, false).await?;
|
||||
assert_eq!(
|
||||
t.get_last_msg().await.rfc724_mid,
|
||||
"0bb9ffe1-2596-d997-95b4-1fef8cc4808e@example.org"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mua_user_adds_member() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
93
test-data/message/messed_up_message_id.eml
Normal file
93
test-data/message/messed_up_message_id.eml
Normal file
@@ -0,0 +1,93 @@
|
||||
From - Thu, 24 Nov 2022 19:06:16 GMT
|
||||
X-Mozilla-Status: 0001
|
||||
X-Mozilla-Status2: 00800000
|
||||
Message-ID: <0bb9ffe1-2596-d997-95b4-1fef8cc4808f@example.org>
|
||||
Date: Thu, 24 Nov 2022 20:05:57 +0100
|
||||
MIME-Version: 1.0
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||
Thunderbird/102.4.2
|
||||
From: Alice <alice@example.org>
|
||||
To: bob@example.net
|
||||
Content-Language: en-US
|
||||
Autocrypt: addr=alice@example.org; keydata=
|
||||
xjMEXlh13RYJKwYBBAHaRw8BAQdAzfVIAleCXMJrq8VeLlEVof6ITCviMktKjmcBKAu4m5DN
|
||||
GUFsaWNlIDxhbGljZUBleGFtcGxlLm9yZz7CkAQTFggAOBYhBC5vossjtTLXKGNLWGSwj2Gp
|
||||
7ZRDBQJeWHXdAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEGSwj2Gp7ZRDE3oA/i4M
|
||||
CyDMTsjWqDZoQwX/A/GoTO2/V0wKPhjJJy/8m2pMAPkBjOnGOtx2SZpQvJGTa9h804RY6iDr
|
||||
RuI8A/8tEEXAA844BF5Ydd0SCisGAQQBl1UBBQEBB0AG7cjWy2SFAU8KnltlubVW67rFiyfp
|
||||
01JrRe6Xqy22HQMBCAfCeAQYFggAIBYhBC5vossjtTLXKGNLWGSwj2Gp7ZRDBQJeWHXdAhsM
|
||||
AAoJEGSwj2Gp7ZRDLo8BAObE8GnsGVwKzNqCvHeWgJsqhjS3C6gvSlV3tEm9XmF6AQDXucIy
|
||||
VfoBwoyMh2h6cSn/ATn5QJb35pgo+ivp3jsMAg==
|
||||
Subject: ...
|
||||
Content-Type: multipart/encrypted;
|
||||
protocol="application/pgp-encrypted";
|
||||
boundary="------------EOdOT2kJUL5hgCilmIhYyVZg"
|
||||
|
||||
This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
|
||||
--------------EOdOT2kJUL5hgCilmIhYyVZg
|
||||
Content-Type: application/pgp-encrypted
|
||||
Content-Description: PGP/MIME version identification
|
||||
|
||||
Version: 1
|
||||
|
||||
--------------EOdOT2kJUL5hgCilmIhYyVZg
|
||||
Content-Type: application/octet-stream; name="encrypted.asc"
|
||||
Content-Description: OpenPGP encrypted message
|
||||
Content-Disposition: inline; filename="encrypted.asc"
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
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
|
||||
oO4vW9/JEmFUqLyQmWnoLJHX3IKtWa9CPvE=
|
||||
=OA6b
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
--------------EOdOT2kJUL5hgCilmIhYyVZg--
|
||||
Reference in New Issue
Block a user