mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-06-17 12:48:24 -04:00
fix(sdk): correctly import e2ee history in join_room_by_id
It turns out that downstream clients can and do call `Client::join_room_by_id()` rather than `Room::join`, so we need to do the room key history import in the lower-level method.
This commit is contained in:
@@ -6,8 +6,14 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
- Add logging to `Room::join`.
|
||||
([#5260](https://github.com/matrix-org/matrix-rust-sdk/pull/5260))
|
||||
### Bug fixes
|
||||
|
||||
- When joining a room via `Client::join_room_by_id()`, if the client has `enable_share_history_on_invite` enabled,
|
||||
we will correctly check for received room key bundles. Previously this was only done when calling `Room::join`.
|
||||
([#5043](https://github.com/matrix-org/matrix-rust-sdk/pull/5043))
|
||||
|
||||
### Refactor
|
||||
|
||||
- `ClientServerCapabilities` has been renamed to `ClientServerInfo`. Alongside this,
|
||||
`Client::reset_server_info` is now `Client::reset_server_info` and `Client::fetch_server_capabilities`
|
||||
is now `Client::fetch_server_versions`, returning the server versions response directly.
|
||||
|
||||
@@ -93,6 +93,7 @@ use crate::{
|
||||
http_client::HttpClient,
|
||||
media::MediaError,
|
||||
notification_settings::NotificationSettings,
|
||||
room::RoomMember,
|
||||
room_preview::RoomPreview,
|
||||
send_queue::SendQueueData,
|
||||
sliding_sync::Version as SlidingSyncVersion,
|
||||
@@ -1429,11 +1430,42 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare to join a room by ID, by getting the current details about it
|
||||
async fn prepare_join_room_by_id(&self, room_id: &RoomId) -> Option<PreJoinRoomInfo> {
|
||||
let Some(room) = self.get_room(room_id) else {
|
||||
// We know nothing about this room.
|
||||
return None;
|
||||
};
|
||||
|
||||
let inviter = match room.invite_details().await {
|
||||
Ok(details) => details.inviter,
|
||||
Err(Error::WrongRoomState(_)) => None,
|
||||
Err(e) => {
|
||||
warn!("Error fetching invite details for room: {e:?}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Some(PreJoinRoomInfo { inviter })
|
||||
}
|
||||
|
||||
/// Finish joining a room.
|
||||
///
|
||||
/// If the room was an invite that should be marked as a DM, will include it
|
||||
/// in the DM event after creating the joined room.
|
||||
async fn finish_join_room(&self, room_id: &RoomId) -> Result<Room> {
|
||||
///
|
||||
/// If encrypted history sharing is enabled, will check to see if we have a
|
||||
/// key bundle, and import it if so.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id` - The `RoomId` of the room that was joined.
|
||||
/// * `pre_join_room_info` - Information about the room before we joined.
|
||||
async fn finish_join_room(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
pre_join_room_info: Option<PreJoinRoomInfo>,
|
||||
) -> Result<Room> {
|
||||
let mark_as_dm = if let Some(room) = self.get_room(room_id) {
|
||||
room.state() == RoomState::Invited
|
||||
&& room.is_direct().await.unwrap_or_else(|e| {
|
||||
@@ -1451,6 +1483,16 @@ impl Client {
|
||||
room.set_is_direct(true).await?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
if self.inner.enable_share_history_on_invite {
|
||||
if let Some(inviter) =
|
||||
pre_join_room_info.as_ref().and_then(|info| info.inviter.as_ref())
|
||||
{
|
||||
crate::room::shared_room_history::maybe_accept_key_bundle(&room, inviter.user_id())
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(room)
|
||||
}
|
||||
|
||||
@@ -1461,10 +1503,15 @@ impl Client {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id` - The `RoomId` of the room to be joined.
|
||||
#[instrument(skip(self))]
|
||||
pub async fn join_room_by_id(&self, room_id: &RoomId) -> Result<Room> {
|
||||
// See who invited us to this room, if anyone. Note we have to do this before
|
||||
// making the `/join` request, otherwise we could race against the sync.
|
||||
let pre_join_info = self.prepare_join_room_by_id(room_id).await;
|
||||
|
||||
let request = join_room_by_id::v3::Request::new(room_id.to_owned());
|
||||
let response = self.send(request).await?;
|
||||
self.finish_join_room(&response.room_id).await
|
||||
self.finish_join_room(&response.room_id, pre_join_info).await
|
||||
}
|
||||
|
||||
/// Join a room by `RoomOrAliasId`.
|
||||
@@ -1486,7 +1533,7 @@ impl Client {
|
||||
via: server_names.to_owned(),
|
||||
});
|
||||
let response = self.send(request).await?;
|
||||
self.finish_join_room(&response.room_id).await
|
||||
self.finish_join_room(&response.room_id, None).await
|
||||
}
|
||||
|
||||
/// Search the homeserver's directory of public rooms.
|
||||
@@ -2730,6 +2777,13 @@ impl<Value> CachedValue<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the state of a room before we joined it.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct PreJoinRoomInfo {
|
||||
/// The user that invited us to the room, if any
|
||||
pub inviter: Option<RoomMember>,
|
||||
}
|
||||
|
||||
// The http mocking library is not supported for wasm32
|
||||
#[cfg(all(test, not(target_family = "wasm")))]
|
||||
pub(crate) mod tests {
|
||||
|
||||
@@ -176,7 +176,7 @@ pub mod reply;
|
||||
pub mod privacy_settings;
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
mod shared_room_history;
|
||||
pub(crate) mod shared_room_history;
|
||||
|
||||
/// A struct containing methods that are common for Joined, Invited and Left
|
||||
/// Rooms
|
||||
@@ -375,7 +375,6 @@ impl Room {
|
||||
///
|
||||
/// Only invited and left rooms can be joined via this method.
|
||||
#[doc(alias = "accept_invitation")]
|
||||
#[instrument(skip_all, fields(room_id = ?self.inner.room_id()))]
|
||||
pub async fn join(&self) -> Result<()> {
|
||||
let prev_room_state = self.inner.state();
|
||||
|
||||
@@ -386,40 +385,8 @@ impl Room {
|
||||
))));
|
||||
}
|
||||
|
||||
let inviter = if prev_room_state == RoomState::Invited {
|
||||
match self.invite_details().await {
|
||||
Ok(details) => details.inviter,
|
||||
Err(e) => {
|
||||
warn!("No invite details were found, can't attempt to find a room key bundle to accept: {e:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
let enable_share_history_on_invite = self.client.inner.enable_share_history_on_invite;
|
||||
|
||||
#[cfg(not(feature = "e2e-encryption"))]
|
||||
let enable_share_history_on_invite = false;
|
||||
|
||||
debug!(
|
||||
?prev_room_state,
|
||||
inviter=?inviter.as_ref().map(|room_member| room_member.user_id()),
|
||||
enable_share_history_on_invite,
|
||||
"Joining room",
|
||||
);
|
||||
|
||||
self.client.join_room_by_id(self.room_id()).await?;
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
if enable_share_history_on_invite {
|
||||
if let Some(inviter) = inviter {
|
||||
shared_room_history::maybe_accept_key_bundle(self, inviter.user_id()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ pub(super) async fn share_room_history(room: &Room, user_id: OwnedUserId) -> Res
|
||||
///
|
||||
/// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
|
||||
#[instrument(skip(room), fields(room_id = ?room.room_id(), bundle_sender))]
|
||||
pub(super) async fn maybe_accept_key_bundle(room: &Room, inviter: &UserId) -> Result<()> {
|
||||
pub(crate) async fn maybe_accept_key_bundle(room: &Room, inviter: &UserId) -> Result<()> {
|
||||
// TODO: retry this if it gets interrupted or it fails.
|
||||
// TODO: do this in the background.
|
||||
|
||||
|
||||
@@ -86,10 +86,10 @@ async fn test_history_share_on_invite() -> Result<()> {
|
||||
"io.element.msc4268.room_key_bundle"
|
||||
);
|
||||
|
||||
let bob_room = bob.get_room(alice_room.room_id()).expect("Bob should have received the invite");
|
||||
bob.get_room(alice_room.room_id()).expect("Bob should have received the invite");
|
||||
|
||||
bob_room
|
||||
.join()
|
||||
let bob_room = bob
|
||||
.join_room_by_id(alice_room.room_id())
|
||||
.instrument(bob_span.clone())
|
||||
.await
|
||||
.expect("Bob should be able to accept the invitation from Alice");
|
||||
|
||||
Reference in New Issue
Block a user