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.
This commit is contained in:
iequidoo
2025-06-27 17:28:44 -03:00
committed by iequidoo
parent 5c3de759d3
commit f3eea9937c

View File

@@ -1588,8 +1588,10 @@ fn migrate_key_contacts(
Ok((id, typ, grpid, protected)) Ok((id, typ, grpid, protected))
}) })
.context("Step 23")?; .context("Step 23")?;
let mut load_chat_contacts_stmt = transaction let mut load_chat_contacts_stmt = transaction.prepare(
.prepare("SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id>9")?; "SELECT contact_id, add_timestamp>=remove_timestamp FROM chats_contacts
WHERE chat_id=? AND contact_id>9",
)?;
let is_chatmail: Option<String> = transaction let is_chatmail: Option<String> = transaction
.query_row( .query_row(
"SELECT value FROM config WHERE keyname='is_chatmail'", "SELECT value FROM config WHERE keyname='is_chatmail'",
@@ -1603,8 +1605,6 @@ fn migrate_key_contacts(
.unwrap_or_default() .unwrap_or_default()
!= 0; != 0;
let map_to_key_contact = |old_member: &u32| { let map_to_key_contact = |old_member: &u32| {
(
*old_member,
autocrypt_key_contacts autocrypt_key_contacts
.get(old_member) .get(old_member)
.or_else(|| { .or_else(|| {
@@ -1618,8 +1618,7 @@ fn migrate_key_contacts(
None None
} }
}) })
.copied(), .copied()
)
}; };
let mut update_member_stmt = transaction let mut update_member_stmt = transaction
@@ -1628,23 +1627,27 @@ fn migrate_key_contacts(
.prepare("SELECT c.addr=d.addr FROM contacts c, contacts d WHERE c.id=? AND d.id=?")?; .prepare("SELECT c.addr=d.addr FROM contacts c, contacts d WHERE c.id=? AND d.id=?")?;
for chat in all_chats { for chat in all_chats {
let (chat_id, typ, grpid, protected) = chat.context("Step 24")?; let (chat_id, typ, grpid, protected) = chat.context("Step 24")?;
// In groups, this also contains past members // In groups, this also contains past members, i.e. `(_, false)` entries.
let old_members: Vec<u32> = load_chat_contacts_stmt let old_members: Vec<(u32, bool)> = load_chat_contacts_stmt
.query_map((chat_id,), |row| row.get::<_, u32>(0)) .query_map((chat_id,), |row| {
let id: u32 = row.get(0)?;
let present: bool = row.get(1)?;
Ok((id, present))
})
.context("Step 25")? .context("Step 25")?
.collect::<Result<Vec<u32>, rusqlite::Error>>() .collect::<Result<Vec<_>, _>>()
.context("Step 26")?; .context("Step 26")?;
let mut keep_address_contacts = |reason: &str| { let mut keep_address_contacts = |reason: &str| {
info!( info!(
context, context,
"Chat {chat_id} will be an unencrypted chat with contacts identified by email address: {reason}" "Chat {chat_id} will be an unencrypted chat with contacts identified by email address: {reason}."
); );
for m in &old_members { for (m, _) in &old_members {
orphaned_contacts.remove(m); orphaned_contacts.remove(m);
} }
}; };
let old_and_new_members: Vec<(u32, Option<u32>)> = match typ { let old_and_new_members: Vec<(u32, bool, Option<u32>)> = match typ {
// 1:1 chats retain: // 1:1 chats retain:
// - address-contact if peerstate is in the "reset" state, // - address-contact if peerstate is in the "reset" state,
// or if there is no key-contact that has the right email address. // or if there is no key-contact that has the right email address.
@@ -1653,15 +1656,15 @@ fn migrate_key_contacts(
// Since the autocrypt and verified key-contact are identital in this case, we can add the Autocrypt key-contact, // Since the autocrypt and verified key-contact are identital in this case, we can add the Autocrypt key-contact,
// and the effect will be the same. // and the effect will be the same.
100 => { 100 => {
let Some(old_member) = old_members.first() else { let Some((old_member, _)) = old_members.first() else {
info!( info!(
context, context,
"1:1 chat {chat_id} doesn't contain contact, probably it's self or device chat" "1:1 chat {chat_id} doesn't contain contact, probably it's self or device chat."
); );
continue; continue;
}; };
let (_, Some(new_contact)) = map_to_key_contact(old_member) else { let Some(new_contact) = map_to_key_contact(old_member) else {
keep_address_contacts("No peerstate, or peerstate in 'reset' state"); keep_address_contacts("No peerstate, or peerstate in 'reset' state");
continue; continue;
}; };
@@ -1677,7 +1680,7 @@ fn migrate_key_contacts(
keep_address_contacts("key contact has different email"); keep_address_contacts("key contact has different email");
continue; continue;
} }
vec![(*old_member, Some(new_contact))] vec![(*old_member, true, Some(new_contact))]
} }
// Group // Group
@@ -1690,15 +1693,15 @@ fn migrate_key_contacts(
} else if protected == 1 { } else if protected == 1 {
old_members old_members
.iter() .iter()
.map(|old_member| { .map(|&(id, present)| {
(*old_member, verified_key_contacts.get(old_member).copied()) (id, present, verified_key_contacts.get(&id).copied())
}) })
.collect() .collect()
} else { } else {
old_members old_members
.iter() .iter()
.map(map_to_key_contact) .map(|&(id, present)| (id, present, map_to_key_contact(&id)))
.collect::<Vec<(u32, Option<u32>)>>() .collect::<Vec<(u32, bool, Option<u32>)>>()
} }
} }
@@ -1711,9 +1714,10 @@ fn migrate_key_contacts(
// Broadcast list // Broadcast list
160 => old_members 160 => old_members
.iter() .iter()
.map(|original| { .map(|(original, _)| {
( (
*original, *original,
true,
autocrypt_key_contacts autocrypt_key_contacts
.get(original) .get(original)
// There will be no unencrypted broadcast lists anymore, // There will be no unencrypted broadcast lists anymore,
@@ -1725,7 +1729,7 @@ fn migrate_key_contacts(
.copied(), .copied(),
) )
}) })
.collect::<Vec<(u32, Option<u32>)>>(), .collect::<Vec<(u32, bool, Option<u32>)>>(),
_ => { _ => {
warn!(context, "Invalid chat type {typ}"); warn!(context, "Invalid chat type {typ}");
continue; continue;
@@ -1734,7 +1738,11 @@ fn migrate_key_contacts(
// If a group contains a contact without a key or with 'reset' peerstate, // If a group contains a contact without a key or with 'reset' peerstate,
// downgrade to unencrypted Ad-Hoc group. // downgrade to unencrypted Ad-Hoc group.
if typ == 120 && old_and_new_members.iter().any(|(_old, new)| new.is_none()) { if typ == 120
&& old_and_new_members
.iter()
.any(|&(_old, present, new)| present && new.is_none())
{
transaction transaction
.execute("UPDATE chats SET grpid='' WHERE id=?", (chat_id,)) .execute("UPDATE chats SET grpid='' WHERE id=?", (chat_id,))
.context("Step 26.1")?; .context("Step 26.1")?;
@@ -1744,7 +1752,7 @@ fn migrate_key_contacts(
let human_readable_transitions = old_and_new_members let human_readable_transitions = old_and_new_members
.iter() .iter()
.map(|(old, new)| format!("{old}->{}", new.unwrap_or_default())) .map(|(old, _, new)| format!("{old}->{}", new.unwrap_or_default()))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(" "); .join(" ");
info!( info!(
@@ -1752,7 +1760,7 @@ fn migrate_key_contacts(
"Migrating chat {chat_id} to key-contacts: {human_readable_transitions}" "Migrating chat {chat_id} to key-contacts: {human_readable_transitions}"
); );
for (old_member, new_member) in old_and_new_members { for (old_member, _, new_member) in old_and_new_members {
if let Some(new_member) = new_member { if let Some(new_member) = new_member {
orphaned_contacts.remove(&new_member); orphaned_contacts.remove(&new_member);
let res = update_member_stmt.execute((new_member, old_member, chat_id)); let res = update_member_stmt.execute((new_member, old_member, chat_id));