Repository: chatmail/core (Rust)

Issue:
Forwarding a call message triggers a call
In core 2.16 forwarding a call message is possible and results in call events on the side receiving forwarded call message.

We don't want message forwarding API to fail with an error because we currently don't do this for any type of message, so bots forwarding messages will start fail, users will get untranslated error etc.

So forwarding of call message should be allowed, but should result in something reasonable like only forwarding the text like "Outgoing call". Also forwarding to Saved messages should result in bookmarking the message. In no case call events should be produced.


Discussion:
It should also be ensured that the WebRTC SDP, don't get passed, because they contain sensitive info, such as the public IP address of a peer (at least as of now). 



Patch:
diff --git a/src/calls.rs b/src/calls.rs
index 95b2f7d7c2..4c2ece1894 100644
--- a/src/calls.rs
+++ b/src/calls.rs
@@ -183,7 +183,11 @@ impl Context {
         place_call_info: String,
     ) -> Result<MsgId> {
         let chat = Chat::load_from_db(self, chat_id).await?;
-        ensure!(chat.typ == Chattype::Single && !chat.is_self_talk());
+        ensure!(
+            chat.typ == Chattype::Single,
+            "Can only place calls in 1:1 chats"
+        );
+        ensure!(!chat.is_self_talk(), "Cannot call self");
 
         let mut call = Message {
             viewtype: Viewtype::Call,
diff --git a/src/chat.rs b/src/chat.rs
index 805f040a8d..7cbabf121f 100644
--- a/src/chat.rs
+++ b/src/chat.rs
@@ -4450,6 +4450,10 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
                 .set_int(Param::Forwarded, src_msg_id.to_u32() as i32);
         }
 
+        if msg.get_viewtype() == Viewtype::Call {
+            msg.viewtype = Viewtype::Text;
+        }
+
         msg.param.remove(Param::GuaranteeE2ee);
         msg.param.remove(Param::ForcePlaintext);
         msg.param.remove(Param::Cmd);
@@ -4459,6 +4463,8 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
         msg.param.remove(Param::WebxdcSummary);
         msg.param.remove(Param::WebxdcSummaryTimestamp);
         msg.param.remove(Param::IsEdited);
+        msg.param.remove(Param::WebrtcRoom);
+        msg.param.remove(Param::WebrtcAccepted);
         msg.in_reply_to = None;
 
         // do not leak data as group names; a default subject is generated by mimefactory


Test patch:
diff --git a/src/calls/calls_tests.rs b/src/calls/calls_tests.rs
index 5763236c22..bbd382734d 100644
--- a/src/calls/calls_tests.rs
+++ b/src/calls/calls_tests.rs
@@ -1,4 +1,5 @@
 use super::*;
+use crate::chat::forward_msgs;
 use crate::config::Config;
 use crate::test_utils::{TestContext, TestContextManager};
 
@@ -498,3 +499,33 @@ async fn test_sdp_has_video() {
     assert_eq!(sdp_has_video(PLACE_INFO).unwrap(), false);
     assert_eq!(sdp_has_video(PLACE_INFO_VIDEO).unwrap(), true);
 }
+
+/// Tests that calls are forwarded as text messages.
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn test_forward_call() -> Result<()> {
+    let mut tcm = TestContextManager::new();
+    let alice = &tcm.alice().await;
+    let bob = &tcm.bob().await;
+    let charlie = &tcm.charlie().await;
+
+    let alice_bob_chat = alice.create_chat(bob).await;
+    let alice_msg_id = alice
+        .place_outgoing_call(alice_bob_chat.id, PLACE_INFO.to_string())
+        .await
+        .context("Failed to place a call")?;
+    let alice_call = Message::load_from_db(alice, alice_msg_id).await?;
+
+    let _alice_sent_call = alice.pop_sent_msg().await;
+    assert_eq!(alice_call.viewtype, Viewtype::Call);
+
+    let alice_charlie_chat = alice.create_chat(charlie).await;
+    forward_msgs(alice, &[alice_call.id], alice_charlie_chat.id).await?;
+    let alice_forwarded_call = alice.pop_sent_msg().await;
+    let alice_forwarded_call_msg = alice_forwarded_call.load_from_db().await;
+    assert_eq!(alice_forwarded_call_msg.viewtype, Viewtype::Text);
+
+    let charlie_forwarded_call = charlie.recv_msg(&alice_forwarded_call).await;
+    assert_eq!(charlie_forwarded_call.viewtype, Viewtype::Text);
+
+    Ok(())
+}