diff --git a/src/calls/calls_tests.rs b/src/calls/calls_tests.rs index 34fe7e720..9ad40efeb 100644 --- a/src/calls/calls_tests.rs +++ b/src/calls/calls_tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::chat::forward_msgs; use crate::config::Config; use crate::constants::DC_CHAT_ID_TRASH; -use crate::receive_imf::receive_imf; +use crate::receive_imf::{receive_imf, receive_imf_from_inbox}; use crate::test_utils::{TestContext, TestContextManager}; struct CallSetup { @@ -604,3 +604,65 @@ async fn test_end_text_call() -> Result<()> { Ok(()) } + +/// Tests that partially downloaded "call ended" +/// messages are not processed. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_no_partial_calls() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + + let seen = false; + + // The messages in the test + // have no `Date` on purpose, + // so they are treated as new. + let received_call = receive_imf( + alice, + b"From: bob@example.net\n\ + To: alice@example.org\n\ + Message-ID: \n\ + Chat-Version: 1.0\n\ + Chat-Content: call\n\ + Chat-Webrtc-Room: YWFhYWFhYWFhCg==\n\ + \n\ + Hello, this is a call\n", + seen, + ) + .await? + .unwrap(); + assert_eq!(received_call.msg_ids.len(), 1); + let call_msg = Message::load_from_db(alice, received_call.msg_ids[0]) + .await + .unwrap(); + assert_eq!(call_msg.viewtype, Viewtype::Call); + assert_eq!(call_state(alice, call_msg.id).await?, CallState::Alerting); + + let imf_raw = b"From: bob@example.net\n\ + To: alice@example.org\n\ + Message-ID: \n\ + In-Reply-To: \n\ + Chat-Version: 1.0\n\ + Chat-Content: call-ended\n\ + \n\ + Call ended\n"; + receive_imf_from_inbox( + alice, + "second@example.net", + imf_raw, + seen, + Some(imf_raw.len().try_into().unwrap()), + ) + .await?; + + // The call is still not ended. + assert_eq!(call_state(alice, call_msg.id).await?, CallState::Alerting); + + // Fully downloading the message ends the call. + receive_imf_from_inbox(alice, "second@example.net", imf_raw, seen, None) + .await + .context("Failed to fully download end call message")?; + assert_eq!(call_state(alice, call_msg.id).await?, CallState::Missed); + + Ok(()) +} diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 51de43da9..47903206c 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -999,7 +999,7 @@ pub(crate) async fn receive_imf_inner( } } - if mime_parser.is_call() { + if is_partial_download.is_none() && mime_parser.is_call() { context .handle_call_msg(insert_msg_id, &mime_parser, from_id) .await?; @@ -1157,8 +1157,9 @@ async fn decide_chat_assignment( { info!(context, "Chat edit/delete/iroh/sync message (TRASH)."); true - } else if mime_parser.is_system_message == SystemMessage::CallAccepted - || mime_parser.is_system_message == SystemMessage::CallEnded + } else if is_partial_download.is_none() + && (mime_parser.is_system_message == SystemMessage::CallAccepted + || mime_parser.is_system_message == SystemMessage::CallEnded) { info!(context, "Call state changed (TRASH)."); true @@ -1986,8 +1987,9 @@ async fn add_parts( handle_edit_delete(context, mime_parser, from_id).await?; - if mime_parser.is_system_message == SystemMessage::CallAccepted - || mime_parser.is_system_message == SystemMessage::CallEnded + if is_partial_download.is_none() + && (mime_parser.is_system_message == SystemMessage::CallAccepted + || mime_parser.is_system_message == SystemMessage::CallEnded) { if let Some(field) = mime_parser.get_header(HeaderDef::InReplyTo) { if let Some(call) =