Skip to content

Commit ea48d78

Browse files
committed
fix broadcast list synchronization
1 parent c6b3876 commit ea48d78

File tree

8 files changed

+115
-46
lines changed

8 files changed

+115
-46
lines changed

src/chat.rs

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,19 +2286,40 @@ impl Chat {
22862286

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

23042325
/// Returns chat id for the purpose of synchronisation across devices.
@@ -4808,6 +4829,10 @@ pub(crate) async fn update_msg_text_and_timestamp(
48084829
/// Set chat contacts by their addresses creating the corresponding contacts if necessary.
48094830
async fn set_contacts_by_addrs(context: &Context, id: ChatId, addrs: &[String]) -> Result<()> {
48104831
let chat = Chat::load_from_db(context, id).await?;
4832+
ensure!(
4833+
!chat.is_encrypted(context).await?,
4834+
"Cannot add email-contacts to encrypted chat {id}"
4835+
);
48114836
ensure!(
48124837
chat.typ == Chattype::Broadcast,
48134838
"{id} is not a broadcast list",
@@ -4843,6 +4868,54 @@ async fn set_contacts_by_addrs(context: &Context, id: ChatId, addrs: &[String])
48434868
Ok(())
48444869
}
48454870

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

@@ -4956,6 +5033,7 @@ impl Context {
49565033
}
49575034
SyncAction::Rename(to) => rename_ex(self, Nosync, chat_id, to).await,
49585035
SyncAction::SetContacts(addrs) => set_contacts_by_addrs(self, chat_id, addrs).await,
5036+
SyncAction::SetPgpContacts(fingerprint_addrs) => set_contacts_by_fingerprints(self, chat_id, fingerprint_addrs).await,
49595037
SyncAction::Delete => chat_id.delete_ex(self, Nosync).await,
49605038
}
49615039
}

src/chat/chat_tests.rs

Lines changed: 7 additions & 8 deletions
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

Lines changed: 7 additions & 2 deletions
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

Lines changed: 4 additions & 5 deletions
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

Lines changed: 1 addition & 4 deletions
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

Lines changed: 3 additions & 9 deletions
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

Lines changed: 1 addition & 3 deletions
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

Lines changed: 1 addition & 2 deletions
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)