mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
Add get_parent_message()
This commit is contained in:
committed by
link2xt
parent
fc1f44c6d6
commit
30e7f84770
@@ -13,9 +13,9 @@ use crate::dc_tools::*;
|
|||||||
use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
|
use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
|
||||||
use crate::error::{bail, ensure, format_err, Result};
|
use crate::error::{bail, ensure, format_err, Result};
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::headerdef::HeaderDef;
|
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||||
use crate::job::{self, Action};
|
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::mimeparser::*;
|
||||||
use crate::param::*;
|
use crate::param::*;
|
||||||
use crate::peerstate::*;
|
use crate::peerstate::*;
|
||||||
@@ -370,10 +370,15 @@ async fn add_parts(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parent = get_parent_message(context, mime_parser).await?;
|
||||||
|
|
||||||
let mut is_dc_message = if mime_parser.has_chat_version() {
|
let mut is_dc_message = if mime_parser.has_chat_version() {
|
||||||
MessengerMessage::Yes
|
MessengerMessage::Yes
|
||||||
} else if is_reply_to_messenger_message(context, mime_parser).await {
|
} else if let Some(parent) = &parent {
|
||||||
MessengerMessage::Reply
|
match parent.is_dc_message {
|
||||||
|
MessengerMessage::No => MessengerMessage::No,
|
||||||
|
MessengerMessage::Yes | MessengerMessage::Reply => MessengerMessage::Reply,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
MessengerMessage::No
|
MessengerMessage::No
|
||||||
};
|
};
|
||||||
@@ -514,7 +519,7 @@ async fn add_parts(
|
|||||||
if Blocked::Not == create_blocked {
|
if Blocked::Not == create_blocked {
|
||||||
chat_id.unblock(context).await;
|
chat_id.unblock(context).await;
|
||||||
chat_id_blocked = Blocked::Not;
|
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,
|
// 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.
|
// the contact requests will pop up and this should be just fine.
|
||||||
Contact::scaleup_origin_by_id(context, from_id, Origin::IncomingReplyTo).await;
|
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 {
|
/// Given a list of Message-IDs, returns the latest message found in the database.
|
||||||
/* check if the message is a reply to a known message; the replies are identified by the Message-ID from
|
async fn get_rfc724_mid_in_list(context: &Context, mid_list: &str) -> Result<Option<Message>> {
|
||||||
`In-Reply-To`/`References:` (to support non-Delta-Clients) */
|
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 let Some(field) = mime_parser.get(HeaderDef::InReplyTo) {
|
||||||
if is_known_rfc724_mid_in_list(context, &field).await {
|
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
|
||||||
return true;
|
return Ok(Some(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(field) = mime_parser.get(HeaderDef::References) {
|
Ok(None)
|
||||||
if is_known_rfc724_mid_in_list(context, &field).await {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_known_rfc724_mid_in_list(context: &Context, mid_list: &str) -> bool {
|
pub(crate) async fn get_prefetch_parent_message(
|
||||||
if mid_list.is_empty() {
|
context: &Context,
|
||||||
return false;
|
headers: &[mailparse::MailHeader<'_>],
|
||||||
}
|
) -> Result<Option<Message>> {
|
||||||
|
if let Some(field) = headers.get_header_value(HeaderDef::References) {
|
||||||
if let Ok(ids) = parse_message_ids(mid_list) {
|
if let Some(msg) = get_rfc724_mid_in_list(context, &field).await? {
|
||||||
for id in ids.iter() {
|
return Ok(Some(msg));
|
||||||
if is_known_rfc724_mid(context, id).await {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
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));
|
||||||
/// 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(value) = mime_parser.get(HeaderDef::References) {
|
Ok(None)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dc_add_or_lookup_contacts_by_address_list(
|
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\
|
static MSGRMSG: &[u8] = b"From: Bob <bob@example.com>\n\
|
||||||
To: alice@example.com\n\
|
To: alice@example.com\n\
|
||||||
Chat-Version: 1.0\n\
|
Chat-Version: 1.0\n\
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use num_traits::FromPrimitive;
|
|||||||
|
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::context::Context;
|
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::error::{bail, format_err, Result};
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
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(
|
pub(crate) async fn prefetch_should_download(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
headers: &[mailparse::MailHeader<'_>],
|
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_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 maybe_ndn = if let Some(from) = headers.get_header_value(HeaderDef::From_) {
|
||||||
let from = from.to_ascii_lowercase();
|
let from = from.to_ascii_lowercase();
|
||||||
|
|||||||
@@ -787,11 +787,8 @@ impl Message {
|
|||||||
pub async fn quoted_message(&self, context: &Context) -> Result<Option<Message>, Error> {
|
pub async fn quoted_message(&self, context: &Context) -> Result<Option<Message>, Error> {
|
||||||
if self.param.get(Param::Quote).is_some() {
|
if self.param.get(Param::Quote).is_some() {
|
||||||
if let Some(in_reply_to) = &self.in_reply_to {
|
if let Some(in_reply_to) = &self.in_reply_to {
|
||||||
let rfc724_mid = in_reply_to.trim_start_matches('<').trim_end_matches('>');
|
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, in_reply_to).await? {
|
||||||
if !rfc724_mid.is_empty() {
|
return Ok(Some(Message::load_from_db(context, msg_id).await?));
|
||||||
if let Some((_, _, msg_id)) = rfc724_mid_exists(context, rfc724_mid).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,
|
context: &Context,
|
||||||
rfc724_mid: &str,
|
rfc724_mid: &str,
|
||||||
) -> Result<Option<(String, u32, MsgId)>, Error> {
|
) -> Result<Option<(String, u32, MsgId)>, Error> {
|
||||||
|
let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>');
|
||||||
if rfc724_mid.is_empty() {
|
if rfc724_mid.is_empty() {
|
||||||
warn!(context, "Empty rfc724_mid passed to rfc724_mid_exists");
|
warn!(context, "Empty rfc724_mid passed to rfc724_mid_exists");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|||||||
Reference in New Issue
Block a user