@@ -56,6 +56,8 @@ use tokio::sync::RwLock;
56
56
use tokio:: sync:: RwLockReadGuard ;
57
57
use tracing:: { debug, info, instrument, trace, warn} ;
58
58
59
+ #[ cfg( all( feature = "e2e-encryption" , feature = "experimental-sliding-sync" ) ) ]
60
+ use crate :: latest_event:: { is_suitable_for_latest_event, PossibleLatestEvent } ;
59
61
use crate :: {
60
62
deserialized_responses:: { AmbiguityChanges , MembersResponse , SyncTimelineEvent } ,
61
63
error:: Result ,
@@ -561,24 +563,84 @@ impl BaseClient {
561
563
changed_devices : & api:: sync:: sync_events:: DeviceLists ,
562
564
one_time_keys_counts : & BTreeMap < ruma:: DeviceKeyAlgorithm , UInt > ,
563
565
unused_fallback_keys : Option < & [ ruma:: DeviceKeyAlgorithm ] > ,
566
+ changes : & mut StateChanges ,
564
567
) -> Result < Vec < Raw < ruma:: events:: AnyToDeviceEvent > > > {
565
568
if let Some ( o) = self . olm_machine ( ) . await . as_ref ( ) {
566
569
// Let the crypto machine handle the sync response, this
567
570
// decrypts to-device events, but leaves room events alone.
568
571
// This makes sure that we have the decryption keys for the room
569
572
// events at hand.
570
- Ok ( o. receive_sync_changes (
571
- to_device_events,
572
- changed_devices,
573
- one_time_keys_counts,
574
- unused_fallback_keys,
575
- )
576
- . await ?)
573
+ let ( events, room_key_updates) = o
574
+ . receive_sync_changes (
575
+ to_device_events,
576
+ changed_devices,
577
+ one_time_keys_counts,
578
+ unused_fallback_keys,
579
+ )
580
+ . await ?;
581
+
582
+ #[ cfg( feature = "experimental-sliding-sync" ) ]
583
+ for room_key_update in room_key_updates {
584
+ if let Some ( mut room) = self . get_room ( & room_key_update. room_id ) {
585
+ self . decrypt_latest_events ( & mut room, changes) . await ;
586
+ }
587
+ }
588
+
589
+ Ok ( events)
577
590
} else {
591
+ // If we have no OlmMachine, just return the events that were passed in.
592
+ // This should not happen unless we forget to set things up by calling
593
+ // set_session_meta().
578
594
Ok ( to_device_events)
579
595
}
580
596
}
581
597
598
+ /// Decrypt any of this room's latest_encrypted_events
599
+ /// that we can and if we can, change latest_event to reflect what we
600
+ /// found, and remove any older encrypted events from
601
+ /// latest_encrypted_events.
602
+ #[ cfg( all( feature = "e2e-encryption" , feature = "experimental-sliding-sync" ) ) ]
603
+ async fn decrypt_latest_events ( & self , room : & mut Room , changes : & mut StateChanges ) {
604
+ // Try to find a message we can decrypt and is suitable for using as the latest
605
+ // event. If we found one, set it as the latest and delete any older
606
+ // encrypted events
607
+ if let Some ( ( found, found_index) ) = self . decrypt_latest_suitable_event ( room) . await {
608
+ room. on_latest_event_decrypted ( found, found_index) ;
609
+ changes. room_infos . insert ( room. room_id ( ) . to_owned ( ) , room. clone_info ( ) ) ;
610
+ }
611
+ }
612
+
613
+ /// Attempt to decrypt a latest event, trying the latest stored encrypted
614
+ /// one first, and walking backwards, stopping when we find an event
615
+ /// that we can decrypt, and that is suitable to be the latest event
616
+ /// (i.e. we can usefully display it as a message preview). Returns the
617
+ /// decrypted event if we found one, along with its index in the
618
+ /// latest_encrypted_events list, or None if we didn't find one.
619
+ #[ cfg( all( feature = "e2e-encryption" , feature = "experimental-sliding-sync" ) ) ]
620
+ async fn decrypt_latest_suitable_event (
621
+ & self ,
622
+ room : & Room ,
623
+ ) -> Option < ( SyncTimelineEvent , usize ) > {
624
+ let enc_events = room. latest_encrypted_events ( ) ;
625
+
626
+ // Walk backwards through the encrypted events, looking for one we can decrypt
627
+ for ( i, event) in enc_events. iter ( ) . enumerate ( ) . rev ( ) {
628
+ if let Ok ( Some ( decrypted) ) = self . decrypt_sync_room_event ( event, room. room_id ( ) ) . await {
629
+ // We found an event we can decrypt
630
+ if let Ok ( any_sync_event) = decrypted. event . deserialize ( ) {
631
+ // We can deserialize it to find its type
632
+ if let PossibleLatestEvent :: YesMessageLike ( _) =
633
+ is_suitable_for_latest_event ( & any_sync_event)
634
+ {
635
+ // The event is the right type for us to use as latest_event
636
+ return Some ( ( decrypted, i) ) ;
637
+ }
638
+ }
639
+ }
640
+ }
641
+ None
642
+ }
643
+
582
644
/// User has joined a room.
583
645
///
584
646
/// Update the internal and cached state accordingly. Return the final Room.
@@ -644,6 +706,7 @@ impl BaseClient {
644
706
}
645
707
646
708
let now = Instant :: now ( ) ;
709
+ let mut changes = Box :: new ( StateChanges :: new ( response. next_batch . clone ( ) ) ) ;
647
710
648
711
#[ cfg( feature = "e2e-encryption" ) ]
649
712
let to_device = self
@@ -652,13 +715,13 @@ impl BaseClient {
652
715
& response. device_lists ,
653
716
& response. device_one_time_keys_count ,
654
717
response. device_unused_fallback_key_types . as_deref ( ) ,
718
+ & mut changes,
655
719
)
656
720
. await ?;
657
721
658
722
#[ cfg( not( feature = "e2e-encryption" ) ) ]
659
723
let to_device = response. to_device . events ;
660
724
661
- let mut changes = Box :: new ( StateChanges :: new ( response. next_batch . clone ( ) ) ) ;
662
725
let mut ambiguity_cache = AmbiguityCache :: new ( self . store . inner . clone ( ) ) ;
663
726
664
727
self . handle_account_data ( & response. account_data . events , & mut changes) . await ;
@@ -1225,31 +1288,24 @@ impl Default for BaseClient {
1225
1288
#[ cfg( test) ]
1226
1289
mod tests {
1227
1290
use matrix_sdk_test:: {
1228
- async_test, response_from_file, EventBuilder , InvitedRoomBuilder , LeftRoomBuilder ,
1229
- StrippedStateTestEvent , TimelineTestEvent ,
1291
+ async_test, response_from_file, EventBuilder , InvitedRoomBuilder , JoinedRoomBuilder ,
1292
+ LeftRoomBuilder , StrippedStateTestEvent , TimelineTestEvent ,
1230
1293
} ;
1231
1294
use ruma:: {
1232
1295
api:: { client as api, IncomingResponse } ,
1233
- room_id, user_id,
1296
+ room_id, user_id, RoomId , UserId ,
1234
1297
} ;
1235
1298
use serde_json:: json;
1236
1299
1237
1300
use super :: BaseClient ;
1238
- use crate :: { DisplayName , RoomState , SessionMeta } ;
1301
+ use crate :: { DisplayName , Room , RoomState , SessionMeta , StateChanges } ;
1239
1302
1240
1303
#[ async_test]
1241
1304
async fn invite_after_leaving ( ) {
1242
1305
let user_id = user_id ! ( "@alice:example.org" ) ;
1243
1306
let room_id = room_id ! ( "!test:example.org" ) ;
1244
1307
1245
- let client = BaseClient :: new ( ) ;
1246
- client
1247
- . set_session_meta ( SessionMeta {
1248
- user_id : user_id. to_owned ( ) ,
1249
- device_id : "FOOBAR" . into ( ) ,
1250
- } )
1251
- . await
1252
- . unwrap ( ) ;
1308
+ let client = logged_in_client ( user_id) . await ;
1253
1309
1254
1310
let mut ev_builder = EventBuilder :: new ( ) ;
1255
1311
@@ -1295,14 +1351,7 @@ mod tests {
1295
1351
let user_id = user_id ! ( "@alice:example.org" ) ;
1296
1352
let room_id = room_id ! ( "!ithpyNKDtmhneaTQja:example.org" ) ;
1297
1353
1298
- let client = BaseClient :: new ( ) ;
1299
- client
1300
- . set_session_meta ( SessionMeta {
1301
- user_id : user_id. to_owned ( ) ,
1302
- device_id : "FOOBAR" . into ( ) ,
1303
- } )
1304
- . await
1305
- . unwrap ( ) ;
1354
+ let client = logged_in_client ( user_id) . await ;
1306
1355
1307
1356
let response = api:: sync:: sync_events:: v3:: Response :: try_from_http_response ( response_from_file ( & json ! ( {
1308
1357
"next_batch" : "asdkl;fjasdkl;fj;asdkl;f" ,
@@ -1384,4 +1433,72 @@ mod tests {
1384
1433
DisplayName :: Calculated ( "Kyra" . to_owned( ) )
1385
1434
) ;
1386
1435
}
1436
+
1437
+ #[ cfg( all( feature = "e2e-encryption" , feature = "experimental-sliding-sync" ) ) ]
1438
+ #[ async_test]
1439
+ async fn when_there_are_no_latest_encrypted_events_decrypting_them_does_nothing ( ) {
1440
+ // Given a room
1441
+ let user_id = user_id ! ( "@u:u.to" ) ;
1442
+ let room_id = room_id ! ( "!r:u.to" ) ;
1443
+ let client = logged_in_client ( user_id) . await ;
1444
+ let mut room = process_room_join ( & client, room_id, "$1" , user_id) . await ;
1445
+
1446
+ // Sanity: it has no latest_encrypted_events or latest_event
1447
+ assert ! ( room. latest_encrypted_events( ) . is_empty( ) ) ;
1448
+ assert ! ( room. latest_event( ) . is_none( ) ) ;
1449
+
1450
+ // When I tell it to do some decryption
1451
+ let mut changes = StateChanges :: default ( ) ;
1452
+ client. decrypt_latest_events ( & mut room, & mut changes) . await ;
1453
+
1454
+ // Then nothing changed
1455
+ assert ! ( room. latest_encrypted_events( ) . is_empty( ) ) ;
1456
+ assert ! ( room. latest_event( ) . is_none( ) ) ;
1457
+ assert ! ( changes. room_infos. is_empty( ) ) ;
1458
+ }
1459
+
1460
+ // TODO: I wanted to write more tests here for decrypt_latest_events but I got
1461
+ // lost trying to set up my OlmMachine to be able to encrypt and decrypt
1462
+ // events. In the meantime, there are tests for the most difficult logic
1463
+ // inside Room. --andyb
1464
+
1465
+ async fn logged_in_client ( user_id : & UserId ) -> BaseClient {
1466
+ let client = BaseClient :: new ( ) ;
1467
+ client
1468
+ . set_session_meta ( SessionMeta {
1469
+ user_id : user_id. to_owned ( ) ,
1470
+ device_id : "FOOBAR" . into ( ) ,
1471
+ } )
1472
+ . await
1473
+ . expect ( "set_session_meta failed!" ) ;
1474
+ client
1475
+ }
1476
+
1477
+ #[ cfg( feature = "e2e-encryption" ) ]
1478
+ async fn process_room_join (
1479
+ client : & BaseClient ,
1480
+ room_id : & RoomId ,
1481
+ event_id : & str ,
1482
+ user_id : & UserId ,
1483
+ ) -> Room {
1484
+ let mut ev_builder = EventBuilder :: new ( ) ;
1485
+ let response = ev_builder
1486
+ . add_joined_room ( JoinedRoomBuilder :: new ( room_id) . add_timeline_event (
1487
+ TimelineTestEvent :: Custom ( json ! ( {
1488
+ "content" : {
1489
+ "displayname" : "Alice" ,
1490
+ "membership" : "join" ,
1491
+ } ,
1492
+ "event_id" : event_id,
1493
+ "origin_server_ts" : 1432135524678u64 ,
1494
+ "sender" : user_id,
1495
+ "state_key" : user_id,
1496
+ "type" : "m.room.member" ,
1497
+ } ) ) ,
1498
+ ) )
1499
+ . build_sync_response ( ) ;
1500
+ client. receive_sync_response ( response) . await . unwrap ( ) ;
1501
+
1502
+ client. get_room ( room_id) . expect ( "Just-created room not found!" )
1503
+ }
1387
1504
}
0 commit comments