mirror of
https://github.com/chatmail/core.git
synced 2026-05-03 05:16:28 +03:00
Merge remote-tracking branch 'origin/main' into hoc/channels-encryption-only-qrcodes
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
use deltachat::{Event as CoreEvent, EventType as CoreEventType};
|
use deltachat::{Event as CoreEvent, EventType as CoreEventType};
|
||||||
|
use num_traits::ToPrimitive;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use typescript_type_def::TypeDef;
|
use typescript_type_def::TypeDef;
|
||||||
|
|
||||||
@@ -303,6 +304,11 @@ pub enum EventType {
|
|||||||
/// ID of the contact that wants to join.
|
/// ID of the contact that wants to join.
|
||||||
contact_id: u32,
|
contact_id: u32,
|
||||||
|
|
||||||
|
/// The type of the joined chat.
|
||||||
|
/// This can take the same values
|
||||||
|
/// as `BasicChat.chatType` ([`crate::api::types::chat::BasicChat::chat_type`]).
|
||||||
|
chat_type: u32,
|
||||||
|
|
||||||
/// Progress as:
|
/// Progress as:
|
||||||
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||||
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".
|
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".
|
||||||
@@ -551,9 +557,11 @@ impl From<CoreEventType> for EventType {
|
|||||||
},
|
},
|
||||||
CoreEventType::SecurejoinInviterProgress {
|
CoreEventType::SecurejoinInviterProgress {
|
||||||
contact_id,
|
contact_id,
|
||||||
|
chat_type,
|
||||||
progress,
|
progress,
|
||||||
} => SecurejoinInviterProgress {
|
} => SecurejoinInviterProgress {
|
||||||
contact_id: contact_id.to_u32(),
|
contact_id: contact_id.to_u32(),
|
||||||
|
chat_type: chat_type.to_u32().unwrap_or(0),
|
||||||
progress,
|
progress,
|
||||||
},
|
},
|
||||||
CoreEventType::SecurejoinJoinerProgress {
|
CoreEventType::SecurejoinJoinerProgress {
|
||||||
|
|||||||
13
src/chat.rs
13
src/chat.rs
@@ -1983,7 +1983,7 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds missing values to the msg object,
|
/// Adds missing values to the msg object,
|
||||||
/// writes the record to the database and returns its msg_id.
|
/// writes the record to the database.
|
||||||
///
|
///
|
||||||
/// If `update_msg_id` is set, that record is reused;
|
/// If `update_msg_id` is set, that record is reused;
|
||||||
/// if `update_msg_id` is None, a new record is created.
|
/// if `update_msg_id` is None, a new record is created.
|
||||||
@@ -1992,7 +1992,7 @@ impl Chat {
|
|||||||
context: &Context,
|
context: &Context,
|
||||||
msg: &mut Message,
|
msg: &mut Message,
|
||||||
update_msg_id: Option<MsgId>,
|
update_msg_id: Option<MsgId>,
|
||||||
) -> Result<MsgId> {
|
) -> Result<()> {
|
||||||
let mut to_id = 0;
|
let mut to_id = 0;
|
||||||
let mut location_id = 0;
|
let mut location_id = 0;
|
||||||
|
|
||||||
@@ -2270,7 +2270,7 @@ impl Chat {
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
context.scheduler.interrupt_ephemeral_task().await;
|
context.scheduler.interrupt_ephemeral_task().await;
|
||||||
Ok(msg.id)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a `SyncAction` synchronising chat contacts to other devices.
|
/// Sends a `SyncAction` synchronising chat contacts to other devices.
|
||||||
@@ -3006,8 +3006,7 @@ async fn prepare_send_msg(
|
|||||||
if !msg.hidden {
|
if !msg.hidden {
|
||||||
chat_id.unarchive_if_not_muted(context, msg.state).await?;
|
chat_id.unarchive_if_not_muted(context, msg.state).await?;
|
||||||
}
|
}
|
||||||
msg.id = chat.prepare_msg_raw(context, msg, update_msg_id).await?;
|
chat.prepare_msg_raw(context, msg, update_msg_id).await?;
|
||||||
msg.chat_id = chat_id;
|
|
||||||
|
|
||||||
let row_ids = create_send_msg_jobs(context, msg)
|
let row_ids = create_send_msg_jobs(context, msg)
|
||||||
.await
|
.await
|
||||||
@@ -4561,13 +4560,13 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
|||||||
msg.state = MessageState::OutPending;
|
msg.state = MessageState::OutPending;
|
||||||
msg.rfc724_mid = create_outgoing_rfc724_mid();
|
msg.rfc724_mid = create_outgoing_rfc724_mid();
|
||||||
msg.timestamp_sort = curr_timestamp;
|
msg.timestamp_sort = curr_timestamp;
|
||||||
let new_msg_id = chat.prepare_msg_raw(context, &mut msg, None).await?;
|
chat.prepare_msg_raw(context, &mut msg, None).await?;
|
||||||
|
|
||||||
curr_timestamp += 1;
|
curr_timestamp += 1;
|
||||||
if !create_send_msg_jobs(context, &mut msg).await?.is_empty() {
|
if !create_send_msg_jobs(context, &mut msg).await?.is_empty() {
|
||||||
context.scheduler.interrupt_smtp().await;
|
context.scheduler.interrupt_smtp().await;
|
||||||
}
|
}
|
||||||
created_msgs.push(new_msg_id);
|
created_msgs.push(msg.id);
|
||||||
}
|
}
|
||||||
for msg_id in created_msgs {
|
for msg_id in created_msgs {
|
||||||
context.emit_msgs_changed(chat_id, msg_id);
|
context.emit_msgs_changed(chat_id, msg_id);
|
||||||
|
|||||||
@@ -3459,6 +3459,30 @@ async fn test_chat_get_encryption_info() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_out_failed_on_all_keys_missing() -> Result<()> {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let alice = &tcm.alice().await;
|
||||||
|
let bob = &tcm.bob().await;
|
||||||
|
let fiona = &tcm.fiona().await;
|
||||||
|
|
||||||
|
let bob_chat_id = bob
|
||||||
|
.create_group_with_members(ProtectionStatus::Unprotected, "", &[alice, fiona])
|
||||||
|
.await;
|
||||||
|
bob.send_text(bob_chat_id, "Gossiping Fiona's key").await;
|
||||||
|
alice
|
||||||
|
.recv_msg(&bob.send_text(bob_chat_id, "No key gossip").await)
|
||||||
|
.await;
|
||||||
|
SystemTime::shift(Duration::from_secs(60));
|
||||||
|
remove_contact_from_chat(bob, bob_chat_id, ContactId::SELF).await?;
|
||||||
|
let alice_chat_id = alice.recv_msg(&bob.pop_sent_msg().await).await.chat_id;
|
||||||
|
alice_chat_id.accept(alice).await?;
|
||||||
|
let mut msg = Message::new_text("Hi".to_string());
|
||||||
|
send_msg(alice, alice_chat_id, &mut msg).await.ok();
|
||||||
|
assert_eq!(msg.id.get_state(alice).await?, MessageState::OutFailed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_get_chat_media() -> Result<()> {
|
async fn test_get_chat_media() -> Result<()> {
|
||||||
let t = TestContext::new_alice().await;
|
let t = TestContext::new_alice().await;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use crate::chat::ChatId;
|
use crate::chat::ChatId;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::constants::Chattype;
|
||||||
use crate::contact::ContactId;
|
use crate::contact::ContactId;
|
||||||
use crate::ephemeral::Timer as EphemeralTimer;
|
use crate::ephemeral::Timer as EphemeralTimer;
|
||||||
use crate::message::MsgId;
|
use crate::message::MsgId;
|
||||||
@@ -272,6 +273,9 @@ pub enum EventType {
|
|||||||
/// ID of the contact that wants to join.
|
/// ID of the contact that wants to join.
|
||||||
contact_id: ContactId,
|
contact_id: ContactId,
|
||||||
|
|
||||||
|
/// The type of the joined chat.
|
||||||
|
chat_type: Chattype,
|
||||||
|
|
||||||
/// Progress as:
|
/// Progress as:
|
||||||
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||||
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".
|
/// 600=vg-/vc-request-with-auth received and verified, typically shown as "bob@addr verified".
|
||||||
|
|||||||
@@ -32,16 +32,30 @@ use qrinvite::QrInvite;
|
|||||||
|
|
||||||
use crate::token::Namespace;
|
use crate::token::Namespace;
|
||||||
|
|
||||||
fn inviter_progress(context: &Context, contact_id: ContactId, progress: usize) {
|
fn inviter_progress(
|
||||||
|
context: &Context,
|
||||||
|
contact_id: ContactId,
|
||||||
|
step: &str,
|
||||||
|
progress: usize,
|
||||||
|
) -> Result<()> {
|
||||||
logged_debug_assert!(
|
logged_debug_assert!(
|
||||||
context,
|
context,
|
||||||
progress <= 1000,
|
progress <= 1000,
|
||||||
"inviter_progress: contact {contact_id}, progress={progress}, but value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success."
|
"inviter_progress: contact {contact_id}, progress={progress}, but value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success."
|
||||||
);
|
);
|
||||||
|
let chat_type = match step.get(..3) {
|
||||||
|
Some("vc-") => Chattype::Single,
|
||||||
|
Some("vg-") => Chattype::Group,
|
||||||
|
Some("vb-") => Chattype::OutBroadcast,
|
||||||
|
_ => bail!("Unknown securejoin step {step}"),
|
||||||
|
};
|
||||||
context.emit_event(EventType::SecurejoinInviterProgress {
|
context.emit_event(EventType::SecurejoinInviterProgress {
|
||||||
contact_id,
|
contact_id,
|
||||||
|
chat_type,
|
||||||
progress,
|
progress,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a Secure Join QR code.
|
/// Generates a Secure Join QR code.
|
||||||
@@ -346,7 +360,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
return Ok(HandshakeMessage::Ignore);
|
return Ok(HandshakeMessage::Ignore);
|
||||||
}
|
}
|
||||||
|
|
||||||
inviter_progress(context, contact_id, 300);
|
inviter_progress(context, contact_id, step, 300)?;
|
||||||
|
|
||||||
let from_addr = ContactAddress::new(&mime_message.from.addr)?;
|
let from_addr = ContactAddress::new(&mime_message.from.addr)?;
|
||||||
let autocrypt_fingerprint = mime_message.autocrypt_fingerprint.as_deref().unwrap_or("");
|
let autocrypt_fingerprint = mime_message.autocrypt_fingerprint.as_deref().unwrap_or("");
|
||||||
@@ -442,7 +456,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
ChatId::create_for_contact(context, contact_id).await?;
|
ChatId::create_for_contact(context, contact_id).await?;
|
||||||
}
|
}
|
||||||
context.emit_event(EventType::ContactsChanged(Some(contact_id)));
|
context.emit_event(EventType::ContactsChanged(Some(contact_id)));
|
||||||
inviter_progress(context, contact_id, 600);
|
inviter_progress(context, contact_id, step, 600)?;
|
||||||
if let Some(group_chat_id) = group_chat_id {
|
if let Some(group_chat_id) = group_chat_id {
|
||||||
// Join group.
|
// Join group.
|
||||||
secure_connection_established(
|
secure_connection_established(
|
||||||
@@ -455,8 +469,8 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
|
|
||||||
chat::add_contact_to_chat_ex(context, Nosync, group_chat_id, contact_id, true)
|
chat::add_contact_to_chat_ex(context, Nosync, group_chat_id, contact_id, true)
|
||||||
.await?;
|
.await?;
|
||||||
inviter_progress(context, contact_id, 800);
|
inviter_progress(context, contact_id, step, 800)?;
|
||||||
inviter_progress(context, contact_id, 1000);
|
inviter_progress(context, contact_id, step, 1000)?;
|
||||||
if step == "vb-request-with-auth" {
|
if step == "vb-request-with-auth" {
|
||||||
// For broadcasts, we don't want to delete the message,
|
// For broadcasts, we don't want to delete the message,
|
||||||
// because the other device should also internally add the member
|
// because the other device should also internally add the member
|
||||||
@@ -480,7 +494,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
.await
|
.await
|
||||||
.context("failed sending vc-contact-confirm message")?;
|
.context("failed sending vc-contact-confirm message")?;
|
||||||
|
|
||||||
inviter_progress(context, contact_id, 1000);
|
inviter_progress(context, contact_id, step, 1000)?;
|
||||||
Ok(HandshakeMessage::Ignore) // "Done" would delete the message and break multi-device (the key from Autocrypt-header is needed)
|
Ok(HandshakeMessage::Ignore) // "Done" would delete the message and break multi-device (the key from Autocrypt-header is needed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,11 +623,11 @@ pub(crate) async fn observe_securejoin_on_other_device(
|
|||||||
ChatId::set_protection_for_contact(context, contact_id, mime_message.timestamp_sent).await?;
|
ChatId::set_protection_for_contact(context, contact_id, mime_message.timestamp_sent).await?;
|
||||||
|
|
||||||
if step == "vg-member-added" {
|
if step == "vg-member-added" {
|
||||||
inviter_progress(context, contact_id, 800);
|
inviter_progress(context, contact_id, step, 800)?;
|
||||||
}
|
}
|
||||||
// TODO superflous vb-member-added (we're early-returning above):
|
// TODO superflous vb-member-added (we're early-returning above):
|
||||||
if step == "vg-member-added" || step == "vb-member-added" || step == "vc-contact-confirm" {
|
if step == "vg-member-added" || step == "vb-member-added" || step == "vc-contact-confirm" {
|
||||||
inviter_progress(context, contact_id, 1000);
|
inviter_progress(context, contact_id, step, 1000)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if step == "vg-request-with-auth" || step == "vc-request-with-auth" {
|
if step == "vg-request-with-auth" || step == "vc-request-with-auth" {
|
||||||
|
|||||||
@@ -366,6 +366,29 @@ async fn test_setup_contact_bob_knows_alice() -> Result<()> {
|
|||||||
alice.recv_msg_trash(&sent).await;
|
alice.recv_msg_trash(&sent).await;
|
||||||
assert_eq!(contact_bob.is_verified(alice).await?, true);
|
assert_eq!(contact_bob.is_verified(alice).await?, true);
|
||||||
|
|
||||||
|
// Check Alice signalled success via the SecurejoinInviterProgress event.
|
||||||
|
let event = alice
|
||||||
|
.evtracker
|
||||||
|
.get_matching(|evt| {
|
||||||
|
matches!(
|
||||||
|
evt,
|
||||||
|
EventType::SecurejoinInviterProgress { progress: 1000, .. }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
match event {
|
||||||
|
EventType::SecurejoinInviterProgress {
|
||||||
|
contact_id,
|
||||||
|
chat_type,
|
||||||
|
progress,
|
||||||
|
} => {
|
||||||
|
assert_eq!(contact_id, contact_bob.id);
|
||||||
|
assert_eq!(chat_type, Chattype::Single);
|
||||||
|
assert_eq!(progress, 1000);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
assert!(msg.was_encrypted());
|
assert!(msg.was_encrypted());
|
||||||
@@ -516,6 +539,29 @@ async fn test_secure_join() -> Result<()> {
|
|||||||
alice.recv_msg_trash(&sent).await;
|
alice.recv_msg_trash(&sent).await;
|
||||||
assert_eq!(contact_bob.is_verified(&alice).await?, true);
|
assert_eq!(contact_bob.is_verified(&alice).await?, true);
|
||||||
|
|
||||||
|
// Check Alice signalled success via the SecurejoinInviterProgress event.
|
||||||
|
let event = alice
|
||||||
|
.evtracker
|
||||||
|
.get_matching(|evt| {
|
||||||
|
matches!(
|
||||||
|
evt,
|
||||||
|
EventType::SecurejoinInviterProgress { progress: 1000, .. }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
match event {
|
||||||
|
EventType::SecurejoinInviterProgress {
|
||||||
|
contact_id,
|
||||||
|
chat_type,
|
||||||
|
progress,
|
||||||
|
} => {
|
||||||
|
assert_eq!(contact_id, contact_bob.id);
|
||||||
|
assert_eq!(chat_type, Chattype::Group);
|
||||||
|
assert_eq!(progress, 1000);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
assert!(msg.was_encrypted());
|
assert!(msg.was_encrypted());
|
||||||
|
|||||||
Reference in New Issue
Block a user