feat: Enhance remote room leave handling

This commit is contained in:
timedout
2025-11-23 16:33:32 +00:00
committed by nex
parent 430200b60e
commit 7f4248a8c6
3 changed files with 60 additions and 21 deletions
+24 -6
View File
@@ -1,4 +1,7 @@
use std::{collections::BTreeMap, fmt::Write as _}; use std::{
collections::{BTreeMap, HashSet},
fmt::Write as _,
};
use api::client::{ use api::client::{
full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, remote_leave_room, 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 futures::{FutureExt, StreamExt};
use ruma::{ use ruma::{
OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId, OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, UserId,
events::{ events::{
RoomAccountDataEventType, StateEventType, RoomAccountDataEventType, StateEventType,
room::{ room::{
@@ -950,23 +953,38 @@ pub(super) async fn force_leave_remote_room(
&self, &self,
user_id: String, user_id: String,
room_id: OwnedRoomOrAliasId, room_id: OwnedRoomOrAliasId,
via: Option<String>,
) -> Result { ) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?; let user_id = parse_local_user_id(self.services, &user_id)?;
let (room_id, _) = self let (room_id, vias_raw) = self
.services .services
.rooms .rooms
.alias .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?; .await?;
assert!( assert!(
self.services.globals.user_is_local(&user_id), self.services.globals.user_is_local(&user_id),
"Parsed user_id must be a local user" "Parsed user_id must be a local user"
); );
remote_leave_room(self.services, &user_id, &room_id, None) let mut vias: HashSet<OwnedServerName> = 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() .boxed()
.await?; .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 .await
} }
+1
View File
@@ -107,6 +107,7 @@ pub enum UserCommand {
ForceLeaveRemoteRoom { ForceLeaveRemoteRoom {
user_id: String, user_id: String,
room_id: OwnedRoomOrAliasId, room_id: OwnedRoomOrAliasId,
via: Option<String>,
}, },
/// - Forces the specified user to drop their power levels to the room /// - Forces the specified user to drop their power levels to the room
+35 -15
View File
@@ -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 // 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 dont_have_room.and(not_knocked).await {
if let Err(e) = remote_leave_room(services, user_id, room_id, reason.clone()) if let Err(e) =
.boxed() remote_leave_room(services, user_id, room_id, reason.clone(), HashSet::new())
.await .boxed()
.await
{ {
warn!(%user_id, "Failed to leave room {room_id} remotely: {e}"); warn!(%user_id, "Failed to leave room {room_id} remotely: {e}");
// Don't tell the client about this error // Don't tell the client about this error
@@ -215,22 +216,25 @@ pub async fn leave_room(
Ok(()) Ok(())
} }
pub async fn remote_leave_room( pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
services: &Services, services: &Services,
user_id: &UserId, user_id: &UserId,
room_id: &RoomId, room_id: &RoomId,
reason: Option<String>, reason: Option<String>,
mut servers: HashSet<OwnedServerName, S>,
) -> Result<()> { ) -> Result<()> {
let mut make_leave_response_and_server = let mut make_leave_response_and_server =
Err!(BadServerResponse("No remote server available to assist in leaving {room_id}.")); Err!(BadServerResponse("No remote server available to assist in leaving {room_id}."));
let mut servers: HashSet<OwnedServerName> = services servers.extend(
.rooms services
.state_cache .rooms
.servers_invite_via(room_id) .state_cache
.map(ToOwned::to_owned) .servers_invite_via(room_id)
.collect() .map(ToOwned::to_owned)
.await; .collect::<HashSet<OwnedServerName>>()
.await,
);
match services match services
.rooms .rooms
@@ -277,6 +281,11 @@ pub async fn remote_leave_room(
if let Some(room_id_server_name) = room_id.server_name() { if let Some(room_id_server_name) = room_id.server_name() {
servers.insert(room_id_server_name.to_owned()); 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:?}"); debug_info!("servers in remote_leave_room: {servers:?}");
@@ -284,7 +293,7 @@ pub async fn remote_leave_room(
let make_leave_response = services let make_leave_response = services
.sending .sending
.send_federation_request( .send_federation_request(
&remote_server, remote_server.as_ref(),
federation::membership::prepare_leave_event::v1::Request { federation::membership::prepare_leave_event::v1::Request {
room_id: room_id.to_owned(), room_id: room_id.to_owned(),
user_id: user_id.to_owned(), user_id: user_id.to_owned(),
@@ -292,11 +301,21 @@ pub async fn remote_leave_room(
) )
.await; .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() { if make_leave_response_and_server.is_ok() {
debug_info!(
"Received make_leave_response from {} for leaving {room_id}",
remote_server
);
break; 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?; 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 { let Some(room_version_id) = make_leave_response.room_version else {
return Err!(BadServerResponse(warn!( return Err!(BadServerResponse(warn!(
"No room version was returned by {remote_server} for {room_id}, room version is \ "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) { if !services.server.supported_room_version(&room_version_id) {
return Err!(BadServerResponse(warn!( 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",
))); )));
} }