diff --git a/src/service/membership/join.rs b/src/service/membership/join.rs index 84b02237..f464c7c4 100644 --- a/src/service/membership/join.rs +++ b/src/service/membership/join.rs @@ -168,6 +168,7 @@ pub async fn join_remote( let room_version_rules = room_version::rules(&room_version_id)?; let (mut join_event, event_id, join_authorized_via_users_server) = self .create_join_event( + room_id, sender_user, &make_join_response.event, &room_version_id, @@ -636,6 +637,7 @@ pub async fn join_local( let room_version_rules = room_version::rules(&room_version_id)?; let (join_event, event_id, _) = self .create_join_event( + room_id, sender_user, &make_join_response.event, &room_version_id, @@ -689,6 +691,7 @@ pub async fn join_local( #[tracing::instrument(name = "make_join", level = "debug", skip_all)] async fn create_join_event( &self, + room_id: &RoomId, sender_user: &UserId, join_event_stub: &RawJsonValue, room_version_id: &RoomVersionId, @@ -711,22 +714,6 @@ async fn create_join_event( }) .and_then(|s| OwnedUserId::try_from(s.as_str().unwrap_or_default()).ok()); - event.insert( - "origin".into(), - CanonicalJsonValue::String( - self.services - .globals - .server_name() - .as_str() - .to_owned(), - ), - ); - - event.insert( - "origin_server_ts".into(), - CanonicalJsonValue::Integer(utils::millis_since_unix_epoch().try_into()?), - ); - let displayname = self.services.users.displayname(sender_user).ok(); let avatar_url = self.services.users.avatar_url(sender_user).ok(); @@ -747,6 +734,30 @@ async fn create_join_event( })?, ); + event.insert( + "origin".into(), + CanonicalJsonValue::String( + self.services + .globals + .server_name() + .as_str() + .to_owned(), + ), + ); + + event.insert( + "origin_server_ts".into(), + CanonicalJsonValue::Integer(utils::millis_since_unix_epoch().try_into()?), + ); + + event.insert("room_id".into(), CanonicalJsonValue::String(room_id.as_str().into())); + + event.insert("sender".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + event.insert("state_key".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + event.insert("type".into(), CanonicalJsonValue::String("m.room.member".into())); + let event_id = self .services .server_keys diff --git a/src/service/membership/knock.rs b/src/service/membership/knock.rs index f7b90e6b..8c73db38 100644 --- a/src/service/membership/knock.rs +++ b/src/service/membership/knock.rs @@ -257,6 +257,17 @@ async fn knock_room_helper_local( .expect("event is valid, we just created it"), ); + knock_event_stub + .insert("room_id".into(), CanonicalJsonValue::String(room_id.as_str().into())); + + knock_event_stub + .insert("state_key".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + knock_event_stub + .insert("sender".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + knock_event_stub.insert("type".into(), CanonicalJsonValue::String("m.room.member".into())); + // In order to create a compatible ref hash (EventID) the `hashes` field needs // to be present self.services @@ -419,6 +430,17 @@ async fn knock_room_helper_remote( .expect("event is valid, we just created it"), ); + knock_event_stub + .insert("room_id".into(), CanonicalJsonValue::String(room_id.as_str().into())); + + knock_event_stub + .insert("state_key".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + knock_event_stub + .insert("sender".into(), CanonicalJsonValue::String(sender_user.as_str().into())); + + knock_event_stub.insert("type".into(), CanonicalJsonValue::String("m.room.member".into())); + // In order to create a compatible ref hash (EventID) the `hashes` field needs // to be present self.services diff --git a/src/service/membership/leave.rs b/src/service/membership/leave.rs index 662ebfe3..67a84e43 100644 --- a/src/service/membership/leave.rs +++ b/src/service/membership/leave.rs @@ -1,9 +1,14 @@ use std::collections::HashSet; -use futures::{FutureExt, StreamExt, TryFutureExt, future::ready, pin_mut}; +use futures::{ + FutureExt, StreamExt, TryFutureExt, + future::{join3, ready}, + pin_mut, +}; use ruma::{ CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, UserId, api::federation, + canonical_json::to_canonical_value, events::{ StateEventType, room::member::{MembershipState, RoomMemberEventContent}, @@ -14,7 +19,10 @@ use tuwunel_core::{ matrix::{PduCount, room_version}, pdu::PduBuilder, state_res, - utils::{self, FutureBoolExt, future::ReadyBoolExt}, + utils::{ + self, FutureBoolExt, + future::{ReadyBoolExt, TryExtExt}, + }, warn, }; @@ -96,7 +104,11 @@ pub async fn leave( // Ask a remote server if we don't have this room and are not knocking on it if remote_leave_now || dont_have_room.and(not_knocked).await { - if let Err(e) = self.remote_leave(user_id, room_id).boxed().await { + if let Err(e) = self + .remote_leave(user_id, room_id, reason) + .boxed() + .await + { warn!(%user_id, "Failed to leave room {room_id} remotely: {e}"); // Don't tell the client about this error } @@ -178,7 +190,12 @@ pub async fn leave( #[implement(Service)] #[tracing::instrument(name = "remote", level = "debug", skip_all)] -async fn remote_leave(&self, user_id: &UserId, room_id: &RoomId) -> Result { +async fn remote_leave( + &self, + user_id: &UserId, + room_id: &RoomId, + reason: Option, +) -> Result { let mut make_leave_response_and_server = Err!(BadServerResponse("No remote server available to assist in leaving {room_id}.")); @@ -281,17 +298,34 @@ async fn remote_leave(&self, user_id: &UserId, room_id: &RoomId) -> Result { let room_version_rules = room_version::rules(&room_version_id)?; - let mut leave_event_stub = serde_json::from_str::( - make_leave_response.event.get(), - ) - .map_err(|e| { - err!(BadServerResponse(warn!( - "Invalid make_leave event json received from {remote_server} for {room_id}: {e:?}" - ))) - })?; + let mut event = serde_json::from_str::(make_leave_response.event.get()) + .map_err(|e| { + err!(BadServerResponse(warn!( + "Invalid make_leave event json received from {remote_server} for {room_id}: \ + {e:?}" + ))) + })?; - // TODO: Is origin needed? - leave_event_stub.insert( + let displayname = self.services.users.displayname(user_id).ok(); + + let avatar_url = self.services.users.avatar_url(user_id).ok(); + + let blurhash = self.services.users.blurhash(user_id).ok(); + + let (displayname, avatar_url, blurhash) = join3(displayname, avatar_url, blurhash).await; + + event.insert( + "content".into(), + to_canonical_value(RoomMemberEventContent { + displayname, + avatar_url, + blurhash, + reason, + ..RoomMemberEventContent::new(MembershipState::Leave) + })?, + ); + + event.insert( "origin".into(), CanonicalJsonValue::String( self.services @@ -301,24 +335,26 @@ async fn remote_leave(&self, user_id: &UserId, room_id: &RoomId) -> Result { .to_owned(), ), ); - leave_event_stub.insert( + + event.insert( "origin_server_ts".into(), - CanonicalJsonValue::Integer( - utils::millis_since_unix_epoch() - .try_into() - .expect("Timestamp is valid js_int value"), - ), + CanonicalJsonValue::Integer(utils::millis_since_unix_epoch().try_into()?), ); + event.insert("room_id".into(), CanonicalJsonValue::String(room_id.as_str().into())); + + event.insert("state_key".into(), CanonicalJsonValue::String(user_id.as_str().into())); + + event.insert("sender".into(), CanonicalJsonValue::String(user_id.as_str().into())); + + event.insert("type".into(), CanonicalJsonValue::String("m.room.member".into())); + let event_id = self .services .server_keys - .gen_id_hash_and_sign_event(&mut leave_event_stub, &room_version_id)?; + .gen_id_hash_and_sign_event(&mut event, &room_version_id)?; - state_res::check_pdu_format(&leave_event_stub, &room_version_rules.event_format)?; - - // It has enough fields to be called a proper event now - let leave_event = leave_event_stub; + state_res::check_pdu_format(&event, &room_version_rules.event_format)?; self.services .federation @@ -328,7 +364,7 @@ async fn remote_leave(&self, user_id: &UserId, room_id: &RoomId) -> Result { pdu: self .services .federation - .format_pdu_into(leave_event.clone(), Some(&room_version_id)) + .format_pdu_into(event.clone(), Some(&room_version_id)) .await, }) .await?;