Skip to content

Commit f3eea99

Browse files
committed
fix: Key-contacts migration: ignore past members with missing keys (#6941)
Missing key for a past member isn't a reason for conversion of an encrypted group to an ad hoc group.
1 parent 5c3de75 commit f3eea99

File tree

1 file changed

+47
-39
lines changed

1 file changed

+47
-39
lines changed

src/sql/migrations.rs

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,8 +1588,10 @@ fn migrate_key_contacts(
15881588
Ok((id, typ, grpid, protected))
15891589
})
15901590
.context("Step 23")?;
1591-
let mut load_chat_contacts_stmt = transaction
1592-
.prepare("SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id>9")?;
1591+
let mut load_chat_contacts_stmt = transaction.prepare(
1592+
"SELECT contact_id, add_timestamp>=remove_timestamp FROM chats_contacts
1593+
WHERE chat_id=? AND contact_id>9",
1594+
)?;
15931595
let is_chatmail: Option<String> = transaction
15941596
.query_row(
15951597
"SELECT value FROM config WHERE keyname='is_chatmail'",
@@ -1603,23 +1605,20 @@ fn migrate_key_contacts(
16031605
.unwrap_or_default()
16041606
!= 0;
16051607
let map_to_key_contact = |old_member: &u32| {
1606-
(
1607-
*old_member,
1608-
autocrypt_key_contacts
1609-
.get(old_member)
1610-
.or_else(|| {
1611-
// For chatmail servers,
1612-
// we send encrypted even if the peerstate is reset,
1613-
// because an unencrypted message likely won't arrive.
1614-
// This is the same behavior as before key-contacts migration.
1615-
if is_chatmail {
1616-
autocrypt_key_contacts_with_reset_peerstate.get(old_member)
1617-
} else {
1618-
None
1619-
}
1620-
})
1621-
.copied(),
1622-
)
1608+
autocrypt_key_contacts
1609+
.get(old_member)
1610+
.or_else(|| {
1611+
// For chatmail servers,
1612+
// we send encrypted even if the peerstate is reset,
1613+
// because an unencrypted message likely won't arrive.
1614+
// This is the same behavior as before key-contacts migration.
1615+
if is_chatmail {
1616+
autocrypt_key_contacts_with_reset_peerstate.get(old_member)
1617+
} else {
1618+
None
1619+
}
1620+
})
1621+
.copied()
16231622
};
16241623

16251624
let mut update_member_stmt = transaction
@@ -1628,23 +1627,27 @@ fn migrate_key_contacts(
16281627
.prepare("SELECT c.addr=d.addr FROM contacts c, contacts d WHERE c.id=? AND d.id=?")?;
16291628
for chat in all_chats {
16301629
let (chat_id, typ, grpid, protected) = chat.context("Step 24")?;
1631-
// In groups, this also contains past members
1632-
let old_members: Vec<u32> = load_chat_contacts_stmt
1633-
.query_map((chat_id,), |row| row.get::<_, u32>(0))
1630+
// In groups, this also contains past members, i.e. `(_, false)` entries.
1631+
let old_members: Vec<(u32, bool)> = load_chat_contacts_stmt
1632+
.query_map((chat_id,), |row| {
1633+
let id: u32 = row.get(0)?;
1634+
let present: bool = row.get(1)?;
1635+
Ok((id, present))
1636+
})
16341637
.context("Step 25")?
1635-
.collect::<Result<Vec<u32>, rusqlite::Error>>()
1638+
.collect::<Result<Vec<_>, _>>()
16361639
.context("Step 26")?;
16371640

16381641
let mut keep_address_contacts = |reason: &str| {
16391642
info!(
16401643
context,
1641-
"Chat {chat_id} will be an unencrypted chat with contacts identified by email address: {reason}"
1644+
"Chat {chat_id} will be an unencrypted chat with contacts identified by email address: {reason}."
16421645
);
1643-
for m in &old_members {
1646+
for (m, _) in &old_members {
16441647
orphaned_contacts.remove(m);
16451648
}
16461649
};
1647-
let old_and_new_members: Vec<(u32, Option<u32>)> = match typ {
1650+
let old_and_new_members: Vec<(u32, bool, Option<u32>)> = match typ {
16481651
// 1:1 chats retain:
16491652
// - address-contact if peerstate is in the "reset" state,
16501653
// or if there is no key-contact that has the right email address.
@@ -1653,15 +1656,15 @@ fn migrate_key_contacts(
16531656
// Since the autocrypt and verified key-contact are identital in this case, we can add the Autocrypt key-contact,
16541657
// and the effect will be the same.
16551658
100 => {
1656-
let Some(old_member) = old_members.first() else {
1659+
let Some((old_member, _)) = old_members.first() else {
16571660
info!(
16581661
context,
1659-
"1:1 chat {chat_id} doesn't contain contact, probably it's self or device chat"
1662+
"1:1 chat {chat_id} doesn't contain contact, probably it's self or device chat."
16601663
);
16611664
continue;
16621665
};
16631666

1664-
let (_, Some(new_contact)) = map_to_key_contact(old_member) else {
1667+
let Some(new_contact) = map_to_key_contact(old_member) else {
16651668
keep_address_contacts("No peerstate, or peerstate in 'reset' state");
16661669
continue;
16671670
};
@@ -1677,7 +1680,7 @@ fn migrate_key_contacts(
16771680
keep_address_contacts("key contact has different email");
16781681
continue;
16791682
}
1680-
vec![(*old_member, Some(new_contact))]
1683+
vec![(*old_member, true, Some(new_contact))]
16811684
}
16821685

16831686
// Group
@@ -1690,15 +1693,15 @@ fn migrate_key_contacts(
16901693
} else if protected == 1 {
16911694
old_members
16921695
.iter()
1693-
.map(|old_member| {
1694-
(*old_member, verified_key_contacts.get(old_member).copied())
1696+
.map(|&(id, present)| {
1697+
(id, present, verified_key_contacts.get(&id).copied())
16951698
})
16961699
.collect()
16971700
} else {
16981701
old_members
16991702
.iter()
1700-
.map(map_to_key_contact)
1701-
.collect::<Vec<(u32, Option<u32>)>>()
1703+
.map(|&(id, present)| (id, present, map_to_key_contact(&id)))
1704+
.collect::<Vec<(u32, bool, Option<u32>)>>()
17021705
}
17031706
}
17041707

@@ -1711,9 +1714,10 @@ fn migrate_key_contacts(
17111714
// Broadcast list
17121715
160 => old_members
17131716
.iter()
1714-
.map(|original| {
1717+
.map(|(original, _)| {
17151718
(
17161719
*original,
1720+
true,
17171721
autocrypt_key_contacts
17181722
.get(original)
17191723
// There will be no unencrypted broadcast lists anymore,
@@ -1725,7 +1729,7 @@ fn migrate_key_contacts(
17251729
.copied(),
17261730
)
17271731
})
1728-
.collect::<Vec<(u32, Option<u32>)>>(),
1732+
.collect::<Vec<(u32, bool, Option<u32>)>>(),
17291733
_ => {
17301734
warn!(context, "Invalid chat type {typ}");
17311735
continue;
@@ -1734,7 +1738,11 @@ fn migrate_key_contacts(
17341738

17351739
// If a group contains a contact without a key or with 'reset' peerstate,
17361740
// downgrade to unencrypted Ad-Hoc group.
1737-
if typ == 120 && old_and_new_members.iter().any(|(_old, new)| new.is_none()) {
1741+
if typ == 120
1742+
&& old_and_new_members
1743+
.iter()
1744+
.any(|&(_old, present, new)| present && new.is_none())
1745+
{
17381746
transaction
17391747
.execute("UPDATE chats SET grpid='' WHERE id=?", (chat_id,))
17401748
.context("Step 26.1")?;
@@ -1744,15 +1752,15 @@ fn migrate_key_contacts(
17441752

17451753
let human_readable_transitions = old_and_new_members
17461754
.iter()
1747-
.map(|(old, new)| format!("{old}->{}", new.unwrap_or_default()))
1755+
.map(|(old, _, new)| format!("{old}->{}", new.unwrap_or_default()))
17481756
.collect::<Vec<String>>()
17491757
.join(" ");
17501758
info!(
17511759
context,
17521760
"Migrating chat {chat_id} to key-contacts: {human_readable_transitions}"
17531761
);
17541762

1755-
for (old_member, new_member) in old_and_new_members {
1763+
for (old_member, _, new_member) in old_and_new_members {
17561764
if let Some(new_member) = new_member {
17571765
orphaned_contacts.remove(&new_member);
17581766
let res = update_member_stmt.execute((new_member, old_member, chat_id));

0 commit comments

Comments
 (0)