@@ -2289,19 +2289,40 @@ impl Chat {
2289
2289
2290
2290
/// Sends a `SyncAction` synchronising chat contacts to other devices.
2291
2291
pub ( crate ) async fn sync_contacts ( & self , context : & Context ) -> Result < ( ) > {
2292
- let addrs = context
2293
- . sql
2294
- . query_map (
2295
- "SELECT c.addr \
2296
- FROM contacts c INNER JOIN chats_contacts cc \
2297
- ON c.id=cc.contact_id \
2298
- WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp",
2299
- ( self . id , ) ,
2300
- |row| row. get :: < _ , String > ( 0 ) ,
2301
- |addrs| addrs. collect :: < Result < Vec < _ > , _ > > ( ) . map_err ( Into :: into) ,
2302
- )
2303
- . await ?;
2304
- self . sync ( context, SyncAction :: SetContacts ( addrs) ) . await
2292
+ if self . is_encrypted ( context) . await ? {
2293
+ let fingerprint_addrs = context
2294
+ . sql
2295
+ . query_map (
2296
+ "SELECT c.fingerprint, c.addr
2297
+ FROM contacts c INNER JOIN chats_contacts cc
2298
+ ON c.id=cc.contact_id
2299
+ WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp" ,
2300
+ ( self . id , ) ,
2301
+ |row| {
2302
+ let fingerprint = row. get ( 0 ) ?;
2303
+ let addr = row. get ( 1 ) ?;
2304
+ Ok ( ( fingerprint, addr) )
2305
+ } ,
2306
+ |addrs| addrs. collect :: < Result < Vec < _ > , _ > > ( ) . map_err ( Into :: into) ,
2307
+ )
2308
+ . await ?;
2309
+ self . sync ( context, SyncAction :: SetPgpContacts ( fingerprint_addrs) ) . await ?;
2310
+ } else {
2311
+ let addrs = context
2312
+ . sql
2313
+ . query_map (
2314
+ "SELECT c.addr \
2315
+ FROM contacts c INNER JOIN chats_contacts cc \
2316
+ ON c.id=cc.contact_id \
2317
+ WHERE cc.chat_id=? AND cc.add_timestamp >= cc.remove_timestamp",
2318
+ ( self . id , ) ,
2319
+ |row| row. get :: < _ , String > ( 0 ) ,
2320
+ |addrs| addrs. collect :: < Result < Vec < _ > , _ > > ( ) . map_err ( Into :: into) ,
2321
+ )
2322
+ . await ?;
2323
+ self . sync ( context, SyncAction :: SetContacts ( addrs) ) . await ?;
2324
+ }
2325
+ Ok ( ( ) )
2305
2326
}
2306
2327
2307
2328
/// Returns chat id for the purpose of synchronisation across devices.
@@ -4811,6 +4832,10 @@ pub(crate) async fn update_msg_text_and_timestamp(
4811
4832
/// Set chat contacts by their addresses creating the corresponding contacts if necessary.
4812
4833
async fn set_contacts_by_addrs ( context : & Context , id : ChatId , addrs : & [ String ] ) -> Result < ( ) > {
4813
4834
let chat = Chat :: load_from_db ( context, id) . await ?;
4835
+ ensure ! (
4836
+ !chat. is_encrypted( context) . await ?,
4837
+ "Cannot add email-contacts to encrypted chat {id}"
4838
+ ) ;
4814
4839
ensure ! (
4815
4840
chat. typ == Chattype :: Broadcast ,
4816
4841
"{id} is not a broadcast list" ,
@@ -4846,6 +4871,54 @@ async fn set_contacts_by_addrs(context: &Context, id: ChatId, addrs: &[String])
4846
4871
Ok ( ( ) )
4847
4872
}
4848
4873
4874
+ /// Set chat contacts by their fingerprints creating the corresponding contacts if necessary.
4875
+ ///
4876
+ /// `fingerprint_addrs` is a list of pairs of fingerprint and address.
4877
+ async fn set_contacts_by_fingerprints (
4878
+ context : & Context ,
4879
+ id : ChatId ,
4880
+ fingerprint_addrs : & [ ( String , String ) ] ,
4881
+ ) -> Result < ( ) > {
4882
+ let chat = Chat :: load_from_db ( context, id) . await ?;
4883
+ ensure ! (
4884
+ chat. is_encrypted( context) . await ?,
4885
+ "Cannot add PGP-contacts to unencrypted chat {id}"
4886
+ ) ;
4887
+ ensure ! (
4888
+ chat. typ == Chattype :: Broadcast ,
4889
+ "{id} is not a broadcast list" ,
4890
+ ) ;
4891
+ let mut contacts = HashSet :: new ( ) ;
4892
+ for ( fingerprint, addr) in fingerprint_addrs {
4893
+ let contact_addr = ContactAddress :: new ( addr) ?;
4894
+ let contact = Contact :: add_or_lookup_ex ( context, "" , & contact_addr, & fingerprint, Origin :: Hidden )
4895
+ . await ?
4896
+ . 0 ;
4897
+ contacts. insert ( contact) ;
4898
+ }
4899
+ let contacts_old = HashSet :: < ContactId > :: from_iter ( get_chat_contacts ( context, id) . await ?) ;
4900
+ if contacts == contacts_old {
4901
+ return Ok ( ( ) ) ;
4902
+ }
4903
+ context
4904
+ . sql
4905
+ . transaction ( move |transaction| {
4906
+ transaction. execute ( "DELETE FROM chats_contacts WHERE chat_id=?" , ( id, ) ) ?;
4907
+
4908
+ // We do not care about `add_timestamp` column
4909
+ // because timestamps are not used for broadcast lists.
4910
+ let mut statement = transaction
4911
+ . prepare ( "INSERT INTO chats_contacts (chat_id, contact_id) VALUES (?, ?)" ) ?;
4912
+ for contact_id in & contacts {
4913
+ statement. execute ( ( id, contact_id) ) ?;
4914
+ }
4915
+ Ok ( ( ) )
4916
+ } )
4917
+ . await ?;
4918
+ context. emit_event ( EventType :: ChatModified ( id) ) ;
4919
+ Ok ( ( ) )
4920
+ }
4921
+
4849
4922
/// A cross-device chat id used for synchronisation.
4850
4923
#[ derive( Debug , Serialize , Deserialize , PartialEq ) ]
4851
4924
pub ( crate ) enum SyncId {
@@ -4876,6 +4949,10 @@ pub(crate) enum SyncAction {
4876
4949
Rename ( String ) ,
4877
4950
/// Set chat contacts by their addresses.
4878
4951
SetContacts ( Vec < String > ) ,
4952
+ /// Set chat contacts by their fingerprints.
4953
+ ///
4954
+ /// The list is a list of pairs of fingerprint and address.
4955
+ SetPgpContacts ( Vec < ( String , String ) > ) ,
4879
4956
Delete ,
4880
4957
}
4881
4958
@@ -4959,6 +5036,7 @@ impl Context {
4959
5036
}
4960
5037
SyncAction :: Rename ( to) => rename_ex ( self , Nosync , chat_id, to) . await ,
4961
5038
SyncAction :: SetContacts ( addrs) => set_contacts_by_addrs ( self , chat_id, addrs) . await ,
5039
+ SyncAction :: SetPgpContacts ( fingerprint_addrs) => set_contacts_by_fingerprints ( self , chat_id, fingerprint_addrs) . await ,
4962
5040
SyncAction :: Delete => chat_id. delete_ex ( self , Nosync ) . await ,
4963
5041
}
4964
5042
}
0 commit comments