Skip to content

Commit fb034c4

Browse files
committed
fix broadcast list synchronization
1 parent 02626fc commit fb034c4

File tree

8 files changed

+115
-46
lines changed

8 files changed

+115
-46
lines changed

src/chat.rs

+91-13
Original file line numberDiff line numberDiff line change
@@ -2289,19 +2289,40 @@ impl Chat {
22892289

22902290
/// Sends a `SyncAction` synchronising chat contacts to other devices.
22912291
pub(crate) async fn sync_contacts(&self, context: &Context) -> Result<()> {
2292-
let addrs = context
2293-
.sql
2294-
.query_map(
2295-
"SELECT c.addr \
2296-
FROM contacts c INNER JOIN chats_contacts cc \
2297-
ON c.id=cc.contact_id \
2298-
WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp",
2299-
(self.id,),
2300-
|row| row.get::<_, String>(0),
2301-
|addrs| addrs.collect::<Result<Vec<_>, _>>().map_err(Into::into),
2302-
)
2303-
.await?;
2304-
self.sync(context, SyncAction::SetContacts(addrs)).await
2292+
if self.is_encrypted(context).await? {
2293+
let fingerprint_addrs = context
2294+
.sql
2295+
.query_map(
2296+
"SELECT c.fingerprint, c.addr
2297+
FROM contacts c INNER JOIN chats_contacts cc
2298+
ON c.id=cc.contact_id
2299+
WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp",
2300+
(self.id,),
2301+
|row| {
2302+
let fingerprint = row.get(0)?;
2303+
let addr = row.get(1)?;
2304+
Ok((fingerprint, addr))
2305+
},
2306+
|addrs| addrs.collect::<Result<Vec<_>, _>>().map_err(Into::into),
2307+
)
2308+
.await?;
2309+
self.sync(context, SyncAction::SetPgpContacts(fingerprint_addrs)).await?;
2310+
} else {
2311+
let addrs = context
2312+
.sql
2313+
.query_map(
2314+
"SELECT c.addr \
2315+
FROM contacts c INNER JOIN chats_contacts cc \
2316+
ON c.id=cc.contact_id \
2317+
WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp",
2318+
(self.id,),
2319+
|row| row.get::<_, String>(0),
2320+
|addrs| addrs.collect::<Result<Vec<_>, _>>().map_err(Into::into),
2321+
)
2322+
.await?;
2323+
self.sync(context, SyncAction::SetContacts(addrs)).await?;
2324+
}
2325+
Ok(())
23052326
}
23062327

23072328
/// Returns chat id for the purpose of synchronisation across devices.
@@ -4811,6 +4832,10 @@ pub(crate) async fn update_msg_text_and_timestamp(
48114832
/// Set chat contacts by their addresses creating the corresponding contacts if necessary.
48124833
async fn set_contacts_by_addrs(context: &Context, id: ChatId, addrs: &[String]) -> Result<()> {
48134834
let chat = Chat::load_from_db(context, id).await?;
4835+
ensure!(
4836+
!chat.is_encrypted(context).await?,
4837+
"Cannot add email-contacts to encrypted chat {id}"
4838+
);
48144839
ensure!(
48154840
chat.typ == Chattype::Broadcast,
48164841
"{id} is not a broadcast list",
@@ -4846,6 +4871,54 @@ async fn set_contacts_by_addrs(context: &Context, id: ChatId, addrs: &[String])
48464871
Ok(())
48474872
}
48484873

4874+
/// Set chat contacts by their fingerprints creating the corresponding contacts if necessary.
4875+
///
4876+
/// `fingerprint_addrs` is a list of pairs of fingerprint and address.
4877+
async fn set_contacts_by_fingerprints(
4878+
context: &Context,
4879+
id: ChatId,
4880+
fingerprint_addrs: &[(String, String)],
4881+
) -> Result<()> {
4882+
let chat = Chat::load_from_db(context, id).await?;
4883+
ensure!(
4884+
chat.is_encrypted(context).await?,
4885+
"Cannot add PGP-contacts to unencrypted chat {id}"
4886+
);
4887+
ensure!(
4888+
chat.typ == Chattype::Broadcast,
4889+
"{id} is not a broadcast list",
4890+
);
4891+
let mut contacts = HashSet::new();
4892+
for (fingerprint, addr) in fingerprint_addrs {
4893+
let contact_addr = ContactAddress::new(addr)?;
4894+
let contact = Contact::add_or_lookup_ex(context, "", &contact_addr, &fingerprint, Origin::Hidden)
4895+
.await?
4896+
.0;
4897+
contacts.insert(contact);
4898+
}
4899+
let contacts_old = HashSet::<ContactId>::from_iter(get_chat_contacts(context, id).await?);
4900+
if contacts == contacts_old {
4901+
return Ok(());
4902+
}
4903+
context
4904+
.sql
4905+
.transaction(move |transaction| {
4906+
transaction.execute("DELETE FROM chats_contacts WHERE chat_id=?", (id,))?;
4907+
4908+
// We do not care about `add_timestamp` column
4909+
// because timestamps are not used for broadcast lists.
4910+
let mut statement = transaction
4911+
.prepare("INSERT INTO chats_contacts (chat_id, contact_id) VALUES (?, ?)")?;
4912+
for contact_id in &contacts {
4913+
statement.execute((id, contact_id))?;
4914+
}
4915+
Ok(())
4916+
})
4917+
.await?;
4918+
context.emit_event(EventType::ChatModified(id));
4919+
Ok(())
4920+
}
4921+
48494922
/// A cross-device chat id used for synchronisation.
48504923
#[derive(Debug, Serialize, Deserialize, PartialEq)]
48514924
pub(crate) enum SyncId {
@@ -4876,6 +4949,10 @@ pub(crate) enum SyncAction {
48764949
Rename(String),
48774950
/// Set chat contacts by their addresses.
48784951
SetContacts(Vec<String>),
4952+
/// Set chat contacts by their fingerprints.
4953+
///
4954+
/// The list is a list of pairs of fingerprint and address.
4955+
SetPgpContacts(Vec<(String, String)>),
48794956
Delete,
48804957
}
48814958

@@ -4959,6 +5036,7 @@ impl Context {
49595036
}
49605037
SyncAction::Rename(to) => rename_ex(self, Nosync, chat_id, to).await,
49615038
SyncAction::SetContacts(addrs) => set_contacts_by_addrs(self, chat_id, addrs).await,
5039+
SyncAction::SetPgpContacts(fingerprint_addrs) => set_contacts_by_fingerprints(self, chat_id, fingerprint_addrs).await,
49625040
SyncAction::Delete => chat_id.delete_ex(self, Nosync).await,
49635041
}
49645042
}

src/chat/chat_tests.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -3213,7 +3213,7 @@ async fn test_sync_broadcast() -> Result<()> {
32133213
for a in [alice0, alice1] {
32143214
a.set_config_bool(Config::SyncMsgs, true).await?;
32153215
}
3216-
let bob = TestContext::new_bob().await;
3216+
let bob = &tcm.bob().await;
32173217
let a0b_contact_id = alice0.add_or_lookup_contact(&bob).await.id;
32183218

32193219
let a0_broadcast_id = create_broadcast_list(alice0).await?;
@@ -3229,13 +3229,12 @@ async fn test_sync_broadcast() -> Result<()> {
32293229
assert!(get_chat_contacts(alice1, a1_broadcast_id).await?.is_empty());
32303230
add_contact_to_chat(alice0, a0_broadcast_id, a0b_contact_id).await?;
32313231
sync(alice0, alice1).await;
3232-
let a1b_contact_id = Contact::lookup_id_by_addr(
3233-
alice1,
3234-
&bob.get_config(Config::Addr).await?.unwrap(),
3235-
Origin::Hidden,
3236-
)
3237-
.await?
3238-
.unwrap();
3232+
3233+
// This also imports Bob's key from the vCard.
3234+
// Otherwise it is possible that second device
3235+
// does not have Bob's key as only the fingerprint
3236+
// is transferred in the sync message.
3237+
let a1b_contact_id = alice1.add_or_lookup_contact(bob).await.id;
32393238
assert_eq!(
32403239
get_chat_contacts(alice1, a1_broadcast_id).await?,
32413240
vec![a1b_contact_id]

src/contact.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1867,7 +1867,10 @@ pub(crate) async fn mark_contact_id_as_verified(
18671867
contact_id: ContactId,
18681868
verifier_id: ContactId,
18691869
) -> Result<()> {
1870-
debug_assert_ne!(contact_id, verifier_id, "Contact cannot be verified by self");
1870+
debug_assert_ne!(
1871+
contact_id, verifier_id,
1872+
"Contact cannot be verified by self"
1873+
);
18711874
context
18721875
.sql
18731876
.transaction(|transaction| {
@@ -1886,7 +1889,9 @@ pub(crate) async fn mark_contact_id_as_verified(
18861889
|row| row.get(0),
18871890
)?;
18881891
if verifier_fingerprint.is_empty() {
1889-
bail!("Contact {contact_id} cannot be verified by non-PGP contact {verifier_id}.");
1892+
bail!(
1893+
"Contact {contact_id} cannot be verified by non-PGP contact {verifier_id}."
1894+
);
18901895
}
18911896
}
18921897
transaction.execute(

src/message/message_tests.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use num_traits::FromPrimitive;
22

33
use super::*;
4-
use crate::chat::{
5-
self, add_contact_to_chat, forward_msgs, marknoticed_chat, save_msgs, send_text_msg, ChatItem,
6-
ProtectionStatus,
7-
};
4+
use crate::chat::{self, forward_msgs, marknoticed_chat, save_msgs, send_text_msg, ChatItem};
85
use crate::chatlist::Chatlist;
96
use crate::config::Config;
107
use crate::reaction::send_reaction;
@@ -197,7 +194,9 @@ async fn test_unencrypted_quote_encrypted_message() -> Result<()> {
197194

198195
tcm.section("Bob sends encrypted message to Alice");
199196
let alice_chat = alice.create_chat(bob).await;
200-
let sent = alice.send_text(alice_chat.id, "Hi! This is encrypted.").await;
197+
let sent = alice
198+
.send_text(alice_chat.id, "Hi! This is encrypted.")
199+
.await;
201200

202201
let bob_received_message = bob.recv_msg(&sent).await;
203202
assert_eq!(bob_received_message.get_showpadlock(), true);

src/receive_imf/receive_imf_tests.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -4735,10 +4735,7 @@ async fn test_protected_group_add_remove_member_missing_key() -> Result<()> {
47354735
let alice_bob_id = alice.add_or_lookup_contact(bob).await.id;
47364736
add_contact_to_chat(alice, group_id, alice_bob_id).await?;
47374737
alice.send_text(group_id, "Hello!").await;
4738-
alice
4739-
.sql
4740-
.execute("DELETE FROM public_keys", ())
4741-
.await?;
4738+
alice.sql.execute("DELETE FROM public_keys", ()).await?;
47424739

47434740
let fiona = &tcm.fiona().await;
47444741
let fiona_addr = fiona.get_config(Config::Addr).await?.unwrap();

src/securejoin/securejoin_tests.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
use deltachat_contact_tools::{ContactAddress, EmailAddress};
1+
use deltachat_contact_tools::EmailAddress;
22

33
use super::*;
44
use crate::chat::{remove_contact_from_chat, CantSendReason};
55
use crate::chatlist::Chatlist;
66
use crate::constants::{self, Chattype};
7-
use crate::imex::{imex, ImexMode};
87
use crate::receive_imf::receive_imf;
98
use crate::stock_str::{self, chat_protection_enabled};
109
use crate::test_utils::{
1110
get_chat_msg, TestContext, TestContextManager, TimeShiftFalsePositiveNote,
1211
};
1312
use crate::tools::SystemTime;
14-
use std::collections::HashSet;
1513
use std::time::Duration;
1614

1715
#[derive(PartialEq)]
@@ -234,9 +232,7 @@ async fn test_setup_contact_ex(case: SetupContactCase) {
234232
tcm.section("Step 5+6: Alice receives vc-request-with-auth, sends vc-contact-confirm");
235233
alice.recv_msg_trash(&sent).await;
236234
assert_eq!(contact_bob.is_verified(&alice).await.unwrap(), true);
237-
let contact_bob = Contact::get_by_id(&alice, contact_bob_id)
238-
.await
239-
.unwrap();
235+
let contact_bob = Contact::get_by_id(&alice, contact_bob_id).await.unwrap();
240236
assert_eq!(contact_bob.get_authname(), "Bob Examplenet");
241237
assert!(contact_bob.get_name().is_empty());
242238
assert_eq!(contact_bob.is_bot(), false);
@@ -465,9 +461,7 @@ async fn test_secure_join() -> Result<()> {
465461
chat::create_group_chat(&alice, ProtectionStatus::Protected, "the chat").await?;
466462

467463
tcm.section("Step 1: Generate QR-code, secure-join implied by chatid");
468-
let qr = get_securejoin_qr(&alice, Some(alice_chatid))
469-
.await
470-
.unwrap();
464+
let qr = get_securejoin_qr(&alice, Some(alice_chatid)).await.unwrap();
471465

472466
tcm.section("Step 2: Bob scans QR-code, sends vg-request");
473467
let bob_chatid = join_securejoin(&bob, &qr).await?;

src/tests/aeap.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use anyhow::Result;
22

33
use crate::chat::{self, Chat, ChatId, ProtectionStatus};
4-
use crate::contact;
5-
use crate::contact::Contact;
6-
use crate::contact::ContactId;
4+
use crate::contact::{Contact, ContactId};
75
use crate::message::Message;
86
use crate::receive_imf::receive_imf;
97
use crate::securejoin::get_securejoin_qr;

src/tests/verified_chats.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ use pretty_assertions::assert_eq;
44
use crate::chat::{
55
self, add_contact_to_chat, remove_contact_from_chat, send_msg, Chat, ProtectionStatus,
66
};
7-
use crate::chatlist::Chatlist;
87
use crate::config::Config;
9-
use crate::constants::{Chattype, DC_GCL_FOR_FORWARDING};
8+
use crate::constants::Chattype;
109
use crate::contact::{Contact, ContactId};
1110
use crate::message::Message;
1211
use crate::mimefactory::MimeFactory;

0 commit comments

Comments
 (0)