mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 21:06:31 +03:00
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:
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user