Skip to content

Commit

Permalink
feat: Don't add "Failed to send message to ..." info messages to grou…
Browse files Browse the repository at this point in the history
…p chats

A NDN may arrive days after the message is sent when it's already impossible to tell which message
wasn't delivered looking at the "Failed to send" info message, so it only clutters the chat and
makes the user think they tried to send some message recently which isn't true. Moreover, the info
message duplicates the info already displayed in the error message behind the exclamation mark and
info messages do not point to the message that is failed to be sent.

Moreover it works rarely because `mimeparser.rs` only parses recipients from `x-failed-recipients`,
so it likely only works for Gmail. Postfix does not add this `X-Failed-Recipients` header. Let's
remove this parsing too. Thanks to @link2xt for pointing this out.
  • Loading branch information
iequidoo committed Dec 9, 2024
1 parent abe81d0 commit 0501917
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 87 deletions.
2 changes: 1 addition & 1 deletion deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -6911,7 +6911,7 @@ void dc_event_unref(dc_event_t* event);

/// "Failed to send message to %1$s."
///
/// Used in status messages.
/// Unused. Was used in group chat status messages.
/// - %1$s will be replaced by the name of the contact the message cannot be sent to
#define DC_STR_FAILED_SENDING_TO 74

Expand Down
78 changes: 9 additions & 69 deletions src/mimeparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ use rand::distributions::{Alphanumeric, DistString};
use crate::aheader::{Aheader, EncryptPreference};
use crate::authres::handle_authres;
use crate::blob::BlobObject;
use crate::chat::{add_info_msg, ChatId};
use crate::chat::ChatId;
use crate::config::Config;
use crate::constants::{self, Chattype};
use crate::contact::{Contact, ContactId, Origin};
use crate::constants;
use crate::contact::ContactId;
use crate::context::Context;
use crate::decrypt::{
get_autocrypt_peerstate, get_encrypted_mime, keyring_from_peerstate, try_decrypt,
Expand All @@ -36,8 +36,7 @@ use crate::peerstate::Peerstate;
use crate::simplify::{simplify, SimplifiedText};
use crate::sync::SyncItems;
use crate::tools::{
create_smeared_timestamp, get_filemeta, parse_receive_headers, smeared_time, truncate_msg_text,
validate_id,
get_filemeta, parse_receive_headers, smeared_time, truncate_msg_text, validate_id,
};
use crate::{chatlist_events, location, stock_str, tools};

Expand Down Expand Up @@ -1665,18 +1664,8 @@ impl MimeMessage {
.get_header_value(HeaderDef::MessageId)
.and_then(|v| parse_message_id(&v).ok())
{
let mut to_list =
get_all_addresses_from_header(&report.headers, "x-failed-recipients");
let to = if to_list.len() != 1 {
// We do not know which recipient failed
None
} else {
to_list.pop()
};

return Ok(Some(DeliveryReport {
rfc724_mid: original_message_id,
failed_recipient: to.map(|s| s.addr),
failure,
}));
}
Expand Down Expand Up @@ -1774,7 +1763,6 @@ impl MimeMessage {
{
self.delivery_report = Some(DeliveryReport {
rfc724_mid: original_message_id,
failed_recipient: None,
failure: true,
})
}
Expand Down Expand Up @@ -1912,7 +1900,6 @@ pub(crate) struct Report {
#[derive(Debug)]
pub(crate) struct DeliveryReport {
pub rfc724_mid: String,
pub failed_recipient: Option<String>,
pub failure: bool,
}

Expand Down Expand Up @@ -2278,37 +2265,26 @@ async fn handle_ndn(
let msgs: Vec<_> = context
.sql
.query_map(
concat!(
"SELECT",
" m.id AS msg_id,",
" c.id AS chat_id,",
" c.type AS type",
" FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id",
" WHERE rfc724_mid=? AND from_id=1",
),
"SELECT id FROM msgs
WHERE rfc724_mid=? AND from_id=1",
(&failed.rfc724_mid,),
|row| {
let msg_id: MsgId = row.get("msg_id")?;
let chat_id: ChatId = row.get("chat_id")?;
let chat_type: Chattype = row.get("type")?;
Ok((msg_id, chat_id, chat_type))
let msg_id: MsgId = row.get(0)?;
Ok(msg_id)
},
|rows| Ok(rows.collect::<Vec<_>>()),
)
.await?;

let error = if let Some(error) = error {
error
} else if let Some(failed_recipient) = &failed.failed_recipient {
format!("Delivery to {failed_recipient} failed.").clone()
} else {
"Delivery to at least one recipient failed.".to_string()
};
let err_msg = &error;

let mut first = true;
for msg in msgs {
let (msg_id, chat_id, chat_type) = msg?;
let msg_id = msg?;
let mut message = Message::load_from_db(context, msg_id).await?;
let aggregated_error = message
.error
Expand All @@ -2320,47 +2296,11 @@ async fn handle_ndn(
aggregated_error.as_ref().unwrap_or(err_msg),
)
.await?;
if first {
// Add only one info msg for all failed messages
ndn_maybe_add_info_msg(context, failed, chat_id, chat_type).await?;
}
first = false;
}

Ok(())
}

async fn ndn_maybe_add_info_msg(
context: &Context,
failed: &DeliveryReport,
chat_id: ChatId,
chat_type: Chattype,
) -> Result<()> {
match chat_type {
Chattype::Group | Chattype::Broadcast => {
if let Some(failed_recipient) = &failed.failed_recipient {
let contact_id =
Contact::lookup_id_by_addr(context, failed_recipient, Origin::Unknown)
.await?
.context("contact ID not found")?;
let contact = Contact::get_by_id(context, contact_id).await?;
// Tell the user which of the recipients failed if we know that (because in
// a group, this might otherwise be unclear)
let text = stock_str::failed_sending_to(context, contact.get_display_name()).await;
add_info_msg(context, chat_id, &text, create_smeared_timestamp(context)).await?;
context.emit_event(EventType::ChatModified(chat_id));
}
}
Chattype::Mailinglist => {
// ndn_maybe_add_info_msg() is about the case when delivery to the group failed.
// If we get an NDN for the mailing list, just issue a warning.
warn!(context, "ignoring NDN for mailing list.");
}
Chattype::Single => {}
}
Ok(())
}

#[cfg(test)]
mod tests {
use mailparse::ParsedMail;
Expand Down
12 changes: 2 additions & 10 deletions src/receive_imf/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,18 +868,10 @@ async fn test_parse_ndn_group_msg() -> Result<()> {
assert_eq!(msg.state, MessageState::OutFailed);

let msgs = chat::get_chat_msgs(&t, msg.chat_id).await?;
let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() {
msg_id
} else {
let ChatItem::Message { msg_id } = *msgs.last().unwrap() else {
panic!("Wrong item type");
};
let last_msg = Message::load_from_db(&t, *msg_id).await?;

assert_eq!(
last_msg.text,
stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await
);
assert_eq!(last_msg.from_id, ContactId::INFO);
assert_eq!(msg_id, msg.id);
Ok(())
}

Expand Down
8 changes: 1 addition & 7 deletions src/stock_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ pub enum StockMessage {
#[strum(props(fallback = "Message from %1$s"))]
SubjectForNewContact = 73,

/// Unused. Was used in group chat status messages.
#[strum(props(fallback = "Failed to send message to %1$s."))]
FailedSendingTo = 74,

Expand Down Expand Up @@ -980,13 +981,6 @@ pub(crate) async fn subject_for_new_contact(context: &Context, self_name: &str)
.replace1(self_name)
}

/// Stock string: `Failed to send message to %1$s.`.
pub(crate) async fn failed_sending_to(context: &Context, name: &str) -> String {
translated(context, StockMessage::FailedSendingTo)
.await
.replace1(name)
}

/// Stock string: `Message deletion timer is disabled.`.
pub(crate) async fn msg_ephemeral_timer_disabled(
context: &Context,
Expand Down

0 comments on commit 0501917

Please sign in to comment.