From 609e239436096309a940c238285512bbcf6cbae4 Mon Sep 17 00:00:00 2001 From: Tom Foster Date: Sat, 30 Aug 2025 16:10:41 +0100 Subject: [PATCH 1/4] fix(fedora): Correct linting issues in RPM spec file The Fedora RPM packaging files added in PR #950 weren't passing pre-commit checks, causing CI failures for any branches rebased after that merge. This applies prek linting fixes (typo correction, trailing whitespace removal, and EOF newline) to ensure CI passes for all contributors. --- fedora/continuwuity.spec.rpkg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fedora/continuwuity.spec.rpkg b/fedora/continuwuity.spec.rpkg index 19edf701..f2efa383 100644 --- a/fedora/continuwuity.spec.rpkg +++ b/fedora/continuwuity.spec.rpkg @@ -38,7 +38,7 @@ sed -i 's/^offline = true$//' .cargo/config.toml %cargo_build # Here's the one legally required mystery incantation in this file. -# Some of our dependencies have source files which are (for some reason) marked as excutable. +# Some of our dependencies have source files which are (for some reason) marked as executable. # Files in .cargo/registry/ are copied into /usr/src/ by the debuginfo machinery # at the end of the build step, and then the BRP shebang mangling script checks # the entire buildroot to find executable files, and fails the build because @@ -46,7 +46,7 @@ sed -i 's/^offline = true$//' .cargo/config.toml # So we have to clear the executable bit on all of them before that happens. find .cargo/registry/ -executable -name "*.rs" -exec chmod -x {} + -# TODO: this fails currently because it's forced to run in offline mode +# TODO: this fails currently because it's forced to run in offline mode # {cargo_license -- --no-dev} > LICENSE.dependencies %install @@ -77,4 +77,4 @@ install -Dpm0644 conduwuit-example.toml %{buildroot}%{_sysconfdir}/conduwuit/con %systemd_postun_with_restart conduwuit.service %changelog -{{{ git_repo_changelog }}} \ No newline at end of file +{{{ git_repo_changelog }}} From af33e4053977ef19148e4d1b0bdeb0dbff246d7a Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Wed, 30 Jul 2025 19:19:32 +0100 Subject: [PATCH 2/4] feat: Force leave remote rooms admin command --- src/admin/user/commands.rs | 30 ++++++++++++++++++++++++++++-- src/admin/user/mod.rs | 6 ++++++ src/api/client/membership/leave.rs | 2 +- src/api/client/membership/mod.rs | 2 +- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 56864a32..37ab030c 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -1,8 +1,8 @@ use std::{collections::BTreeMap, fmt::Write as _}; use api::client::{ - full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, update_avatar_url, - update_displayname, + full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, remote_leave_room, + update_avatar_url, update_displayname, }; use conduwuit::{ Err, Result, debug, debug_warn, error, info, is_equal_to, @@ -926,3 +926,29 @@ pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result { )) .await } + +#[admin_command] +pub(super) async fn force_leave_remote_room( + &self, + user_id: String, + room_id: OwnedRoomOrAliasId, +) -> Result { + let user_id = parse_local_user_id(self.services, &user_id)?; + let (room_id, _) = self + .services + .rooms + .alias + .resolve_with_servers(&room_id, 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) + .boxed() + .await?; + + self.write_str(&format!("{user_id} has been joined to {room_id}.",)) + .await +} diff --git a/src/admin/user/mod.rs b/src/admin/user/mod.rs index 656cacaf..366f7dd5 100644 --- a/src/admin/user/mod.rs +++ b/src/admin/user/mod.rs @@ -103,6 +103,12 @@ pub enum UserCommand { room_id: OwnedRoomOrAliasId, }, + /// - Manually leave a remote room for a local user. + ForceLeaveRemoteRoom { + user_id: String, + room_id: OwnedRoomOrAliasId, + }, + /// - Forces the specified user to drop their power levels to the room /// default, if their permissions allow and the auth check permits ForceDemote { diff --git a/src/api/client/membership/leave.rs b/src/api/client/membership/leave.rs index f4f1666b..0aadd833 100644 --- a/src/api/client/membership/leave.rs +++ b/src/api/client/membership/leave.rs @@ -215,7 +215,7 @@ pub async fn leave_room( Ok(()) } -async fn remote_leave_room( +pub async fn remote_leave_room( services: &Services, user_id: &UserId, room_id: &RoomId, diff --git a/src/api/client/membership/mod.rs b/src/api/client/membership/mod.rs index 7a6f19ad..691419f6 100644 --- a/src/api/client/membership/mod.rs +++ b/src/api/client/membership/mod.rs @@ -29,7 +29,7 @@ pub(crate) use self::{ }; pub use self::{ join::join_room_by_id_helper, - leave::{leave_all_rooms, leave_room}, + leave::{leave_all_rooms, leave_room, remote_leave_room}, }; use crate::{Ruma, client::full_user_deactivate}; From 5f35ebbce0ecd12c7ea7312bf5567a4fbe2b00d4 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Wed, 30 Jul 2025 19:29:33 +0100 Subject: [PATCH 3/4] fix: Make remote leave helper a public fn --- src/api/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/client/mod.rs b/src/api/client/mod.rs index e4be20b7..be335cba 100644 --- a/src/api/client/mod.rs +++ b/src/api/client/mod.rs @@ -55,7 +55,7 @@ pub(super) use keys::*; pub(super) use media::*; pub(super) use media_legacy::*; pub(super) use membership::*; -pub use membership::{join_room_by_id_helper, leave_all_rooms, leave_room}; +pub use membership::{join_room_by_id_helper, leave_all_rooms, leave_room, remote_leave_room}; pub(super) use message::*; pub(super) use openid::*; pub(super) use presence::*; From e903390b980397a2af28a9ea0044693f25214402 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 31 Jul 2025 17:48:30 +0100 Subject: [PATCH 4/4] feat: Only inject vias when manual ones aren't provided during join --- src/api/client/membership/join.rs | 49 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/api/client/membership/join.rs b/src/api/client/membership/join.rs index dc170cbf..f3434bf5 100644 --- a/src/api/client/membership/join.rs +++ b/src/api/client/membership/join.rs @@ -156,31 +156,34 @@ pub(crate) async fn join_room_by_id_or_alias_route( .await?; let mut servers = body.via.clone(); - servers.extend( - services - .rooms - .state_cache - .servers_invite_via(&room_id) - .map(ToOwned::to_owned) - .collect::>() - .await, - ); + if servers.is_empty() { + debug!("No via servers provided for join, injecting some."); + servers.extend( + services + .rooms + .state_cache + .servers_invite_via(&room_id) + .map(ToOwned::to_owned) + .collect::>() + .await, + ); - servers.extend( - services - .rooms - .state_cache - .invite_state(sender_user, &room_id) - .await - .unwrap_or_default() - .iter() - .filter_map(|event| event.get_field("sender").ok().flatten()) - .filter_map(|sender: &str| UserId::parse(sender).ok()) - .map(|user| user.server_name().to_owned()), - ); + servers.extend( + services + .rooms + .state_cache + .invite_state(sender_user, &room_id) + .await + .unwrap_or_default() + .iter() + .filter_map(|event| event.get_field("sender").ok().flatten()) + .filter_map(|sender: &str| UserId::parse(sender).ok()) + .map(|user| user.server_name().to_owned()), + ); - if let Some(server) = room_id.server_name() { - servers.push(server.to_owned()); + if let Some(server) = room_id.server_name() { + servers.push(server.to_owned()); + } } servers.sort_unstable();