Compare commits

...

3 Commits

Author SHA1 Message Date
link2xt
2e030aaab9 Adding error context for forward_msgs 2024-10-20 08:28:48 +00:00
link2xt
deb14600e8 feat: add more context to remove_contact_from_chat errors 2024-10-20 08:23:45 +00:00
link2xt
95c252a5a6 refactor(remove_contact_from_chat): remove one level of indentation 2024-10-20 08:23:45 +00:00

View File

@@ -3920,63 +3920,71 @@ pub async fn remove_contact_from_chat(
let mut msg = Message::default();
let chat = Chat::load_from_db(context, chat_id).await?;
if chat.typ == Chattype::Group || chat.typ == Chattype::Broadcast {
if !chat.is_self_in_chat(context).await? {
let err_msg = format!(
"Cannot remove contact {contact_id} from chat {chat_id}: self not in group."
);
context.emit_event(EventType::ErrorSelfNotInGroup(err_msg.clone()));
bail!("{}", err_msg);
} else {
let mut sync = Nosync;
// We do not return an error if the contact does not exist in the database.
// This allows to delete dangling references to deleted contacts
// in case of the database becoming inconsistent due to a bug.
if let Some(contact) = Contact::get_by_id_optional(context, contact_id).await? {
if chat.typ == Chattype::Group && chat.is_promoted() {
msg.viewtype = Viewtype::Text;
if contact_id == ContactId::SELF {
msg.text = stock_str::msg_group_left_local(context, ContactId::SELF).await;
} else {
msg.text = stock_str::msg_del_member_local(
context,
contact.get_addr(),
ContactId::SELF,
)
.await;
}
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
msg.param.set(Param::Arg, contact.get_addr().to_lowercase());
let res = send_msg(context, chat_id, &mut msg).await;
if contact_id == ContactId::SELF {
res?;
set_group_explicitly_left(context, &chat.grpid).await?;
} else if let Err(e) = res {
warn!(context, "remove_contact_from_chat({chat_id}, {contact_id}): send_msg() failed: {e:#}.");
}
if chat.typ != Chattype::Group && chat.typ != Chattype::Broadcast {
bail!("Cannot remove members from non-group chats.");
}
if !chat.is_self_in_chat(context).await? {
let err_msg =
format!("Cannot remove contact {contact_id} from chat {chat_id}: self not in group.");
context.emit_event(EventType::ErrorSelfNotInGroup(err_msg.clone()));
bail!("{}", err_msg);
} else {
let mut sync = Nosync;
// We do not return an error if the contact does not exist in the database.
// This allows to delete dangling references to deleted contacts
// in case of the database becoming inconsistent due to a bug.
if let Some(contact) = Contact::get_by_id_optional(context, contact_id).await? {
if chat.typ == Chattype::Group && chat.is_promoted() {
msg.viewtype = Viewtype::Text;
if contact_id == ContactId::SELF {
msg.text = stock_str::msg_group_left_local(context, ContactId::SELF).await;
} else {
sync = Sync;
msg.text = stock_str::msg_del_member_local(
context,
contact.get_addr(),
ContactId::SELF,
)
.await;
}
}
// we remove the member from the chat after constructing the
// to-be-send message. If between send_msg() and here the
// process dies, the user will be able to redo the action. It's better than the other
// way round: you removed someone from DB but no peer or device gets to know about it
// and group membership is thus different on different devices. But if send_msg()
// failed, we still remove the member locally, otherwise it would be impossible to
// remove a member with missing key from a protected group.
// Note also that sending a message needs all recipients
// in order to correctly determine encryption so if we
// removed it first, it would complicate the
// check/encryption logic.
remove_from_chat_contacts_table(context, chat_id, contact_id).await?;
context.emit_event(EventType::ChatModified(chat_id));
if sync.into() {
chat.sync_contacts(context).await.log_err(context).ok();
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
msg.param.set(Param::Arg, contact.get_addr().to_lowercase());
let res = send_msg(context, chat_id, &mut msg)
.await
.context("Failed to send member removed message");
if contact_id == ContactId::SELF {
res?;
set_group_explicitly_left(context, &chat.grpid)
.await
.context("Failed to mark group as explicitly left")?;
} else if let Err(e) = res {
warn!(
context,
"remove_contact_from_chat({chat_id}, {contact_id}): {e:#}."
);
}
} else {
sync = Sync;
}
}
} else {
bail!("Cannot remove members from non-group chats.");
// we remove the member from the chat after constructing the
// to-be-send message. If between send_msg() and here the
// process dies, the user will be able to redo the action. It's better than the other
// way round: you removed someone from DB but no peer or device gets to know about it
// and group membership is thus different on different devices. But if send_msg()
// failed, we still remove the member locally, otherwise it would be impossible to
// remove a member with missing key from a protected group.
// Note also that sending a message needs all recipients
// in order to correctly determine encryption so if we
// removed it first, it would complicate the
// check/encryption logic.
remove_from_chat_contacts_table(context, chat_id, contact_id)
.await
.context("Failed to remove contact from chats_contacts table")?;
context.emit_event(EventType::ChatModified(chat_id));
if sync.into() {
chat.sync_contacts(context).await.log_err(context).ok();
}
}
Ok(())
@@ -4134,7 +4142,9 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
chat_id
.unarchive_if_not_muted(context, MessageState::Undefined)
.await?;
let mut chat = Chat::load_from_db(context, chat_id).await?;
let mut chat = Chat::load_from_db(context, chat_id)
.await
.context("Failed to load chat from the database")?;
if let Some(reason) = chat.why_cant_send(context).await? {
bail!("cannot send to {}: {}", chat_id, reason);
}
@@ -4150,7 +4160,8 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|row| row.get::<_, MsgId>(0),
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
)
.await?;
.await
.context("Failed to sort forwarded messages by timestamp")?;
for id in ids {
let src_msg_id: MsgId = id;
@@ -4205,9 +4216,14 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
msg.state = MessageState::OutPending;
new_msg_id = chat
.prepare_msg_raw(context, &mut msg, None, curr_timestamp)
.await?;
.await
.context("Failed to prepare forwarded message")?;
curr_timestamp += 1;
if !create_send_msg_jobs(context, &mut msg).await?.is_empty() {
if !create_send_msg_jobs(context, &mut msg)
.await
.context("Failed to create send jobs for the forwarded message")?
.is_empty()
{
context.scheduler.interrupt_smtp().await;
}
}