api: don't load trashed messages with Message::load_from_db

API now pretends that trashed messages don't exist.
This way callers don't have to check if loaded message
belongs to trash chat.
If message may be trashed by the time it is attempted to be loaded,
callers should use Message::load_from_db_optional.

Most changes are around receive_status_update() function
because previously it relied on loading trashed status update
messages immediately after adding them to the database.
This commit is contained in:
link2xt
2024-04-13 23:34:09 +00:00
parent 94ac2b1097
commit c069190b68
11 changed files with 161 additions and 116 deletions

View File

@@ -448,10 +448,9 @@ mod tests {
) )
.await?; .await?;
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0); assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0);
assert!(Message::load_from_db(&bob, msg.id) assert!(Message::load_from_db_optional(&bob, msg.id)
.await? .await?
.chat_id .is_none());
.is_trash());
Ok(()) Ok(())
} }
@@ -507,10 +506,9 @@ mod tests {
// (usually mdn are too small for not being downloaded directly) // (usually mdn are too small for not being downloaded directly)
receive_imf_from_inbox(&bob, "bar@example.org", raw, false, None, false).await?; receive_imf_from_inbox(&bob, "bar@example.org", raw, false, None, false).await?;
assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0); assert_eq!(get_chat_msgs(&bob, chat_id).await?.len(), 0);
assert!(Message::load_from_db(&bob, msg.id) assert!(Message::load_from_db_optional(&bob, msg.id)
.await? .await?
.chat_id .is_none());
.is_trash());
Ok(()) Ok(())
} }

View File

@@ -1098,8 +1098,8 @@ mod tests {
}) })
.await; .await;
let loaded = Message::load_from_db(t, msg_id).await?; let loaded = Message::load_from_db_optional(t, msg_id).await?;
assert_eq!(loaded.chat_id, DC_CHAT_ID_TRASH); assert!(loaded.is_none());
// Check that the msg was deleted locally. // Check that the msg was deleted locally.
check_msg_is_deleted(t, chat, msg_id).await; check_msg_is_deleted(t, chat, msg_id).await;

View File

@@ -1158,7 +1158,8 @@ mod tests {
// Send a message that cannot be decrypted because the keys are // Send a message that cannot be decrypted because the keys are
// not synchronized yet. // not synchronized yet.
let sent = alice2.send_text(msg.chat_id, "Test").await; let sent = alice2.send_text(msg.chat_id, "Test").await;
alice.recv_msg(&sent).await; let trashed_message = alice.recv_msg_opt(&sent).await;
assert!(trashed_message.is_none());
assert_ne!(alice.get_last_msg().await.get_text(), "Test"); assert_ne!(alice.get_last_msg().await.get_text(), "Test");
// Transfer the key. // Transfer the key.

View File

@@ -506,7 +506,7 @@ impl Message {
" m.location_id AS location,", " m.location_id AS location,",
" c.blocked AS blocked", " c.blocked AS blocked",
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id", " FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
" WHERE m.id=?;" " WHERE m.id=? AND chat_id!=3;"
), ),
(id,), (id,),
|row| { |row| {
@@ -1145,13 +1145,8 @@ impl Message {
pub async fn parent(&self, context: &Context) -> Result<Option<Message>> { pub async fn parent(&self, context: &Context) -> Result<Option<Message>> {
if let Some(in_reply_to) = &self.in_reply_to { if let Some(in_reply_to) = &self.in_reply_to {
if let Some((msg_id, _ts_sent)) = rfc724_mid_exists(context, in_reply_to).await? { if let Some((msg_id, _ts_sent)) = rfc724_mid_exists(context, in_reply_to).await? {
let msg = Message::load_from_db(context, msg_id).await?; let msg = Message::load_from_db_optional(context, msg_id).await?;
return if msg.chat_id.is_trash() { return Ok(msg);
// If message is already moved to trash chat, pretend it does not exist.
Ok(None)
} else {
Ok(Some(msg))
};
} }
} }
Ok(None) Ok(None)
@@ -1881,8 +1876,7 @@ pub(crate) async fn get_latest_by_rfc724_mids(
) -> Result<Option<Message>> { ) -> Result<Option<Message>> {
for id in mids.iter().rev() { for id in mids.iter().rev() {
if let Some((msg_id, _)) = rfc724_mid_exists(context, id).await? { if let Some((msg_id, _)) = rfc724_mid_exists(context, id).await? {
let msg = Message::load_from_db(context, msg_id).await?; if let Some(msg) = Message::load_from_db_optional(context, msg_id).await? {
if msg.chat_id != DC_CHAT_ID_TRASH {
return Ok(Some(msg)); return Ok(Some(msg));
} }
} }

View File

@@ -385,7 +385,6 @@ mod tests {
use crate::chat::{forward_msgs, get_chat_msgs, send_text_msg}; use crate::chat::{forward_msgs, get_chat_msgs, send_text_msg};
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::constants::DC_CHAT_ID_TRASH;
use crate::contact::{Contact, ContactAddress, Origin}; use crate::contact::{Contact, ContactAddress, Origin};
use crate::download::DownloadState; use crate::download::DownloadState;
use crate::message::{delete_msgs, MessageState}; use crate::message::{delete_msgs, MessageState};
@@ -594,8 +593,7 @@ Here's my footer -- bob@example.net"
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2); assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
let bob_reaction_msg = bob.pop_sent_msg().await; let bob_reaction_msg = bob.pop_sent_msg().await;
let alice_reaction_msg = alice.recv_msg_opt(&bob_reaction_msg).await.unwrap(); alice.recv_msg_trash(&bob_reaction_msg).await;
assert_eq!(alice_reaction_msg.chat_id, DC_CHAT_ID_TRASH);
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2); assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?; let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
@@ -648,8 +646,7 @@ Here's my footer -- bob@example.net"
bob_msg1.chat_id.accept(&bob).await?; bob_msg1.chat_id.accept(&bob).await?;
send_reaction(&bob, bob_msg1.id, "👍").await?; send_reaction(&bob, bob_msg1.id, "👍").await?;
let bob_send_reaction = bob.pop_sent_msg().await; let bob_send_reaction = bob.pop_sent_msg().await;
let alice_rcvd_reaction = alice.recv_msg(&bob_send_reaction).await; alice.recv_msg_trash(&bob_send_reaction).await;
assert!(alice_rcvd_reaction.get_timestamp() > bob_msg1.get_timestamp());
let chatlist = Chatlist::try_load(&bob, 0, None, None).await?; let chatlist = Chatlist::try_load(&bob, 0, None, None).await?;
let summary = chatlist.get_summary(&bob, 0, None).await?; let summary = chatlist.get_summary(&bob, 0, None).await?;
@@ -664,7 +661,7 @@ Here's my footer -- bob@example.net"
SystemTime::shift(Duration::from_secs(10)); SystemTime::shift(Duration::from_secs(10));
send_reaction(&alice, alice_msg1.sender_msg_id, "🍿").await?; send_reaction(&alice, alice_msg1.sender_msg_id, "🍿").await?;
let alice_send_reaction = alice.pop_sent_msg().await; let alice_send_reaction = alice.pop_sent_msg().await;
bob.recv_msg(&alice_send_reaction).await; bob.recv_msg_opt(&alice_send_reaction).await;
assert_summary(&alice, "You reacted 🍿 to \"Party?\"").await; assert_summary(&alice, "You reacted 🍿 to \"Party?\"").await;
assert_summary(&bob, "ALICE reacted 🍿 to \"Party?\"").await; assert_summary(&bob, "ALICE reacted 🍿 to \"Party?\"").await;
@@ -681,7 +678,7 @@ Here's my footer -- bob@example.net"
SystemTime::shift(Duration::from_secs(10)); SystemTime::shift(Duration::from_secs(10));
send_reaction(&alice, alice_msg1.sender_msg_id, "🤘").await?; send_reaction(&alice, alice_msg1.sender_msg_id, "🤘").await?;
let alice_send_reaction = alice.pop_sent_msg().await; let alice_send_reaction = alice.pop_sent_msg().await;
bob.recv_msg(&alice_send_reaction).await; bob.recv_msg_opt(&alice_send_reaction).await;
assert_summary(&alice, "You reacted 🤘 to \"Party?\"").await; assert_summary(&alice, "You reacted 🤘 to \"Party?\"").await;
assert_summary(&bob, "ALICE reacted 🤘 to \"Party?\"").await; assert_summary(&bob, "ALICE reacted 🤘 to \"Party?\"").await;
@@ -690,7 +687,7 @@ Here's my footer -- bob@example.net"
SystemTime::shift(Duration::from_secs(10)); SystemTime::shift(Duration::from_secs(10));
send_reaction(&alice, alice_msg1.sender_msg_id, "").await?; send_reaction(&alice, alice_msg1.sender_msg_id, "").await?;
let alice_remove_reaction = alice.pop_sent_msg().await; let alice_remove_reaction = alice.pop_sent_msg().await;
bob.recv_msg(&alice_remove_reaction).await; bob.recv_msg_opt(&alice_remove_reaction).await;
assert_summary(&alice, "kewl").await; assert_summary(&alice, "kewl").await;
assert_summary(&bob, "kewl").await; assert_summary(&bob, "kewl").await;
@@ -807,7 +804,7 @@ Here's my footer -- bob@example.net"
let bob_reaction_msg = bob.pop_sent_msg().await; let bob_reaction_msg = bob.pop_sent_msg().await;
// Alice receives a reaction. // Alice receives a reaction.
alice.recv_msg_opt(&bob_reaction_msg).await.unwrap(); alice.recv_msg_trash(&bob_reaction_msg).await;
let reactions = get_msg_reactions(&alice, alice_msg_id).await?; let reactions = get_msg_reactions(&alice, alice_msg_id).await?;
assert_eq!(reactions.to_string(), "👍1"); assert_eq!(reactions.to_string(), "👍1");
@@ -859,7 +856,7 @@ Here's my footer -- bob@example.net"
{ {
send_reaction(&alice2, alice2_msg.id, "👍").await?; send_reaction(&alice2, alice2_msg.id, "👍").await?;
let msg = alice2.pop_sent_msg().await; let msg = alice2.pop_sent_msg().await;
alice1.recv_msg(&msg).await; alice1.recv_msg_trash(&msg).await;
} }
// Check that the status is still the same. // Check that the status is still the same.
@@ -881,7 +878,7 @@ Here's my footer -- bob@example.net"
let alice1_msg = alice1.recv_msg(&alice0.pop_sent_msg().await).await; let alice1_msg = alice1.recv_msg(&alice0.pop_sent_msg().await).await;
send_reaction(&alice0, alice0_msg_id, "👀").await?; send_reaction(&alice0, alice0_msg_id, "👀").await?;
alice1.recv_msg(&alice0.pop_sent_msg().await).await; alice1.recv_msg_trash(&alice0.pop_sent_msg().await).await;
expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?; expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?;
expect_reactions_changed_event(&alice1, alice1_msg.chat_id, alice1_msg.id, ContactId::SELF) expect_reactions_changed_event(&alice1, alice1_msg.chat_id, alice1_msg.id, ContactId::SELF)

View File

@@ -476,11 +476,46 @@ pub(crate) async fn receive_imf_inner(
} }
if let Some(ref status_update) = mime_parser.webxdc_status_update { if let Some(ref status_update) = mime_parser.webxdc_status_update {
if let Err(err) = context let can_info_msg;
.receive_status_update(from_id, insert_msg_id, status_update) let instance = if mime_parser
.await .parts
.first()
.filter(|part| part.typ == Viewtype::Webxdc)
.is_some()
{ {
warn!(context, "receive_imf cannot update status: {err:#}."); can_info_msg = false;
Some(Message::load_from_db(context, insert_msg_id).await?)
} else if let Some(field) = mime_parser.get_header(HeaderDef::InReplyTo) {
if let Some(instance) = get_rfc724_mid_in_list(context, field).await? {
can_info_msg = instance.download_state() == DownloadState::Done;
Some(instance)
} else {
can_info_msg = false;
None
}
} else {
can_info_msg = false;
None
};
if let Some(instance) = instance {
if let Err(err) = context
.receive_status_update(
from_id,
&instance,
received_msg.sort_timestamp,
can_info_msg,
status_update,
)
.await
{
warn!(context, "receive_imf cannot update status: {err:#}.");
}
} else {
warn!(
context,
"Received webxdc update, but cannot assign it to message."
);
} }
} }
@@ -2694,8 +2729,6 @@ async fn mark_recipients_as_verified(
/// Returns the last message referenced from `References` header if it is in the database. /// Returns the last message referenced from `References` header if it is in the database.
/// ///
/// For Delta Chat messages it is the last message in the chat of the sender. /// For Delta Chat messages it is the last message in the chat of the sender.
///
/// Note that the returned message may be trashed.
async fn get_previous_message( async fn get_previous_message(
context: &Context, context: &Context,
mime_parser: &MimeMessage, mime_parser: &MimeMessage,
@@ -2703,7 +2736,7 @@ async fn get_previous_message(
if let Some(field) = mime_parser.get_header(HeaderDef::References) { if let Some(field) = mime_parser.get_header(HeaderDef::References) {
if let Some(rfc724mid) = parse_message_ids(field).last() { if let Some(rfc724mid) = parse_message_ids(field).last() {
if let Some((msg_id, _)) = rfc724_mid_exists(context, rfc724mid).await? { if let Some((msg_id, _)) = rfc724_mid_exists(context, rfc724mid).await? {
return Ok(Some(Message::load_from_db(context, msg_id).await?)); return Message::load_from_db_optional(context, msg_id).await;
} }
} }
} }

View File

@@ -4014,8 +4014,7 @@ async fn test_member_left_does_not_create_chat() -> Result<()> {
// which some members simply deleted and some members left, // which some members simply deleted and some members left,
// recreating the chat for others. // recreating the chat for others.
remove_contact_from_chat(&alice, alice_chat_id, ContactId::SELF).await?; remove_contact_from_chat(&alice, alice_chat_id, ContactId::SELF).await?;
let bob_chat_id = bob.recv_msg(&alice.pop_sent_msg().await).await.chat_id; bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
assert!(bob_chat_id.is_trash());
Ok(()) Ok(())
} }
@@ -4365,8 +4364,8 @@ async fn test_forged_from() -> Result<()> {
.payload .payload
.replace("bob@example.net", "notbob@example.net"); .replace("bob@example.net", "notbob@example.net");
let msg = alice.recv_msg(&sent_msg).await; let msg = alice.recv_msg_opt(&sent_msg).await;
assert!(msg.chat_id.is_trash()); assert!(msg.is_none());
Ok(()) Ok(())
} }

View File

@@ -833,7 +833,7 @@ mod tests {
assert!(msg.get_header(HeaderDef::SecureJoinInvitenumber).is_some()); assert!(msg.get_header(HeaderDef::SecureJoinInvitenumber).is_some());
// Step 3: Alice receives vc-request, sends vc-auth-required // Step 3: Alice receives vc-request, sends vc-auth-required
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!( assert_eq!(
Chatlist::try_load(&alice, 0, None, None) Chatlist::try_load(&alice, 0, None, None)
.await .await
@@ -851,7 +851,7 @@ mod tests {
); );
// Step 4: Bob receives vc-auth-required, sends vc-request-with-auth // Step 4: Bob receives vc-auth-required, sends vc-request-with-auth
bob.recv_msg(&sent).await; bob.recv_msg_trash(&sent).await;
// Check Bob emitted the JoinerProgress event. // Check Bob emitted the JoinerProgress event.
let event = bob let event = bob
@@ -933,7 +933,7 @@ mod tests {
} }
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm // Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice.ctx).await.unwrap(), true); assert_eq!(contact_bob.is_verified(&alice.ctx).await.unwrap(), true);
// exactly one one-to-one chat should be visible for both now // exactly one one-to-one chat should be visible for both now
@@ -982,7 +982,7 @@ mod tests {
assert_eq!(contact_bob.is_verified(&bob.ctx).await.unwrap(), false); assert_eq!(contact_bob.is_verified(&bob.ctx).await.unwrap(), false);
// Step 7: Bob receives vc-contact-confirm // Step 7: Bob receives vc-contact-confirm
bob.recv_msg(&sent).await; bob.recv_msg_trash(&sent).await;
assert_eq!(contact_alice.is_verified(&bob.ctx).await.unwrap(), true); assert_eq!(contact_alice.is_verified(&bob.ctx).await.unwrap(), true);
// Check Bob got the verified message in his 1:1 chat. // Check Bob got the verified message in his 1:1 chat.
@@ -1083,7 +1083,7 @@ mod tests {
assert_eq!(contact_bob.is_verified(&alice.ctx).await?, false); assert_eq!(contact_bob.is_verified(&alice.ctx).await?, false);
// Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm // Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice.ctx).await?, true); assert_eq!(contact_bob.is_verified(&alice.ctx).await?, true);
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
@@ -1104,7 +1104,7 @@ mod tests {
assert_eq!(contact_bob.is_verified(&bob.ctx).await?, false); assert_eq!(contact_bob.is_verified(&bob.ctx).await?, false);
// Step 7: Bob receives vc-contact-confirm // Step 7: Bob receives vc-contact-confirm
bob.recv_msg(&sent).await; bob.recv_msg_trash(&sent).await;
assert_eq!(contact_alice.is_verified(&bob.ctx).await?, true); assert_eq!(contact_alice.is_verified(&bob.ctx).await?, true);
Ok(()) Ok(())
@@ -1182,7 +1182,7 @@ mod tests {
assert!(msg.get_header(HeaderDef::SecureJoinGroup).is_none()); assert!(msg.get_header(HeaderDef::SecureJoinGroup).is_none());
// Step 3: Alice receives vg-request, sends vg-auth-required // Step 3: Alice receives vg-request, sends vg-auth-required
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
let msg = bob.parse_msg(&sent).await; let msg = bob.parse_msg(&sent).await;
@@ -1193,7 +1193,7 @@ mod tests {
); );
// Step 4: Bob receives vg-auth-required, sends vg-request-with-auth // Step 4: Bob receives vg-auth-required, sends vg-request-with-auth
bob.recv_msg(&sent).await; bob.recv_msg_trash(&sent).await;
let sent = bob.pop_sent_msg().await; let sent = bob.pop_sent_msg().await;
// Check Bob emitted the JoinerProgress event. // Check Bob emitted the JoinerProgress event.
@@ -1240,7 +1240,7 @@ mod tests {
assert_eq!(contact_bob.is_verified(&alice.ctx).await?, false); assert_eq!(contact_bob.is_verified(&alice.ctx).await?, false);
// Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added // Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
assert_eq!(contact_bob.is_verified(&alice.ctx).await?, true); assert_eq!(contact_bob.is_verified(&alice.ctx).await?, true);
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
@@ -1388,15 +1388,15 @@ First thread."#;
// vc-request // vc-request
let sent = bob.pop_sent_msg().await; let sent = bob.pop_sent_msg().await;
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
// vc-auth-required // vc-auth-required
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
bob.recv_msg(&sent).await; bob.recv_msg_trash(&sent).await;
// vc-request-with-auth // vc-request-with-auth
let sent = bob.pop_sent_msg().await; let sent = bob.pop_sent_msg().await;
alice.recv_msg(&sent).await; alice.recv_msg_trash(&sent).await;
// Alice has Bob verified now. // Alice has Bob verified now.
let contact_bob_id = let contact_bob_id =

View File

@@ -571,7 +571,7 @@ mod tests {
let sent_msg = alice.pop_sent_msg().await; let sent_msg = alice.pop_sent_msg().await;
let alice2 = TestContext::new_alice().await; let alice2 = TestContext::new_alice().await;
alice2.set_config_bool(Config::SyncMsgs, true).await?; alice2.set_config_bool(Config::SyncMsgs, true).await?;
alice2.recv_msg(&sent_msg).await; alice2.recv_msg_trash(&sent_msg).await;
assert!(token::exists(&alice2, token::Namespace::Auth, "testtoken").await?); assert!(token::exists(&alice2, token::Namespace::Auth, "testtoken").await?);
assert_eq!(Chatlist::try_load(&alice2, 0, None, None).await?.len(), 0); assert_eq!(Chatlist::try_load(&alice2, 0, None, None).await?.len(), 0);

View File

@@ -27,8 +27,7 @@ use crate::chat::{
}; };
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::constants::DC_GCL_NO_SPECIALS; use crate::constants::{Blocked, Chattype, DC_CHAT_ID_TRASH, DC_GCL_NO_SPECIALS};
use crate::constants::{Blocked, Chattype};
use crate::contact::{Contact, ContactAddress, ContactId, Modifier, Origin}; use crate::contact::{Contact, ContactAddress, ContactId, Modifier, Origin};
use crate::context::Context; use crate::context::Context;
use crate::e2ee::EncryptHelper; use crate::e2ee::EncryptHelper;
@@ -179,9 +178,9 @@ impl TestContextManager {
loop { loop {
if let Some(sent) = scanner.pop_sent_msg_opt(Duration::ZERO).await { if let Some(sent) = scanner.pop_sent_msg_opt(Duration::ZERO).await {
scanned.recv_msg(&sent).await; scanned.recv_msg_opt(&sent).await;
} else if let Some(sent) = scanned.pop_sent_msg_opt(Duration::ZERO).await { } else if let Some(sent) = scanned.pop_sent_msg_opt(Duration::ZERO).await {
scanner.recv_msg(&sent).await; scanner.recv_msg_opt(&sent).await;
} else { } else {
break; break;
} }
@@ -537,6 +536,16 @@ impl TestContext {
receive_imf(self, msg.payload().as_bytes(), false) receive_imf(self, msg.payload().as_bytes(), false)
.await .await
.unwrap() .unwrap()
.filter(|msg| msg.chat_id != DC_CHAT_ID_TRASH)
}
/// Recevies a message and asserts that it goes to trash chat.
pub async fn recv_msg_trash(&self, msg: &SentMessage<'_>) {
let received = receive_imf(self, msg.payload().as_bytes(), false)
.await
.unwrap()
.unwrap();
assert_eq!(received.chat_id, DC_CHAT_ID_TRASH);
} }
/// Gets the most recent message of a chat. /// Gets the most recent message of a chat.
@@ -1069,7 +1078,8 @@ pub(crate) async fn mark_as_verified(this: &TestContext, other: &TestContext) {
/// alice0's side that implies sending a sync message. /// alice0's side that implies sending a sync message.
pub(crate) async fn sync(alice0: &TestContext, alice1: &TestContext) { pub(crate) async fn sync(alice0: &TestContext, alice1: &TestContext) {
let sync_msg = alice0.pop_sent_msg().await; let sync_msg = alice0.pop_sent_msg().await;
alice1.recv_msg(&sync_msg).await; let no_msg = alice1.recv_msg_opt(&sync_msg).await;
assert!(no_msg.is_none());
} }
/// Pretty-print an event to stdout /// Pretty-print an event to stdout

View File

@@ -30,7 +30,6 @@ use crate::chat::{self, Chat};
use crate::constants::Chattype; use crate::constants::Chattype;
use crate::contact::ContactId; use crate::contact::ContactId;
use crate::context::Context; use crate::context::Context;
use crate::download::DownloadState;
use crate::events::EventType; use crate::events::EventType;
use crate::message::{Message, MessageState, MsgId, Viewtype}; use crate::message::{Message, MessageState, MsgId, Viewtype};
use crate::mimefactory::wrapped_base64_encode; use crate::mimefactory::wrapped_base64_encode;
@@ -285,7 +284,7 @@ impl Context {
/// writes it to the database and handles events, info-messages, document name and summary. /// writes it to the database and handles events, info-messages, document name and summary.
async fn create_status_update_record( async fn create_status_update_record(
&self, &self,
instance: &mut Message, instance: &Message,
status_update_item: StatusUpdateItem, status_update_item: StatusUpdateItem,
timestamp: i64, timestamp: i64,
can_info_msg: bool, can_info_msg: bool,
@@ -329,6 +328,7 @@ impl Context {
let mut param_changed = false; let mut param_changed = false;
let mut instance = instance.clone();
if let Some(ref document) = status_update_item.document { if let Some(ref document) = status_update_item.document {
if instance if instance
.param .param
@@ -446,7 +446,7 @@ impl Context {
mut status_update: StatusUpdateItem, mut status_update: StatusUpdateItem,
descr: &str, descr: &str,
) -> Result<()> { ) -> Result<()> {
let mut instance = Message::load_from_db(self, instance_msg_id) let instance = Message::load_from_db(self, instance_msg_id)
.await .await
.with_context(|| { .with_context(|| {
format!("Failed to load message {instance_msg_id} from the database") format!("Failed to load message {instance_msg_id} from the database")
@@ -474,7 +474,7 @@ impl Context {
status_update.uid = Some(create_id()); status_update.uid = Some(create_id());
let status_update_serial: StatusUpdateSerial = self let status_update_serial: StatusUpdateSerial = self
.create_status_update_record( .create_status_update_record(
&mut instance, &instance,
status_update, status_update,
create_smeared_timestamp(self), create_smeared_timestamp(self),
send_now, send_now,
@@ -569,33 +569,22 @@ impl Context {
/// Receives status updates from receive_imf to the database /// Receives status updates from receive_imf to the database
/// and sends out an event. /// and sends out an event.
/// ///
/// `from_id` is the sender /// `instance` is a webxdc instance.
/// ///
/// `msg_id` may be an instance (in case there are initial status updates) /// `from_id` is the sender.
/// or a reply to an instance (for all other updates). ///
/// `timestamp` is the timestamp of the update.
/// ///
/// `json` is an array containing one or more update items as created by send_webxdc_status_update(), /// `json` is an array containing one or more update items as created by send_webxdc_status_update(),
/// the array is parsed using serde, the single payloads are used as is. /// the array is parsed using serde, the single payloads are used as is.
pub(crate) async fn receive_status_update( pub(crate) async fn receive_status_update(
&self, &self,
from_id: ContactId, from_id: ContactId,
msg_id: MsgId, instance: &Message,
timestamp: i64,
can_info_msg: bool,
json: &str, json: &str,
) -> Result<()> { ) -> Result<()> {
let msg = Message::load_from_db(self, msg_id).await?;
let (timestamp, mut instance, can_info_msg) = if msg.viewtype == Viewtype::Webxdc {
(msg.timestamp_sort, msg, false)
} else if let Some(parent) = msg.parent(self).await? {
if parent.viewtype == Viewtype::Webxdc {
(msg.timestamp_sort, parent, true)
} else if parent.download_state() != DownloadState::Done {
(msg.timestamp_sort, parent, false)
} else {
bail!("receive_status_update: message is not the child of a webxdc message.")
}
} else {
bail!("receive_status_update: status message has no parent.")
};
let chat_id = instance.chat_id; let chat_id = instance.chat_id;
if from_id != ContactId::SELF && !chat::is_contact_in_chat(self, chat_id, from_id).await? { if from_id != ContactId::SELF && !chat::is_contact_in_chat(self, chat_id, from_id).await? {
@@ -612,7 +601,7 @@ impl Context {
let updates: StatusUpdates = serde_json::from_str(json)?; let updates: StatusUpdates = serde_json::from_str(json)?;
for update_item in updates.updates { for update_item in updates.updates {
self.create_status_update_record( self.create_status_update_record(
&mut instance, instance,
update_item, update_item,
timestamp, timestamp,
can_info_msg, can_info_msg,
@@ -866,9 +855,10 @@ mod tests {
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::contact::Contact; use crate::contact::Contact;
use crate::download::DownloadState;
use crate::ephemeral; use crate::ephemeral;
use crate::receive_imf::{receive_imf, receive_imf_from_inbox}; use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
use crate::test_utils::TestContext; use crate::test_utils::{TestContext, TestContextManager};
use crate::tools::{self, SystemTime}; use crate::tools::{self, SystemTime};
use crate::{message, sql}; use crate::{message, sql};
@@ -1053,8 +1043,10 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_webxdc_instance_and_info() -> Result<()> { async fn test_resend_webxdc_instance_and_info() -> Result<()> {
let mut tcm = TestContextManager::new();
// Alice uses webxdc in a group // Alice uses webxdc in a group
let alice = TestContext::new_alice().await; let alice = tcm.alice().await;
alice.set_config_bool(Config::BccSelf, false).await?; alice.set_config_bool(Config::BccSelf, false).await?;
let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?; let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
let alice_instance = send_webxdc_instance(&alice, alice_grp).await?; let alice_instance = send_webxdc_instance(&alice, alice_grp).await?;
@@ -1069,7 +1061,7 @@ mod tests {
assert_eq!(alice_grp.get_msg_cnt(&alice).await?, 2); assert_eq!(alice_grp.get_msg_cnt(&alice).await?, 2);
assert!(alice.get_last_msg_in(alice_grp).await.is_info()); assert!(alice.get_last_msg_in(alice_grp).await.is_info());
// Alice adds Bob and resend already used webxdc // Alice adds Bob and resends already used webxdc
add_contact_to_chat( add_contact_to_chat(
&alice, &alice,
alice_grp, alice_grp,
@@ -1081,7 +1073,7 @@ mod tests {
let sent1 = alice.pop_sent_msg().await; let sent1 = alice.pop_sent_msg().await;
// Bob received webxdc, legacy info-messages updates are received but not added to the chat // Bob received webxdc, legacy info-messages updates are received but not added to the chat
let bob = TestContext::new_bob().await; let bob = tcm.bob().await;
let bob_instance = bob.recv_msg(&sent1).await; let bob_instance = bob.recv_msg(&sent1).await;
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert!(!bob_instance.is_info()); assert!(!bob_instance.is_info());
@@ -1204,7 +1196,7 @@ mod tests {
.await?; .await?;
let bob_instance = bob.get_last_msg().await; let bob_instance = bob.get_last_msg().await;
bob_instance.chat_id.accept(&bob).await?; bob_instance.chat_id.accept(&bob).await?;
bob.recv_msg(&sent2).await; bob.recv_msg_trash(&sent2).await;
assert_eq!(bob_instance.download_state, DownloadState::Available); assert_eq!(bob_instance.download_state, DownloadState::Available);
// Bob downloads instance, updates should be assigned correctly // Bob downloads instance, updates should be assigned correctly
@@ -1239,9 +1231,12 @@ mod tests {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_webxdc_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
let now = tools::time();
t.receive_status_update( t.receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updates":[{"payload":1}]}"#, r#"{"updates":[{"payload":1}]}"#,
) )
.await?; .await?;
@@ -1269,9 +1264,12 @@ mod tests {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_webxdc_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
let now = tools::time();
t.receive_status_update( t.receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updates":[{"payload":1}, {"payload":2}]}"#, r#"{"updates":[{"payload":1}, {"payload":2}]}"#,
) )
.await?; .await?;
@@ -1336,7 +1334,7 @@ mod tests {
async fn test_create_status_update_record() -> Result<()> { async fn test_create_status_update_record() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let mut instance = send_webxdc_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
assert_eq!( assert_eq!(
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0)) t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
@@ -1346,7 +1344,7 @@ mod tests {
let update_id1 = t let update_id1 = t
.create_status_update_record( .create_status_update_record(
&mut instance, &instance,
StatusUpdateItem { StatusUpdateItem {
payload: json!({"foo": "bar"}), payload: json!({"foo": "bar"}),
info: None, info: None,
@@ -1370,7 +1368,7 @@ mod tests {
// Whatever the payload is, update should be ignored just because ID is duplicate. // Whatever the payload is, update should be ignored just because ID is duplicate.
let update_id1_duplicate = t let update_id1_duplicate = t
.create_status_update_record( .create_status_update_record(
&mut instance, &instance,
StatusUpdateItem { StatusUpdateItem {
payload: json!({"nothing": "this should be ignored"}), payload: json!({"nothing": "this should be ignored"}),
info: None, info: None,
@@ -1403,7 +1401,7 @@ mod tests {
let update_id2 = t let update_id2 = t
.create_status_update_record( .create_status_update_record(
&mut instance, &instance,
StatusUpdateItem { StatusUpdateItem {
payload: json!({"foo2": "bar2"}), payload: json!({"foo2": "bar2"}),
info: None, info: None,
@@ -1422,7 +1420,7 @@ mod tests {
r#"[{"payload":{"foo2":"bar2"},"serial":3,"max_serial":3}]"# r#"[{"payload":{"foo2":"bar2"},"serial":3,"max_serial":3}]"#
); );
t.create_status_update_record( t.create_status_update_record(
&mut instance, &instance,
StatusUpdateItem { StatusUpdateItem {
payload: Value::Bool(true), payload: Value::Bool(true),
info: None, info: None,
@@ -1463,15 +1461,18 @@ mod tests {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_webxdc_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
let now = tools::time();
assert!(t assert!(t
.receive_status_update(ContactId::SELF, instance.id, r#"foo: bar"#) .receive_status_update(ContactId::SELF, &instance, now, true, r#"foo: bar"#)
.await .await
.is_err()); // no json .is_err()); // no json
assert!(t assert!(t
.receive_status_update( .receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updada":[{"payload":{"foo":"bar"}}]}"# r#"{"updada":[{"payload":{"foo":"bar"}}]}"#
) )
.await .await
@@ -1479,7 +1480,9 @@ mod tests {
assert!(t assert!(t
.receive_status_update( .receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updates":[{"foo":"bar"}]}"# r#"{"updates":[{"foo":"bar"}]}"#
) )
.await .await
@@ -1487,7 +1490,9 @@ mod tests {
assert!(t assert!(t
.receive_status_update( .receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updates":{"payload":{"foo":"bar"}}}"# r#"{"updates":{"payload":{"foo":"bar"}}}"#
) )
.await .await
@@ -1495,7 +1500,9 @@ mod tests {
t.receive_status_update( t.receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#"{"updates":[{"payload":{"foo":"bar"}, "someTrash": "definitely TrAsH"}]}"#, r#"{"updates":[{"payload":{"foo":"bar"}, "someTrash": "definitely TrAsH"}]}"#,
) )
.await?; .await?;
@@ -1507,7 +1514,9 @@ mod tests {
t.receive_status_update( t.receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#" {"updates": [ {"payload" :42} , {"payload": 23} ] } "#, r#" {"updates": [ {"payload" :42} , {"payload": 23} ] } "#,
) )
.await?; .await?;
@@ -1521,7 +1530,9 @@ mod tests {
t.receive_status_update( t.receive_status_update(
ContactId::SELF, ContactId::SELF,
instance.id, &instance,
now,
true,
r#" {"updates": [ {"payload" :"ok", "future_item": "test"} ], "from": "future" } "#, r#" {"updates": [ {"payload" :"ok", "future_item": "test"} ], "from": "future" } "#,
) )
.await?; // ignore members that may be added in the future .await?; // ignore members that may be added in the future
@@ -1619,7 +1630,8 @@ mod tests {
assert_eq!(bob_instance.viewtype, Viewtype::Webxdc); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
bob.recv_msg(sent2).await; let bob_received_update = bob.recv_msg_opt(sent2).await;
assert!(bob_received_update.is_none());
expect_status_update_event(&bob, bob_instance.id).await?; expect_status_update_event(&bob, bob_instance.id).await?;
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
@@ -1632,7 +1644,7 @@ mod tests {
// Alice has a second device and also receives messages there // Alice has a second device and also receives messages there
let alice2 = TestContext::new_alice().await; let alice2 = TestContext::new_alice().await;
alice2.recv_msg(sent1).await; alice2.recv_msg(sent1).await;
alice2.recv_msg(sent2).await; alice2.recv_msg_trash(sent2).await;
let alice2_instance = alice2.get_last_msg().await; let alice2_instance = alice2.get_last_msg().await;
let alice2_chat_id = alice2_instance.chat_id; let alice2_chat_id = alice2_instance.chat_id;
assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc); assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc);
@@ -2166,8 +2178,8 @@ sth_for_the = "future""#
// Bob receives the updates // Bob receives the updates
let bob_instance = bob.recv_msg(sent_instance).await; let bob_instance = bob.recv_msg(sent_instance).await;
bob.recv_msg(sent_update1).await; bob.recv_msg_trash(sent_update1).await;
bob.recv_msg(sent_update2).await; bob.recv_msg_trash(sent_update2).await;
let info = Message::load_from_db(&bob, bob_instance.id) let info = Message::load_from_db(&bob, bob_instance.id)
.await? .await?
.get_webxdc_info(&bob) .get_webxdc_info(&bob)
@@ -2177,8 +2189,8 @@ sth_for_the = "future""#
// Alice has a second device and also receives the updates there // Alice has a second device and also receives the updates there
let alice2 = TestContext::new_alice().await; let alice2 = TestContext::new_alice().await;
let alice2_instance = alice2.recv_msg(sent_instance).await; let alice2_instance = alice2.recv_msg(sent_instance).await;
alice2.recv_msg(sent_update1).await; alice2.recv_msg_trash(sent_update1).await;
alice2.recv_msg(sent_update2).await; alice2.recv_msg_trash(sent_update2).await;
let info = Message::load_from_db(&alice2, alice2_instance.id) let info = Message::load_from_db(&alice2, alice2_instance.id)
.await? .await?
.get_webxdc_info(&alice2) .get_webxdc_info(&alice2)
@@ -2219,7 +2231,7 @@ sth_for_the = "future""#
// Bob receives the updates // Bob receives the updates
let bob_instance = bob.recv_msg(sent_instance).await; let bob_instance = bob.recv_msg(sent_instance).await;
bob.recv_msg(sent_update1).await; bob.recv_msg_trash(sent_update1).await;
let info = Message::load_from_db(&bob, bob_instance.id) let info = Message::load_from_db(&bob, bob_instance.id)
.await? .await?
.get_webxdc_info(&bob) .get_webxdc_info(&bob)
@@ -2271,7 +2283,7 @@ sth_for_the = "future""#
// Bob receives all messages // Bob receives all messages
let bob_instance = bob.recv_msg(sent1).await; let bob_instance = bob.recv_msg(sent1).await;
let bob_chat_id = bob_instance.chat_id; let bob_chat_id = bob_instance.chat_id;
bob.recv_msg(sent2).await; bob.recv_msg_trash(sent2).await;
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
let info_msg = bob.get_last_msg().await; let info_msg = bob.get_last_msg().await;
assert!(info_msg.is_info()); assert!(info_msg.is_info());
@@ -2290,7 +2302,7 @@ sth_for_the = "future""#
let alice2 = TestContext::new_alice().await; let alice2 = TestContext::new_alice().await;
let alice2_instance = alice2.recv_msg(sent1).await; let alice2_instance = alice2.recv_msg(sent1).await;
let alice2_chat_id = alice2_instance.chat_id; let alice2_chat_id = alice2_instance.chat_id;
alice2.recv_msg(sent2).await; alice2.recv_msg_trash(sent2).await;
assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 2); assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 2);
let info_msg = alice2.get_last_msg().await; let info_msg = alice2.get_last_msg().await;
assert!(info_msg.is_info()); assert!(info_msg.is_info());
@@ -2340,9 +2352,9 @@ sth_for_the = "future""#
// When Bob receives the messages, they should be cleaned up as well // When Bob receives the messages, they should be cleaned up as well
let bob_instance = bob.recv_msg(sent1).await; let bob_instance = bob.recv_msg(sent1).await;
let bob_chat_id = bob_instance.chat_id; let bob_chat_id = bob_instance.chat_id;
bob.recv_msg(sent2).await; bob.recv_msg_trash(sent2).await;
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
bob.recv_msg(sent3).await; bob.recv_msg_trash(sent3).await;
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 2);
let info_msg = bob.get_last_msg().await; let info_msg = bob.get_last_msg().await;
assert_eq!(info_msg.get_text(), "i2"); assert_eq!(info_msg.get_text(), "i2");
@@ -2400,7 +2412,7 @@ sth_for_the = "future""#
// Bob receives instance+update // Bob receives instance+update
let bob_instance = bob.recv_msg(sent1).await; let bob_instance = bob.recv_msg(sent1).await;
bob.recv_msg(sent2).await; bob.recv_msg_trash(sent2).await;
assert!(bob_instance.get_showpadlock()); assert!(bob_instance.get_showpadlock());
// Bob adds Claire with unknown key, update to Alice+Claire cannot be encrypted // Bob adds Claire with unknown key, update to Alice+Claire cannot be encrypted
@@ -2530,7 +2542,7 @@ sth_for_the = "future""#
.await?; .await?;
bob.flush_status_updates().await?; bob.flush_status_updates().await?;
let msg = bob.pop_sent_msg().await; let msg = bob.pop_sent_msg().await;
alice.recv_msg(&msg).await; alice.recv_msg_trash(&msg).await;
alice alice
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0)) .get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
.await .await
@@ -2648,7 +2660,8 @@ sth_for_the = "future""#
) )
.await?; .await?;
alice.flush_status_updates().await?; alice.flush_status_updates().await?;
bob.recv_msg(&alice.pop_sent_msg().await).await; let received_update = bob.recv_msg_opt(&alice.pop_sent_msg().await).await;
assert!(received_update.is_none());
assert_eq!( assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
@@ -2683,7 +2696,7 @@ sth_for_the = "future""#
.set(Param::Arg, r#"{"updates":[{"payload":{"foo":"bar"}}]}"#); .set(Param::Arg, r#"{"updates":[{"payload":{"foo":"bar"}}]}"#);
update.set_quote(alice, Some(&alice_instance)).await?; update.set_quote(alice, Some(&alice_instance)).await?;
let sent_msg = alice.send_msg(alice_chat.id, &mut update).await; let sent_msg = alice.send_msg(alice_chat.id, &mut update).await;
bob.recv_msg(&sent_msg).await; bob.recv_msg_trash(&sent_msg).await;
assert_eq!( assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
.await?, .await?,