diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 418105e4..2cf344f6 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -1,4 +1,7 @@ -use std::{collections::BTreeMap, fmt::Write as _}; +use std::{ + collections::{BTreeMap, HashSet}, + fmt::Write as _, +}; use api::client::{ full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, remote_leave_room, @@ -12,7 +15,7 @@ use conduwuit::{ }; use futures::{FutureExt, StreamExt}; use ruma::{ - OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId, + OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, UserId, events::{ RoomAccountDataEventType, StateEventType, room::{ @@ -950,23 +953,38 @@ pub(super) async fn force_leave_remote_room( &self, user_id: String, room_id: OwnedRoomOrAliasId, + via: Option, ) -> Result { let user_id = parse_local_user_id(self.services, &user_id)?; - let (room_id, _) = self + let (room_id, vias_raw) = self .services .rooms .alias - .resolve_with_servers(&room_id, None) + .resolve_with_servers( + &room_id, + if let Some(v) = via.clone() { + Some(vec![OwnedServerName::parse(v)?]) + } else { + None + }, + ) .await?; assert!( self.services.globals.user_is_local(&user_id), "Parsed user_id must be a local user" ); - remote_leave_room(self.services, &user_id, &room_id, None) + let mut vias: HashSet = HashSet::new(); + if let Some(via) = via { + vias.insert(OwnedServerName::parse(via)?); + } + for server in vias_raw { + vias.insert(server); + } + remote_leave_room(self.services, &user_id, &room_id, None, vias) .boxed() .await?; - self.write_str(&format!("{user_id} has been joined to {room_id}.",)) + self.write_str(&format!("{user_id} successfully left {room_id} via remote server.")) .await } diff --git a/src/admin/user/mod.rs b/src/admin/user/mod.rs index 366f7dd5..c1067bf5 100644 --- a/src/admin/user/mod.rs +++ b/src/admin/user/mod.rs @@ -107,6 +107,7 @@ pub enum UserCommand { ForceLeaveRemoteRoom { user_id: String, room_id: OwnedRoomOrAliasId, + via: Option, }, /// - Forces the specified user to drop their power levels to the room diff --git a/src/api/client/membership/leave.rs b/src/api/client/membership/leave.rs index 5af9abc2..51223fa9 100644 --- a/src/api/client/membership/leave.rs +++ b/src/api/client/membership/leave.rs @@ -131,9 +131,10 @@ pub async fn leave_room( // Ask a remote server if we don't have this room and are not knocking on it if dont_have_room.and(not_knocked).await { - if let Err(e) = remote_leave_room(services, user_id, room_id, reason.clone()) - .boxed() - .await + if let Err(e) = + remote_leave_room(services, user_id, room_id, reason.clone(), HashSet::new()) + .boxed() + .await { warn!(%user_id, "Failed to leave room {room_id} remotely: {e}"); // Don't tell the client about this error @@ -215,22 +216,25 @@ pub async fn leave_room( Ok(()) } -pub async fn remote_leave_room( +pub async fn remote_leave_room( services: &Services, user_id: &UserId, room_id: &RoomId, reason: Option, + mut servers: HashSet, ) -> Result<()> { let mut make_leave_response_and_server = Err!(BadServerResponse("No remote server available to assist in leaving {room_id}.")); - let mut servers: HashSet = services - .rooms - .state_cache - .servers_invite_via(room_id) - .map(ToOwned::to_owned) - .collect() - .await; + servers.extend( + services + .rooms + .state_cache + .servers_invite_via(room_id) + .map(ToOwned::to_owned) + .collect::>() + .await, + ); match services .rooms @@ -277,6 +281,11 @@ pub async fn remote_leave_room( if let Some(room_id_server_name) = room_id.server_name() { servers.insert(room_id_server_name.to_owned()); } + if servers.is_empty() { + return Err!(BadServerResponse(warn!( + "No remote servers found to assist in leaving {room_id}." + ))); + } debug_info!("servers in remote_leave_room: {servers:?}"); @@ -284,7 +293,7 @@ pub async fn remote_leave_room( let make_leave_response = services .sending .send_federation_request( - &remote_server, + remote_server.as_ref(), federation::membership::prepare_leave_event::v1::Request { room_id: room_id.to_owned(), user_id: user_id.to_owned(), @@ -292,11 +301,21 @@ pub async fn remote_leave_room( ) .await; - make_leave_response_and_server = make_leave_response.map(|r| (r, remote_server)); + let error = make_leave_response.as_ref().err().map(|e| e.to_string()); + make_leave_response_and_server = make_leave_response.map(|r| (r, remote_server.clone())); if make_leave_response_and_server.is_ok() { + debug_info!( + "Received make_leave_response from {} for leaving {room_id}", + remote_server + ); break; } + debug_warn!( + "Failed to get make_leave_response from {} for leaving {room_id}: {}", + remote_server, + error.unwrap() + ); } let (make_leave_response, remote_server) = make_leave_response_and_server?; @@ -304,13 +323,14 @@ pub async fn remote_leave_room( let Some(room_version_id) = make_leave_response.room_version else { return Err!(BadServerResponse(warn!( "No room version was returned by {remote_server} for {room_id}, room version is \ - likely not supported by conduwuit" + likely not supported by continuwuity" ))); }; if !services.server.supported_room_version(&room_version_id) { return Err!(BadServerResponse(warn!( - "Remote room version {room_version_id} for {room_id} is not supported by conduwuit", + "Remote room version {room_version_id} for {room_id} is not supported by \ + continuwuity", ))); }