Add get_parent_message()

This commit is contained in:
Alexander Krotov
2020-12-05 06:53:08 +03:00
committed by link2xt
parent fc1f44c6d6
commit 30e7f84770
3 changed files with 61 additions and 146 deletions

View File

@@ -13,9 +13,9 @@ use crate::dc_tools::*;
use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
use crate::error::{bail, ensure, format_err, Result};
use crate::events::EventType;
use crate::headerdef::HeaderDef;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::job::{self, Action};
use crate::message::{self, MessageState, MessengerMessage, MsgId};
use crate::message::{self, rfc724_mid_exists, Message, MessageState, MessengerMessage, MsgId};
use crate::mimeparser::*;
use crate::param::*;
use crate::peerstate::*;
@@ -370,10 +370,15 @@ async fn add_parts(
return Ok(());
}
let parent = get_parent_message(context, mime_parser).await?;
let mut is_dc_message = if mime_parser.has_chat_version() {
MessengerMessage::Yes
} else if is_reply_to_messenger_message(context, mime_parser).await {
MessengerMessage::Reply
} else if let Some(parent) = &parent {
match parent.is_dc_message {
MessengerMessage::No => MessengerMessage::No,
MessengerMessage::Yes | MessengerMessage::Reply => MessengerMessage::Reply,
}
} else {
MessengerMessage::No
};
@@ -514,7 +519,7 @@ async fn add_parts(
if Blocked::Not == create_blocked {
chat_id.unblock(context).await;
chat_id_blocked = Blocked::Not;
} else if is_reply_to_known_message(context, mime_parser).await {
} else if get_parent_message(context, mime_parser).await?.is_some() {
// we do not want any chat to be created implicitly. Because of the origin-scale-up,
// the contact requests will pop up and this should be just fine.
Contact::scaleup_origin_by_id(context, from_id, Origin::IncomingReplyTo).await;
@@ -1811,102 +1816,63 @@ fn set_better_msg(mime_parser: &mut MimeMessage, better_msg: impl AsRef<str>) {
}
}
async fn is_reply_to_known_message(context: &Context, mime_parser: &MimeMessage) -> bool {
/* 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) */
/// Given a list of Message-IDs, returns the latest message found in the database.
async fn get_rfc724_mid_in_list(context: &Context, mid_list: &str) -> Result<Option<Message>> {
if mid_list.is_empty() {
return Ok(None);
}
if let Ok(ids) = parse_message_ids(mid_list) {
for id in ids.iter().rev() {
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, id).await? {
return Ok(Some(Message::load_from_db(context, msg_id).await?));
}
}
}
Ok(None)
}
/// Returns the last message referenced from References: header found in the database.
///
/// If none found, tries In-Reply-To: as a fallback for classic MUAs that don't set the
/// References: header.
async fn get_parent_message(
context: &Context,
mime_parser: &MimeMessage,
) -> Result<Option<Message>> {
if let Some(field) = mime_parser.get(HeaderDef::References) {
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
return Ok(Some(msg));
}
}
if let Some(field) = mime_parser.get(HeaderDef::InReplyTo) {
if is_known_rfc724_mid_in_list(context, &field).await {
return true;
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
return Ok(Some(msg));
}
}
if let Some(field) = mime_parser.get(HeaderDef::References) {
if is_known_rfc724_mid_in_list(context, &field).await {
return true;
}
}
false
Ok(None)
}
async fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool {
if mid_list.is_empty() {
return false;
}
if let Ok(ids) = parse_message_ids(mid_list) {
for id in ids.iter() {
if is_known_rfc724_mid(context, id).await {
return true;
}
pub(crate) async fn get_prefetch_parent_message(
context: &Context,
headers: &[mailparse::MailHeader<'_>],
) -> Result<Option<Message>> {
if let Some(field) = headers.get_header_value(HeaderDef::References) {
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
return Ok(Some(msg));
}
}
false
}
/// Check if a message is a reply to a known message (messenger or non-messenger).
async fn is_known_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool {
let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>');
context
.sql
.exists(
"SELECT m.id FROM msgs m \
LEFT JOIN chats c ON m.chat_id=c.id \
WHERE m.rfc724_mid=? \
AND m.chat_id>9 AND c.blocked=0;",
paramsv![rfc724_mid],
)
.await
.unwrap_or_default()
}
/// Checks if the message defined by mime_parser references a message send by us from Delta Chat.
/// This is similar to is_reply_to_known_message() but
/// - 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
/// - no check for the Chat-* headers (function is only called if it is no messenger message itself)
async fn is_reply_to_messenger_message(context: &Context, mime_parser: &MimeMessage) -> bool {
if let Some(value) = mime_parser.get(HeaderDef::InReplyTo) {
if is_msgrmsg_rfc724_mid_in_list(context, &value).await {
return true;
if let Some(field) = headers.get_header_value(HeaderDef::InReplyTo) {
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
return Ok(Some(msg));
}
}
if let Some(value) = mime_parser.get(HeaderDef::References) {
if is_msgrmsg_rfc724_mid_in_list(context, &value).await {
return true;
}
}
false
}
pub(crate) async fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool {
if let Ok(ids) = parse_message_ids(mid_list) {
for id in ids.iter() {
if is_msgrmsg_rfc724_mid(context, id).await {
return true;
}
}
}
false
}
/// Check if a message is a reply to any messenger message.
async fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &str) -> bool {
let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>');
context
.sql
.exists(
"SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;",
paramsv![rfc724_mid],
)
.await
.unwrap_or_default()
Ok(None)
}
async fn dc_add_or_lookup_contacts_by_address_list(
@@ -2032,38 +1998,6 @@ mod tests {
);
}
#[async_std::test]
async fn test_is_known_rfc724_mid() {
let t = TestContext::new().await;
let mut msg = Message::new(Viewtype::Text);
msg.text = Some("first message".to_string());
let msg_id = chat::add_device_msg(&t.ctx, None, Some(&mut msg))
.await
.unwrap();
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
// Message-IDs may or may not be surrounded by angle brackets
assert!(is_known_rfc724_mid(&t.ctx, format!("<{}>", msg.rfc724_mid).as_str()).await);
assert!(is_known_rfc724_mid(&t.ctx, &msg.rfc724_mid).await);
assert!(!is_known_rfc724_mid(&t.ctx, "nonexistant@message.id").await);
}
#[async_std::test]
async fn test_is_msgrmsg_rfc724_mid() {
let t = TestContext::new().await;
let mut msg = Message::new(Viewtype::Text);
msg.text = Some("first message".to_string());
let msg_id = chat::add_device_msg(&t.ctx, None, Some(&mut msg))
.await
.unwrap();
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
// Message-IDs may or may not be surrounded by angle brackets
assert!(is_msgrmsg_rfc724_mid(&t.ctx, format!("<{}>", msg.rfc724_mid).as_str()).await);
assert!(is_msgrmsg_rfc724_mid(&t.ctx, &msg.rfc724_mid).await);
assert!(!is_msgrmsg_rfc724_mid(&t.ctx, "nonexistant@message.id").await);
}
static MSGRMSG: &[u8] = b"From: Bob <bob@example.com>\n\
To: alice@example.com\n\
Chat-Version: 1.0\n\

View File

@@ -16,7 +16,7 @@ use num_traits::FromPrimitive;
use crate::constants::*;
use crate::context::Context;
use crate::dc_receive_imf::{from_field_to_contact_id, is_msgrmsg_rfc724_mid_in_list};
use crate::dc_receive_imf::{from_field_to_contact_id, get_prefetch_parent_message};
use crate::error::{bail, format_err, Result};
use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap};
@@ -1556,25 +1556,6 @@ fn prefetch_get_message_id(headers: &[mailparse::MailHeader]) -> Result<String>
}
}
async fn prefetch_is_reply_to_chat_message(
context: &Context,
headers: &[mailparse::MailHeader<'_>],
) -> bool {
if let Some(value) = headers.get_header_value(HeaderDef::InReplyTo) {
if is_msgrmsg_rfc724_mid_in_list(context, &value).await {
return true;
}
}
if let Some(value) = headers.get_header_value(HeaderDef::References) {
if is_msgrmsg_rfc724_mid_in_list(context, &value).await {
return true;
}
}
false
}
pub(crate) async fn prefetch_should_download(
context: &Context,
headers: &[mailparse::MailHeader<'_>],
@@ -1593,7 +1574,9 @@ pub(crate) async fn prefetch_should_download(
}
let is_chat_message = headers.get_header_value(HeaderDef::ChatVersion).is_some();
let is_reply_to_chat_message = prefetch_is_reply_to_chat_message(context, &headers).await;
let is_reply_to_chat_message = get_prefetch_parent_message(context, headers)
.await?
.is_some();
let maybe_ndn = if let Some(from) = headers.get_header_value(HeaderDef::From_) {
let from = from.to_ascii_lowercase();

View File

@@ -787,11 +787,8 @@ impl Message {
pub async fn quoted_message(&self, context: &Context) -> Result<Option<Message>, Error> {
if self.param.get(Param::Quote).is_some() {
if let Some(in_reply_to) = &self.in_reply_to {
let rfc724_mid = in_reply_to.trim_start_matches('<').trim_end_matches('>');
if !rfc724_mid.is_empty() {
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, rfc724_mid).await? {
return Ok(Some(Message::load_from_db(context, msg_id).await?));
}
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, in_reply_to).await? {
return Ok(Some(Message::load_from_db(context, msg_id).await?));
}
}
}
@@ -1818,6 +1815,7 @@ pub(crate) async fn rfc724_mid_exists(
context: &Context,
rfc724_mid: &str,
) -> Result<Option<(String, u32, MsgId)>, Error> {
let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>');
if rfc724_mid.is_empty() {
warn!(context, "Empty rfc724_mid passed to rfc724_mid_exists");
return Ok(None);