diff --git a/src/chat.rs b/src/chat.rs index 7f49a4ace..7b0aa53a1 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1885,10 +1885,11 @@ impl Chat { pub async fn is_encrypted(&self, context: &Context) -> Result { let is_encrypted = self.is_protected() || match self.typ { - Chattype::Single => { - match context + Chattype::Group if !self.grpid.is_empty() => true, + Chattype::Group | Chattype::Single => { + let contacts = context .sql - .query_row_optional( + .query_map( "SELECT cc.contact_id, c.fingerprint<>'' FROM chats_contacts cc LEFT JOIN contacts c ON c.id=cc.contact_id @@ -1900,16 +1901,13 @@ impl Chat { let is_key: bool = row.get(1)?; Ok((id, is_key)) }, + |ids| ids.collect::, _>>().map_err(Into::into), ) - .await? - { - Some((id, is_key)) => is_key || id == ContactId::DEVICE, - None => true, - } - } - Chattype::Group => { - // Do not encrypt ad-hoc groups. - !self.grpid.is_empty() + .await?; + (self.typ != Chattype::Group || contacts.iter().any(|&(_, is_key)| is_key)) + && contacts.iter().all(|&(id, is_key)| { + is_key || matches!(id, ContactId::SELF | ContactId::DEVICE) + }) } Chattype::Mailinglist => false, Chattype::OutBroadcast | Chattype::InBroadcast => true, diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 185a2626d..c2d09ded8 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -114,7 +114,8 @@ enum ChatAssignment { /// Group chat without a Group ID. /// - /// This is not encrypted. + /// This is either encrypted or unencrypted. Encryption never downgrades, but it can upgrade if + /// the last address-contact is removed from the group. AdHocGroup, /// Assign the message to existing chat @@ -372,25 +373,52 @@ async fn get_to_and_past_contact_ids( } } ChatAssignment::AdHocGroup => { - to_ids = add_or_lookup_contacts_by_address_list( - context, - &mime_parser.recipients, - if !mime_parser.incoming { - Origin::OutgoingTo - } else if incoming_origin.is_known() { - Origin::IncomingTo - } else { - Origin::IncomingUnknownTo - }, - ) - .await?; + let key_to_ids = match mime_parser.was_encrypted() { + false => Vec::new(), + true => { + let ids = lookup_key_contacts_by_address_list( + context, + &mime_parser.recipients, + to_member_fingerprints, + None, + ) + .await?; + match ids.contains(&None) { + false => ids, + true => Vec::new(), + } + } + }; + if !key_to_ids.is_empty() { + to_ids = key_to_ids; + past_ids = lookup_key_contacts_by_address_list( + context, + &mime_parser.past_members, + past_member_fingerprints, + None, + ) + .await?; + } else { + to_ids = add_or_lookup_contacts_by_address_list( + context, + &mime_parser.recipients, + if !mime_parser.incoming { + Origin::OutgoingTo + } else if incoming_origin.is_known() { + Origin::IncomingTo + } else { + Origin::IncomingUnknownTo + }, + ) + .await?; - past_ids = add_or_lookup_contacts_by_address_list( - context, - &mime_parser.past_members, - Origin::Hidden, - ) - .await?; + past_ids = add_or_lookup_contacts_by_address_list( + context, + &mime_parser.past_members, + Origin::Hidden, + ) + .await?; + } } ChatAssignment::OneOneChat => { let pgp_to_ids = add_or_lookup_key_contacts( @@ -2535,7 +2563,7 @@ async fn lookup_or_create_adhoc_group( .query_row( "SELECT c.id, c.blocked FROM chats c INNER JOIN msgs m ON c.id=m.chat_id - WHERE m.hidden=0 AND c.grpid='' AND c.name=? + WHERE m.hidden=0 AND (? OR c.grpid='') AND c.name=? AND (SELECT COUNT(*) FROM chats_contacts WHERE chat_id=c.id AND add_timestamp >= remove_timestamp)=? @@ -2544,7 +2572,7 @@ async fn lookup_or_create_adhoc_group( AND contact_id NOT IN (SELECT id FROM temp.contacts) AND add_timestamp >= remove_timestamp)=0 ORDER BY m.timestamp DESC", - (&grpname, contact_ids.len()), + (mime_parser.was_encrypted(), &grpname, contact_ids.len()), |row| { let id: ChatId = row.get(0)?; let blocked: Blocked = row.get(1)?;