leave room locally if room is banned, rescind knocks on deactivation too

Signed-off-by: June Clementine Strawberry <june@3.dog>
This commit is contained in:
June Clementine Strawberry 2025-04-03 12:20:53 -04:00
parent 24be579477
commit f14756fb76
5 changed files with 87 additions and 28 deletions

View file

@ -475,9 +475,9 @@ pub(crate) async fn leave_room_route(
State(services): State<crate::State>,
body: Ruma<leave_room::v3::Request>,
) -> Result<leave_room::v3::Response> {
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone()).await?;
Ok(leave_room::v3::Response::new())
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
.await
.map(|()| leave_room::v3::Response::new())
}
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
@ -1763,8 +1763,8 @@ pub(crate) async fn invite_helper(
Ok(())
}
// Make a user leave all their joined rooms, forgets all rooms, and ignores
// errors
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
// and ignores errors
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
let rooms_joined = services
.rooms
@ -1778,7 +1778,17 @@ pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
.rooms_invited(user_id)
.map(|(r, _)| r);
let all_rooms: Vec<_> = rooms_joined.chain(rooms_invited).collect().await;
let rooms_knocked = services
.rooms
.state_cache
.rooms_knocked(user_id)
.map(|(r, _)| r);
let all_rooms: Vec<_> = rooms_joined
.chain(rooms_invited)
.chain(rooms_knocked)
.collect()
.await;
for room_id in all_rooms {
// ignore errors
@ -1795,7 +1805,40 @@ pub async fn leave_room(
user_id: &UserId,
room_id: &RoomId,
reason: Option<String>,
) -> Result<()> {
) -> Result {
let default_member_content = RoomMemberEventContent {
membership: MembershipState::Leave,
reason: reason.clone(),
join_authorized_via_users_server: None,
is_direct: None,
avatar_url: None,
displayname: None,
third_party_invite: None,
blurhash: None,
};
if services.rooms.metadata.is_banned(room_id).await
|| services.rooms.metadata.is_disabled(room_id).await
{
// the room is banned/disabled, the room must be rejected locally since we
// cant/dont want to federate with this server
services
.rooms
.state_cache
.update_membership(
room_id,
user_id,
default_member_content,
user_id,
None,
None,
true,
)
.await?;
return Ok(());
}
// Ask a remote server if we don't have this room and are not knocking on it
if !services
.rooms
@ -1828,7 +1871,7 @@ pub async fn leave_room(
.update_membership(
room_id,
user_id,
RoomMemberEventContent::new(MembershipState::Leave),
default_member_content,
user_id,
last_state,
None,
@ -1848,26 +1891,23 @@ pub async fn leave_room(
)
.await
else {
// Fix for broken rooms
warn!(
debug_warn!(
"Trying to leave a room you are not a member of, marking room as left locally."
);
services
return services
.rooms
.state_cache
.update_membership(
room_id,
user_id,
RoomMemberEventContent::new(MembershipState::Leave),
default_member_content,
user_id,
None,
None,
true,
)
.await?;
return Ok(());
.await;
};
services
@ -1897,7 +1937,7 @@ async fn remote_leave_room(
room_id: &RoomId,
) -> Result<()> {
let mut make_leave_response_and_server =
Err!(BadServerResponse("No server available to assist in leaving."));
Err!(BadServerResponse("No remote server available to assist in leaving {room_id}."));
let mut servers: HashSet<OwnedServerName> = services
.rooms
@ -1977,20 +2017,25 @@ async fn remote_leave_room(
let (make_leave_response, remote_server) = make_leave_response_and_server?;
let Some(room_version_id) = make_leave_response.room_version else {
return Err!(BadServerResponse("Remote room version is not supported by conduwuit"));
return Err!(BadServerResponse(warn!(
"No room version was returned by {remote_server} for {room_id}, room version is \
likely not supported by conduwuit"
)));
};
if !services.server.supported_room_version(&room_version_id) {
return Err!(BadServerResponse(
"Remote room version {room_version_id} is not supported by conduwuit"
));
return Err!(BadServerResponse(warn!(
"Remote room version {room_version_id} for {room_id} is not supported by conduwuit",
)));
}
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
make_leave_response.event.get(),
)
.map_err(|e| {
err!(BadServerResponse("Invalid make_leave event json received from server: {e:?}"))
err!(BadServerResponse(warn!(
"Invalid make_leave event json received from {remote_server} for {room_id}: {e:?}"
)))
})?;
// TODO: Is origin needed?

View file

@ -15,6 +15,7 @@ use conduwuit::{
math::ruma_from_u64,
stream::{BroadbandExt, Tools, TryExpect, WidebandExt},
},
warn,
};
use conduwuit_service::{
Services,
@ -428,9 +429,12 @@ async fn handle_left_room(
return Ok(None);
}
if !services.rooms.metadata.exists(room_id).await {
if !services.rooms.metadata.exists(room_id).await
|| services.rooms.metadata.is_disabled(room_id).await
|| services.rooms.metadata.is_banned(room_id).await
{
// This is just a rejected invite, not a room we know
// Insert a leave event anyways
// Insert a leave event anyways for the client
let event = PduEvent {
event_id: EventId::new(services.globals.server_name()),
sender: sender_user.to_owned(),
@ -489,7 +493,7 @@ async fn handle_left_room(
.room_state_get_id(room_id, &StateEventType::RoomMember, sender_user.as_str())
.await
else {
error!("Left room but no left state event");
warn!("Left {room_id} but no left state event");
return Ok(None);
};
@ -499,7 +503,7 @@ async fn handle_left_room(
.pdu_shortstatehash(&left_event_id)
.await
else {
error!(event_id = %left_event_id, "Leave event has no state");
warn!(event_id = %left_event_id, "Leave event has no state in {room_id}");
return Ok(None);
};

View file

@ -438,7 +438,10 @@ pub(crate) async fn sync_events_v4_route(
let mut known_subscription_rooms = BTreeSet::new();
for (room_id, room) in &body.room_subscriptions {
if !services.rooms.metadata.exists(room_id).await {
if !services.rooms.metadata.exists(room_id).await
|| services.rooms.metadata.is_disabled(room_id).await
|| services.rooms.metadata.is_banned(room_id).await
{
continue;
}
let todo_room =

View file

@ -214,7 +214,10 @@ async fn fetch_subscriptions(
) {
let mut known_subscription_rooms = BTreeSet::new();
for (room_id, room) in &body.room_subscriptions {
if !services.rooms.metadata.exists(room_id).await {
if !services.rooms.metadata.exists(room_id).await
|| services.rooms.metadata.is_disabled(room_id).await
|| services.rooms.metadata.is_banned(room_id).await
{
continue;
}
let todo_room =

View file

@ -40,6 +40,7 @@ struct Services {
account_data: Dep<account_data::Service>,
config: Dep<config::Service>,
globals: Dep<globals::Service>,
metadata: Dep<rooms::metadata::Service>,
state_accessor: Dep<rooms::state_accessor::Service>,
users: Dep<users::Service>,
}
@ -73,6 +74,7 @@ impl crate::Service for Service {
account_data: args.depend::<account_data::Service>("account_data"),
config: args.depend::<config::Service>("config"),
globals: args.depend::<globals::Service>("globals"),
metadata: args.depend::<rooms::metadata::Service>("rooms::metadata"),
state_accessor: args
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
users: args.depend::<users::Service>("users"),
@ -271,7 +273,9 @@ impl Service {
self.mark_as_left(user_id, room_id);
if self.services.globals.user_is_local(user_id)
&& self.services.config.forget_forced_upon_leave
&& (self.services.config.forget_forced_upon_leave
|| self.services.metadata.is_banned(room_id).await
|| self.services.metadata.is_disabled(room_id).await)
{
self.forget(room_id, user_id);
}