@@ -107,24 +107,16 @@ pub mod sync {
107
107
}
108
108
109
109
AnySyncStateEvent :: RoomCreate ( create) => {
110
- if super :: is_create_event_valid (
110
+ let edited_create = super :: validate_create_event_predecessor (
111
111
context,
112
112
room_info. room_id ( ) ,
113
113
create,
114
114
state_store,
115
- ) {
116
- room_info. handle_state_event ( event) ;
117
- } else {
118
- error ! (
119
- room_id = ?room_info. room_id( ) ,
120
- ?create,
121
- "`m.create.tombstone` event is invalid, it creates a loop"
122
- ) ;
115
+ ) ;
123
116
124
- // Do not add the event to `room_info`.
125
- // Do not add the event to `context.state_changes.state`.
126
- continue ;
127
- }
117
+ room_info. handle_state_event (
118
+ edited_create. map ( Into :: into) . as_ref ( ) . unwrap_or ( event) ,
119
+ ) ;
128
120
}
129
121
130
122
AnySyncStateEvent :: RoomTombstone ( tombstone) => {
@@ -317,31 +309,47 @@ where
317
309
. unzip ( )
318
310
}
319
311
320
- /// Check if `m.room.create` isn't creating a loop of rooms.
321
- pub fn is_create_event_valid (
312
+ /// Check if the `predecessor` in `m.room.create` isn't creating a loop of
313
+ /// rooms.
314
+ ///
315
+ /// If it is, we return a clone of the event with the predecessor removed.
316
+ pub fn validate_create_event_predecessor (
322
317
context : & mut Context ,
323
318
room_id : & RoomId ,
324
319
event : & SyncStateEvent < RoomCreateEventContent > ,
325
320
state_store : & BaseStateStore ,
326
- ) -> bool {
321
+ ) -> Option < SyncStateEvent < RoomCreateEventContent > > {
327
322
let mut already_seen = BTreeSet :: new ( ) ;
328
323
already_seen. insert ( room_id. to_owned ( ) ) ;
329
324
330
- let Some ( mut predecessor_room_id) = event
331
- . as_original ( )
332
- . and_then ( |event| Some ( event. content . predecessor . as_ref ( ) ?. room_id . clone ( ) ) )
325
+ // Redacted and non-redacted create events use the same content type.
326
+ let content = match event {
327
+ SyncStateEvent :: Original ( event) => & event. content ,
328
+ SyncStateEvent :: Redacted ( event) => & event. content ,
329
+ } ;
330
+
331
+ let Some ( mut predecessor_room_id) =
332
+ content. predecessor . as_ref ( ) . map ( |predecessor| predecessor. room_id . clone ( ) )
333
333
else {
334
- // `true` means no problem. No predecessor = no problem here.
335
- return true ;
334
+ // No predecessor = no problem here.
335
+ return None ;
336
336
} ;
337
337
338
338
loop {
339
339
// We must check immediately if the `predecessor_room_id` is in `already_seen`
340
340
// in case of a room is created and marks itself as its predecessor in a single
341
341
// sync.
342
- if already_seen. contains ( AsRef :: < RoomId > :: as_ref ( & predecessor_room_id) ) {
342
+ if already_seen. contains ( & predecessor_room_id) {
343
343
// Ahhh, there is a loop with `m.room.create` events!
344
- return false ;
344
+ // We remove the predecessor so that we don't process it later.
345
+ let mut event = event. clone ( ) ;
346
+
347
+ match & mut event {
348
+ SyncStateEvent :: Original ( event) => event. content . predecessor . take ( ) ,
349
+ SyncStateEvent :: Redacted ( event) => event. content . predecessor . take ( ) ,
350
+ } ;
351
+
352
+ return Some ( event) ;
345
353
}
346
354
347
355
already_seen. insert ( predecessor_room_id. clone ( ) ) ;
@@ -366,7 +374,7 @@ pub fn is_create_event_valid(
366
374
predecessor_room_id = next_predecessor_room_id;
367
375
}
368
376
369
- true
377
+ None
370
378
}
371
379
372
380
/// Check if `m.room.tombstone` isn't creating a loop of rooms.
@@ -420,6 +428,7 @@ pub fn is_tombstone_event_valid(
420
428
421
429
#[ cfg( test) ]
422
430
mod tests {
431
+ use assert_matches2:: assert_matches;
423
432
use matrix_sdk_test:: {
424
433
DEFAULT_TEST_ROOM_ID , JoinedRoomBuilder , StateTestEvent , SyncResponseBuilder , async_test,
425
434
event_factory:: EventFactory ,
@@ -920,11 +929,12 @@ mod tests {
920
929
// The sync doesn't fail but…
921
930
assert ! ( client. receive_sync_response( response) . await . is_ok( ) ) ;
922
931
923
- // … the state event has not been saved.
932
+ // … the predecessor has not been saved.
924
933
let room_0 = client. get_room ( room_id_0) . unwrap ( ) ;
925
934
926
935
assert ! ( room_0. predecessor_room( ) . is_none( ) , "room 0 must not have a predecessor" ) ;
927
936
assert ! ( room_0. successor_room( ) . is_none( ) , "room 0 must not have a successor" ) ;
937
+ assert_matches ! ( room_0. create_content( ) , Some ( _) , "room 0 must have a create content" ) ;
928
938
}
929
939
}
930
940
@@ -961,11 +971,12 @@ mod tests {
961
971
// The sync doesn't fail but…
962
972
assert ! ( client. receive_sync_response( response) . await . is_ok( ) ) ;
963
973
964
- // … the state event has not been saved.
974
+ // … the tombstone event and the predecessor have not been saved.
965
975
let room_0 = client. get_room ( room_id_0) . unwrap ( ) ;
966
976
967
977
assert ! ( room_0. predecessor_room( ) . is_none( ) , "room 0 must not have a predecessor" ) ;
968
978
assert ! ( room_0. successor_room( ) . is_none( ) , "room 0 must not have a successor" ) ;
979
+ assert_matches ! ( room_0. create_content( ) , Some ( _) , "room 0 must have a create content" ) ;
969
980
}
970
981
}
971
982
@@ -1070,6 +1081,7 @@ mod tests {
1070
1081
// this state event is missing because it creates a loop
1071
1082
assert ! ( room_2. predecessor_room( ) . is_none( ) , "room 2 must not have a predecessor" ) ;
1072
1083
assert ! ( room_2. successor_room( ) . is_none( ) , "room 2 must not have a successor" , ) ;
1084
+ assert_matches ! ( room_2. create_content( ) , Some ( _) , "room 2 must have a create content" ) ;
1073
1085
}
1074
1086
}
1075
1087
0 commit comments