Skip to content

Commit

Permalink
fix: Fix info-message orderings of verified 1:1 chats
Browse files Browse the repository at this point in the history
  • Loading branch information
Hocuri committed Jul 14, 2023
1 parent 1e28ea9 commit 76565e6
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 11 deletions.
33 changes: 22 additions & 11 deletions src/receive_imf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,9 @@ async fn add_parts(
new_protection = ProtectionStatus::ProtectionBroken;
}

// The message itself will be sorted under the device message since the device
// message is `MessageState::InNoticed`, which means that all following
// messages are sorted under it.
let sort_timestamp =
calc_sort_timestamp(context, sent_timestamp, chat_id, true).await?;
chat_id
Expand Down Expand Up @@ -947,7 +950,7 @@ async fn add_parts(
};

let in_fresh = state == MessageState::InFresh;
let sort_timestamp = calc_sort_timestamp(context, sent_timestamp, chat_id, in_fresh).await?;
let sort_timestamp = calc_sort_timestamp(context, sent_timestamp, chat_id, false).await?;

// Apply ephemeral timer changes to the chat.
//
Expand Down Expand Up @@ -1369,25 +1372,33 @@ async fn calc_sort_timestamp(
context: &Context,
message_timestamp: i64,
chat_id: ChatId,
is_fresh_msg: bool,
always_sort_to_bottom: bool,
) -> Result<i64> {
let mut sort_timestamp = message_timestamp;

// get newest non fresh message for this chat
// update sort_timestamp if less than that
if is_fresh_msg {
let last_msg_time: Option<i64> = context
let last_msg_time: Option<i64> = if always_sort_to_bottom {
// get newest message for this chat
context
.sql
.query_get_value(
"SELECT MAX(timestamp) FROM msgs WHERE chat_id=?",
(chat_id,),
)
.await?
} else {
// get newest non fresh message for this chat
context
.sql
.query_get_value(
"SELECT MAX(timestamp) FROM msgs WHERE chat_id=? AND state>?",
(chat_id, MessageState::InFresh),
)
.await?;
.await?
};

if let Some(last_msg_time) = last_msg_time {
if last_msg_time > sort_timestamp {
sort_timestamp = last_msg_time;
}
if let Some(last_msg_time) = last_msg_time {
if last_msg_time > sort_timestamp {
sort_timestamp = last_msg_time;
}
}

Expand Down
129 changes: 129 additions & 0 deletions src/tests/verified_chats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,135 @@ async fn test_verified_oneonone_chat_enable_disable() -> Result<()> {
Ok(())
}

/// Messages with old timestamps are difficult for verified chats:
/// - They must not be sorted over a protection-changed info message.
/// That's what `test_old_message_2` tests
/// - If they change the protection, then they must not be sorted over existing other messages,
/// because then the protection-changed info message would also be above these existing messages.
/// That's what `test_old_message_3` tests.
///
/// `test_old_message_1` tests the case where both the old and the new message
/// change verification
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_old_message_1() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
enable_verified_oneonone_chats(&[&alice, &bob]).await;

mark_as_verified(&alice, &bob).await;

let chat = alice.create_chat(&bob).await; // This creates a protection-changed info message
assert!(chat.is_protected());

// This creates protection-changed info message #2;
// even though the date is old, info message and email must be sorted below the original info message.
receive_imf(
&alice,
b"From: Bob <bob@example.net>\n\
To: alice@example.org\n\
Message-ID: <1234-2-3@example.org>\n\
Date: Sat, 07 Dec 2019 19:00:27 +0000\n\
\n\
Message from Thunderbird\n",
true,
)
.await?;

alice.golden_test_chat(chat.id, "test_old_message_1").await;

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_old_message_2() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
enable_verified_oneonone_chats(&[&alice, &bob]).await;

mark_as_verified(&alice, &bob).await;

// This creates protection-changed info message #1:
let chat = alice.create_chat(&bob).await;
assert!(chat.is_protected());
let protection_msg = alice.get_last_msg().await;
assert_eq!(
protection_msg.param.get_cmd(),
SystemMessage::ChatProtectionEnabled
);

// This creates protection-changed info message #2.
let first_email = receive_imf(
&alice,
b"From: Bob <bob@example.net>\n\
To: alice@example.org\n\
Message-ID: <1234-2-3@example.org>\n\
Date: Sun, 08 Dec 2019 19:00:27 +0000\n\
\n\
Somewhat old message\n",
false,
)
.await?
.unwrap();

// Both messages will get the same timestamp as the protection-changed
// message, so this one will be sorted under the previous one
// even though it has an older timestamp.
let second_email = receive_imf(
&alice,
b"From: Bob <bob@example.net>\n\
To: alice@example.org\n\
Message-ID: <2319-2-3@example.org>\n\
Date: Sat, 07 Dec 2019 19:00:27 +0000\n\
\n\
Even older message, that must NOT be shown before the info message\n",
true,
)
.await?
.unwrap();

assert_eq!(first_email.sort_timestamp, second_email.sort_timestamp);
assert_eq!(first_email.sort_timestamp, protection_msg.timestamp_sort);

alice.golden_test_chat(chat.id, "test_old_message_2").await;

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_old_message_3() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
enable_verified_oneonone_chats(&[&alice, &bob]).await;

mark_as_verified(&alice, &bob).await;
mark_as_verified(&bob, &alice).await;

tcm.send_recv_accept(&bob, &alice, "Heyho from my verified device!")
.await;

// This unverified message must not be sorted over the message sent in the previous line:
receive_imf(
&alice,
b"From: Bob <bob@example.net>\n\
To: alice@example.org\n\
Message-ID: <1234-2-3@example.org>\n\
Date: Sat, 07 Dec 2019 19:00:27 +0000\n\
\n\
Old, unverified message\n",
true,
)
.await?;

alice
.golden_test_chat(alice.get_chat(&bob).await.unwrap().id, "test_old_message_3")
.await;

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mdn_doesnt_disable_verification() -> Result<()> {
let mut tcm = TestContextManager::new();
Expand Down
6 changes: 6 additions & 0 deletions test-data/golden/test_old_message_1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Single#Chat#10: Bob [bob@example.net]
--------------------------------------------------------------------------------
Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
Msg#11: info (Contact#Contact#Info): Bob sent a message from another device. [NOTICED][INFO 🛡️❌]
Msg#12: (Contact#Contact#10): Message from Thunderbird [SEEN]
--------------------------------------------------------------------------------
7 changes: 7 additions & 0 deletions test-data/golden/test_old_message_2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Single#Chat#10: Bob [bob@example.net]
--------------------------------------------------------------------------------
Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
Msg#11: info (Contact#Contact#Info): Bob sent a message from another device. [NOTICED][INFO 🛡️❌]
Msg#12: (Contact#Contact#10): Somewhat old message [FRESH]
Msg#13: (Contact#Contact#10): Even older message, that must NOT be shown before the info message [SEEN]
--------------------------------------------------------------------------------
7 changes: 7 additions & 0 deletions test-data/golden/test_old_message_3
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Single#Chat#10: Bob [bob@example.net]
--------------------------------------------------------------------------------
Msg#10: info (Contact#Contact#Info): Messages are guaranteed to be end-to-end encrypted from now on. [NOTICED][INFO 🛡️]
Msg#11🔒: (Contact#Contact#10): Heyho from my verified device! [FRESH]
Msg#12: info (Contact#Contact#Info): Bob sent a message from another device. [NOTICED][INFO 🛡️❌]
Msg#13: (Contact#Contact#10): Old, unverified message [SEEN]
--------------------------------------------------------------------------------

0 comments on commit 76565e6

Please sign in to comment.