fix: lookup_chat_by_reply(): Skip not fully downloaded and undecipherable messages (#4676)

Such a message may be assigned to a wrong chat (e.g. undecipherable group msgs often get assigned to
the 1:1 chat with the sender). Add `DownloadState::Undecipherable` so that messages referencing
undecipherable ones don't go to that wrong chat too. Also do not reply to not fully downloaded
messages. Before `Message.error` was checked for that purpose, but a message can be error for many
reasons.
This commit is contained in:
iequidoo
2023-09-09 21:47:39 -03:00
committed by link2xt
parent f02299c06c
commit 676f311f97
5 changed files with 31 additions and 36 deletions

View File

@@ -318,6 +318,7 @@ pub enum DownloadState {
Done, Done,
Available, Available,
Failure, Failure,
Undecipherable,
InProgress, InProgress,
} }
@@ -327,6 +328,7 @@ impl From<download::DownloadState> for DownloadState {
download::DownloadState::Done => DownloadState::Done, download::DownloadState::Done => DownloadState::Done,
download::DownloadState::Available => DownloadState::Available, download::DownloadState::Available => DownloadState::Available,
download::DownloadState::Failure => DownloadState::Failure, download::DownloadState::Failure => DownloadState::Failure,
download::DownloadState::Undecipherable => DownloadState::Undecipherable,
download::DownloadState::InProgress => DownloadState::InProgress, download::DownloadState::InProgress => DownloadState::InProgress,
} }
} }

View File

@@ -187,6 +187,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
DownloadState::Available => " [⬇ Download available]", DownloadState::Available => " [⬇ Download available]",
DownloadState::InProgress => " [⬇ Download in progress...]", DownloadState::InProgress => " [⬇ Download in progress...]",
DownloadState::Failure => " [⬇ Download failed]", DownloadState::Failure => " [⬇ Download failed]",
DownloadState::Undecipherable => " [⬇ Decryption failed]",
}; };
let temp2 = timestamp_to_str(msg.get_timestamp()); let temp2 = timestamp_to_str(msg.get_timestamp());

View File

@@ -23,6 +23,7 @@ use crate::constants::{
use crate::contact::{Contact, ContactId, Origin, VerifiedStatus}; use crate::contact::{Contact, ContactId, Origin, VerifiedStatus};
use crate::context::Context; use crate::context::Context;
use crate::debug_logging::maybe_set_logging_xdc; use crate::debug_logging::maybe_set_logging_xdc;
use crate::download::DownloadState;
use crate::ephemeral::Timer as EphemeralTimer; use crate::ephemeral::Timer as EphemeralTimer;
use crate::events::EventType; use crate::events::EventType;
use crate::html::new_html_mimepart; use crate::html::new_html_mimepart;
@@ -1038,11 +1039,14 @@ impl ChatId {
T: Send + 'static, T: Send + 'static,
{ {
let sql = &context.sql; let sql = &context.sql;
// Do not reply to not fully downloaded messages. Such a message could be a group chat
// message that we assigned to 1:1 chat.
let query = format!( let query = format!(
"SELECT {fields} \ "SELECT {fields} \
FROM msgs WHERE chat_id=? AND state NOT IN (?, ?) AND NOT hidden \ FROM msgs WHERE chat_id=? AND state NOT IN (?, ?) AND NOT hidden AND download_state={} \
ORDER BY timestamp DESC, id DESC \ ORDER BY timestamp DESC, id DESC \
LIMIT 1;" LIMIT 1;",
DownloadState::Done as u32,
); );
let row = sql let row = sql
.query_row_optional( .query_row_optional(
@@ -1067,34 +1071,17 @@ impl ChatId {
self, self,
context: &Context, context: &Context,
) -> Result<Option<(String, String, String)>> { ) -> Result<Option<(String, String, String)>> {
if let Some((rfc724_mid, mime_in_reply_to, mime_references, error)) = self self.parent_query(
.parent_query( context,
context, "rfc724_mid, mime_in_reply_to, mime_references",
"rfc724_mid, mime_in_reply_to, mime_references, error", |row: &rusqlite::Row| {
|row: &rusqlite::Row| { let rfc724_mid: String = row.get(0)?;
let rfc724_mid: String = row.get(0)?; let mime_in_reply_to: String = row.get(1)?;
let mime_in_reply_to: String = row.get(1)?; let mime_references: String = row.get(2)?;
let mime_references: String = row.get(2)?; Ok((rfc724_mid, mime_in_reply_to, mime_references))
let error: String = row.get(3)?; },
Ok((rfc724_mid, mime_in_reply_to, mime_references, error)) )
}, .await
)
.await?
{
if !error.is_empty() {
// Do not reply to error messages.
//
// An error message could be a group chat message that we failed to decrypt and
// assigned to 1:1 chat. A reply to it will show up as a reply to group message
// on the other side. To avoid such situations, it is better not to reply to
// error messages at all.
Ok(None)
} else {
Ok(Some((rfc724_mid, mime_in_reply_to, mime_references)))
}
} else {
Ok(None)
}
} }
/// Returns multi-line text summary of encryption preferences of all chat contacts. /// Returns multi-line text summary of encryption preferences of all chat contacts.

View File

@@ -59,6 +59,9 @@ pub enum DownloadState {
/// Failed to fully download the message. /// Failed to fully download the message.
Failure = 20, Failure = 20,
/// Undecipherable message.
Undecipherable = 30,
/// Full download of the message is in progress. /// Full download of the message is in progress.
InProgress = 1000, InProgress = 1000,
} }
@@ -80,7 +83,9 @@ impl MsgId {
pub async fn download_full(self, context: &Context) -> Result<()> { pub async fn download_full(self, context: &Context) -> Result<()> {
let msg = Message::load_from_db(context, self).await?; let msg = Message::load_from_db(context, self).await?;
match msg.download_state() { match msg.download_state() {
DownloadState::Done => return Err(anyhow!("Nothing to download.")), DownloadState::Done | DownloadState::Undecipherable => {
return Err(anyhow!("Nothing to download."))
}
DownloadState::InProgress => return Err(anyhow!("Download already in progress.")), DownloadState::InProgress => return Err(anyhow!("Download already in progress.")),
DownloadState::Available | DownloadState::Failure => { DownloadState::Available | DownloadState::Failure => {
self.update_download_state(context, DownloadState::InProgress) self.update_download_state(context, DownloadState::InProgress)

View File

@@ -1227,6 +1227,8 @@ RETURNING id
ephemeral_timestamp, ephemeral_timestamp,
if is_partial_download.is_some() { if is_partial_download.is_some() {
DownloadState::Available DownloadState::Available
} else if mime_parser.decrypting_failed {
DownloadState::Undecipherable
} else { } else {
DownloadState::Done DownloadState::Done
}, },
@@ -1409,11 +1411,9 @@ async fn lookup_chat_by_reply(
if let Some(parent) = parent { if let Some(parent) = parent {
let parent_chat = Chat::load_from_db(context, parent.chat_id).await?; let parent_chat = Chat::load_from_db(context, parent.chat_id).await?;
if parent.error.is_some() { if parent.download_state != DownloadState::Done {
// If the parent msg is undecipherable, then it may have been assigned to the wrong chat // If the parent msg is not fully downloaded or undecipherable, it may have been
// (undecipherable group msgs often get assigned to the 1:1 chat with the sender). // assigned to the wrong chat (they often get assigned to the 1:1 chat with the sender).
// We don't have any way of finding out whether a msg is undecipherable, so we check for
// error.is_some() instead.
return Ok(None); return Ok(None);
} }