diff --git a/book/changelog.md b/book/changelog.md index 8426be1a..3f12e7d0 100644 --- a/book/changelog.md +++ b/book/changelog.md @@ -327,3 +327,5 @@ This will be the first release of Grapevine since it was forked from Conduit ([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124)) 26. Allow adding canonical aliases from remote servers. ([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158)) +27. Transfer local canonical aliases to the new room when upgrading a room. + ([!186](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/186)) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index ebb5a26b..5f1dd87a 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -777,6 +777,20 @@ pub(crate) async fn upgrade_room_route( } // Moves any local aliases to the new room + let mut old_canonical_alias: RoomCanonicalAliasEventContent = services() + .rooms + .state_accessor + .room_state_get(&body.room_id, &StateEventType::RoomCanonicalAlias, "")? + .and_then(|event| { + serde_json::from_str(event.content.get()) + .inspect_err(|_| { + Error::bad_database("invalid m.room.canonical_alias event"); + }) + .ok() + }) + .unwrap_or_default(); + let mut new_canonical_alias = RoomCanonicalAliasEventContent::default(); + for alias in services() .rooms .alias @@ -788,6 +802,67 @@ pub(crate) async fn upgrade_room_route( &replacement_room, sender_user, )?; + + if old_canonical_alias.alias.as_ref() == Some(&alias) { + new_canonical_alias.alias = Some(alias.clone()); + old_canonical_alias.alias = None; + } + let mut is_alt_alias = false; + old_canonical_alias.alt_aliases.retain(|a| { + let equal = a == &alias; + is_alt_alias |= equal; + !equal + }); + if is_alt_alias { + new_canonical_alias.alt_aliases.push(alias); + } + } + + // Send updated events to both rooms if we moved any canonical aliases + let new_canonical_alias_empty = new_canonical_alias.alias.is_none() + && new_canonical_alias.alt_aliases.is_empty(); + if !new_canonical_alias_empty { + if let Err(error) = services() + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomCanonicalAlias, + content: to_raw_value(&old_canonical_alias) + .expect("event is valid"), + unsigned: None, + state_key: Some(String::new()), + redacts: None, + }, + sender_user, + &original_room_token, + ) + .await + { + // The sender may not have had permission to send + // m.room.canonical_alias in the old room. Don't treat + // this as fatal. + warn!( + %error, + "Failed to remove local canonical aliases from old room" + ); + } + services() + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomCanonicalAlias, + content: to_raw_value(&new_canonical_alias) + .expect("event is valid"), + unsigned: None, + state_key: Some(String::new()), + redacts: None, + }, + sender_user, + &replacement_room_token, + ) + .await?; } // Get the old room power levels