fix: add device message instead of partial message when receive_imf fails

This commit is contained in:
link2xt
2025-11-04 23:32:45 +00:00
committed by l
parent f04c881b8c
commit 81ba2d20d6
5 changed files with 33 additions and 77 deletions

View File

@@ -338,44 +338,27 @@ def test_receive_imf_failure(acfactory) -> None:
bob.set_config("fail_on_receiving_full_msg", "1") bob.set_config("fail_on_receiving_full_msg", "1")
alice_chat_bob.send_text("Hello!") alice_chat_bob.send_text("Hello!")
event = bob.wait_for_incoming_msg_event() event = bob.wait_for_event(EventType.MSGS_CHANGED)
chat_id = event.chat_id assert event.chat_id == bob.get_device_chat().id
msg_id = event.msg_id msg_id = event.msg_id
message = bob.get_message_by_id(msg_id) message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot() snapshot = message.get_snapshot()
assert snapshot.chat_id == chat_id assert (
assert snapshot.download_state == DownloadState.AVAILABLE snapshot.text == "❌ Failed to receive a message:"
assert snapshot.error is not None " Condition failed: `!context.get_config_bool(Config::FailOnReceivingFullMsg).await?`."
assert snapshot.show_padlock " Please report this bug to delta@merlinux.eu or https://support.delta.chat/."
snapshot.chat.accept() )
# The failed message doesn't break the IMAP loop. # The failed message doesn't break the IMAP loop.
bob.set_config("fail_on_receiving_full_msg", "0") bob.set_config("fail_on_receiving_full_msg", "0")
alice_chat_bob.send_text("Hello again!") alice_chat_bob.send_text("Hello again!")
event = bob.wait_for_incoming_msg_event() event = bob.wait_for_incoming_msg_event()
assert event.chat_id == chat_id
msg_id = event.msg_id
message1 = bob.get_message_by_id(msg_id)
snapshot = message1.get_snapshot()
assert snapshot.chat_id == chat_id
assert snapshot.download_state == DownloadState.DONE
assert snapshot.error is None
# The failed message can be re-downloaded later.
bob._rpc.download_full_message(bob.id, message.id)
event = bob.wait_for_event(EventType.MSGS_CHANGED)
message = bob.get_message_by_id(event.msg_id)
snapshot = message.get_snapshot()
assert snapshot.download_state == DownloadState.IN_PROGRESS
event = bob.wait_for_event(EventType.MSGS_CHANGED)
assert event.chat_id == chat_id
msg_id = event.msg_id msg_id = event.msg_id
message = bob.get_message_by_id(msg_id) message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot() snapshot = message.get_snapshot()
assert snapshot.chat_id == chat_id assert snapshot.text == "Hello again!"
assert snapshot.download_state == DownloadState.DONE assert snapshot.download_state == DownloadState.DONE
assert snapshot.error is None assert snapshot.error is None
assert snapshot.text == "Hello!"
def test_selfavatar_sync(acfactory, data, log) -> None: def test_selfavatar_sync(acfactory, data, log) -> None:

View File

@@ -221,21 +221,14 @@ impl MimeMessage {
/// To create the placeholder, only the outermost header can be used, /// To create the placeholder, only the outermost header can be used,
/// the mime-structure itself is not available. /// the mime-structure itself is not available.
/// ///
/// The placeholder part currently contains a text with size and availability of the message; /// The placeholder part currently contains a text with size and availability of the message.
/// `error` is set as the part error;
/// in the future, we may do more advanced things as previews here.
pub(crate) async fn create_stub_from_partial_download( pub(crate) async fn create_stub_from_partial_download(
&mut self, &mut self,
context: &Context, context: &Context,
org_bytes: u32, org_bytes: u32,
error: Option<String>,
) -> Result<()> { ) -> Result<()> {
let prefix = match error {
None => "",
Some(_) => "[❗] ",
};
let mut text = format!( let mut text = format!(
"{prefix}[{}]", "[{}]",
stock_str::partial_download_msg_body(context, org_bytes).await stock_str::partial_download_msg_body(context, org_bytes).await
); );
if let Some(delete_server_after) = context.get_config_delete_server_after().await? { if let Some(delete_server_after) = context.get_config_delete_server_after().await? {
@@ -252,7 +245,6 @@ impl MimeMessage {
self.do_add_single_part(Part { self.do_add_single_part(Part {
typ: Viewtype::Text, typ: Viewtype::Text,
msg: text, msg: text,
error,
..Default::default() ..Default::default()
}); });

View File

@@ -24,7 +24,7 @@ use ratelimit::Ratelimit;
use url::Url; use url::Url;
use crate::calls::{create_fallback_ice_servers, create_ice_servers_from_metadata}; use crate::calls::{create_fallback_ice_servers, create_ice_servers_from_metadata};
use crate::chat::{self, ChatId, ChatIdBlocked}; use crate::chat::{self, ChatId, ChatIdBlocked, add_device_msg};
use crate::chatlist_events; use crate::chatlist_events;
use crate::config::Config; use crate::config::Config;
use crate::constants::{self, Blocked, Chattype, ShowEmails}; use crate::constants::{self, Blocked, Chattype, ShowEmails};
@@ -1468,29 +1468,19 @@ impl Session {
context, context,
"Passing message UID {} to receive_imf().", request_uid "Passing message UID {} to receive_imf().", request_uid
); );
let res = receive_imf_inner( let res = receive_imf_inner(context, rfc724_mid, body, is_seen, partial).await;
context, let received_msg = match res {
rfc724_mid, Err(err) => {
body, warn!(context, "receive_imf error: {err:#}.");
is_seen,
partial.map(|msg_size| (msg_size, None)), let text = format!(
) "❌ Failed to receive a message: {err:#}. Please report this bug to delta@merlinux.eu or https://support.delta.chat/."
.await; );
let received_msg = if let Err(err) = res { let mut msg = Message::new_text(text);
warn!(context, "receive_imf error: {:#}.", err); add_device_msg(context, None, Some(&mut msg)).await?;
if partial.is_some() { None
return Err(err);
} }
receive_imf_inner( Ok(msg) => msg,
context,
rfc724_mid,
body,
is_seen,
Some((body.len().try_into()?, Some(format!("{err:#}")))),
)
.await?
} else {
res?
}; };
received_msgs_channel received_msgs_channel
.send((request_uid, received_msg)) .send((request_uid, received_msg))

View File

@@ -240,12 +240,11 @@ const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup";
impl MimeMessage { impl MimeMessage {
/// Parse a mime message. /// Parse a mime message.
/// ///
/// If `partial` is set, it contains the full message size in bytes and an optional error text /// If `partial` is set, it contains the full message size in bytes.
/// for the partially downloaded message, and `body` contains the HEADER only.
pub(crate) async fn from_bytes( pub(crate) async fn from_bytes(
context: &Context, context: &Context,
body: &[u8], body: &[u8],
partial: Option<(u32, Option<String>)>, partial: Option<u32>,
) -> Result<Self> { ) -> Result<Self> {
let mail = mailparse::parse_mail(body)?; let mail = mailparse::parse_mail(body)?;
@@ -619,9 +618,9 @@ impl MimeMessage {
}; };
match partial { match partial {
Some((org_bytes, err)) => { Some(org_bytes) => {
parser parser
.create_stub_from_partial_download(context, org_bytes, err) .create_stub_from_partial_download(context, org_bytes)
.await?; .await?;
} }
None => match mail { None => match mail {

View File

@@ -186,14 +186,7 @@ pub(crate) async fn receive_imf_from_inbox(
seen: bool, seen: bool,
is_partial_download: Option<u32>, is_partial_download: Option<u32>,
) -> Result<Option<ReceivedMsg>> { ) -> Result<Option<ReceivedMsg>> {
receive_imf_inner( receive_imf_inner(context, rfc724_mid, imf_raw, seen, is_partial_download).await
context,
rfc724_mid,
imf_raw,
seen,
is_partial_download.map(|msg_size| (msg_size, None)),
)
.await
} }
/// Inserts a tombstone into `msgs` table /// Inserts a tombstone into `msgs` table
@@ -490,14 +483,13 @@ async fn get_to_and_past_contact_ids(
/// If the message is so wrong that we didn't even create a database entry, /// If the message is so wrong that we didn't even create a database entry,
/// returns `Ok(None)`. /// returns `Ok(None)`.
/// ///
/// If `partial` is set, it contains the full message size in bytes and an optional error text for /// If `is_partial_download` is set, it contains the full message size in bytes.
/// the partially downloaded message.
pub(crate) async fn receive_imf_inner( pub(crate) async fn receive_imf_inner(
context: &Context, context: &Context,
rfc724_mid: &str, rfc724_mid: &str,
imf_raw: &[u8], imf_raw: &[u8],
seen: bool, seen: bool,
partial: Option<(u32, Option<String>)>, is_partial_download: Option<u32>,
) -> Result<Option<ReceivedMsg>> { ) -> Result<Option<ReceivedMsg>> {
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() { if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
info!( info!(
@@ -506,7 +498,7 @@ pub(crate) async fn receive_imf_inner(
String::from_utf8_lossy(imf_raw), String::from_utf8_lossy(imf_raw),
); );
} }
if partial.is_none() { if is_partial_download.is_none() {
ensure!( ensure!(
!context !context
.get_config_bool(Config::FailOnReceivingFullMsg) .get_config_bool(Config::FailOnReceivingFullMsg)
@@ -514,8 +506,8 @@ pub(crate) async fn receive_imf_inner(
); );
} }
let is_partial_download = partial.as_ref().map(|(msg_size, _err)| *msg_size); let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, is_partial_download).await
let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, partial).await { {
Err(err) => { Err(err) => {
warn!(context, "receive_imf: can't parse MIME: {err:#}."); warn!(context, "receive_imf: can't parse MIME: {err:#}.");
if rfc724_mid.starts_with(GENERATED_PREFIX) { if rfc724_mid.starts_with(GENERATED_PREFIX) {