fixup+update msc3266, add fed support, parallelise IO
Signed-off-by: June Clementine Strawberry <june@3.dog>
This commit is contained in:
parent
1036f8dfa8
commit
0e0b8cc403
8 changed files with 389 additions and 174 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -3531,7 +3531,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma"
|
name = "ruma"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"js_int",
|
"js_int",
|
||||||
|
@ -3551,7 +3551,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-appservice-api"
|
name = "ruma-appservice-api"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
@ -3563,7 +3563,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-client-api"
|
name = "ruma-client-api"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"assign",
|
"assign",
|
||||||
|
@ -3586,7 +3586,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-common"
|
name = "ruma-common"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
@ -3618,7 +3618,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events"
|
name = "ruma-events"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"indexmap 2.8.0",
|
"indexmap 2.8.0",
|
||||||
|
@ -3643,7 +3643,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-federation-api"
|
name = "ruma-federation-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"headers",
|
"headers",
|
||||||
|
@ -3665,7 +3665,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-validation"
|
name = "ruma-identifiers-validation"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
|
@ -3674,7 +3674,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identity-service-api"
|
name = "ruma-identity-service-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
@ -3684,7 +3684,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-macros"
|
name = "ruma-macros"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
|
@ -3699,7 +3699,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-push-gateway-api"
|
name = "ruma-push-gateway-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
@ -3711,7 +3711,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-signatures"
|
name = "ruma-signatures"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
|
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
|
|
@ -346,7 +346,7 @@ version = "0.1.2"
|
||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
git = "https://github.com/girlbossceo/ruwuma"
|
git = "https://github.com/girlbossceo/ruwuma"
|
||||||
#branch = "conduwuit-changes"
|
#branch = "conduwuit-changes"
|
||||||
rev = "d197318a2507d38ffe6ee524d0d52728ca72538a"
|
rev = "ea1278657125e9414caada074e8c172bc252fb1c"
|
||||||
features = [
|
features = [
|
||||||
"compat",
|
"compat",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
@ -2,9 +2,14 @@ mod aliases;
|
||||||
mod create;
|
mod create;
|
||||||
mod event;
|
mod event;
|
||||||
mod initial_sync;
|
mod initial_sync;
|
||||||
|
mod summary;
|
||||||
mod upgrade;
|
mod upgrade;
|
||||||
|
|
||||||
pub(crate) use self::{
|
pub(crate) use self::{
|
||||||
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
|
aliases::get_room_aliases_route,
|
||||||
initial_sync::room_initial_sync_route, upgrade::upgrade_room_route,
|
create::create_room_route,
|
||||||
|
event::get_room_event_route,
|
||||||
|
initial_sync::room_initial_sync_route,
|
||||||
|
summary::{get_room_summary, get_room_summary_legacy},
|
||||||
|
upgrade::upgrade_room_route,
|
||||||
};
|
};
|
||||||
|
|
308
src/api/client/room/summary.rs
Normal file
308
src/api/client/room/summary.rs
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum_client_ip::InsecureClientIp;
|
||||||
|
use conduwuit::{
|
||||||
|
Err, Result, debug_warn,
|
||||||
|
utils::{IterStream, future::TryExtExt},
|
||||||
|
};
|
||||||
|
use futures::{FutureExt, StreamExt, future::join3, stream::FuturesUnordered};
|
||||||
|
use ruma::{
|
||||||
|
OwnedRoomId, OwnedServerName, RoomId, UserId,
|
||||||
|
api::{
|
||||||
|
client::room::get_summary,
|
||||||
|
federation::space::{SpaceHierarchyParentSummary, get_hierarchy},
|
||||||
|
},
|
||||||
|
events::room::member::MembershipState,
|
||||||
|
space::SpaceRoomJoinRule::{self, *},
|
||||||
|
};
|
||||||
|
use service::Services;
|
||||||
|
|
||||||
|
use crate::{Ruma, RumaResponse};
|
||||||
|
|
||||||
|
/// # `GET /_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`
|
||||||
|
///
|
||||||
|
/// Returns a short description of the state of a room.
|
||||||
|
///
|
||||||
|
/// This is the "wrong" endpoint that some implementations/clients may use
|
||||||
|
/// according to the MSC. Request and response bodies are the same as
|
||||||
|
/// `get_room_summary`.
|
||||||
|
///
|
||||||
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
|
pub(crate) async fn get_room_summary_legacy(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
|
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
||||||
|
get_room_summary(State(services), InsecureClientIp(client), body)
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
.map(RumaResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # `GET /_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`
|
||||||
|
///
|
||||||
|
/// Returns a short description of the state of a room.
|
||||||
|
///
|
||||||
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
||||||
|
pub(crate) async fn get_room_summary(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
|
) -> Result<get_summary::msc3266::Response> {
|
||||||
|
let (room_id, servers) = services
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.resolve_with_servers(&body.room_id_or_alias, Some(body.via.clone()))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if services.rooms.metadata.is_banned(&room_id).await {
|
||||||
|
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
room_summary_response(&services, &room_id, &servers, body.sender_user.as_deref())
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn room_summary_response(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
|
sender_user: Option<&UserId>,
|
||||||
|
) -> Result<get_summary::msc3266::Response> {
|
||||||
|
if services.rooms.metadata.exists(room_id).await {
|
||||||
|
return local_room_summary_response(services, room_id, sender_user)
|
||||||
|
.boxed()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let room =
|
||||||
|
remote_room_summary_hierarchy_response(services, room_id, servers, sender_user).await?;
|
||||||
|
|
||||||
|
Ok(get_summary::msc3266::Response {
|
||||||
|
room_id: room_id.to_owned(),
|
||||||
|
canonical_alias: room.canonical_alias,
|
||||||
|
avatar_url: room.avatar_url,
|
||||||
|
guest_can_join: room.guest_can_join,
|
||||||
|
name: room.name,
|
||||||
|
num_joined_members: room.num_joined_members,
|
||||||
|
topic: room.topic,
|
||||||
|
world_readable: room.world_readable,
|
||||||
|
join_rule: room.join_rule,
|
||||||
|
room_type: room.room_type,
|
||||||
|
room_version: room.room_version,
|
||||||
|
membership: if sender_user.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(MembershipState::Leave)
|
||||||
|
},
|
||||||
|
encryption: room.encryption,
|
||||||
|
allowed_room_ids: room.allowed_room_ids,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn local_room_summary_response(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender_user: Option<&UserId>,
|
||||||
|
) -> Result<get_summary::msc3266::Response> {
|
||||||
|
let join_rule = services.rooms.state_accessor.get_space_join_rule(room_id);
|
||||||
|
let world_readable = services.rooms.state_accessor.is_world_readable(room_id);
|
||||||
|
let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id);
|
||||||
|
|
||||||
|
let ((join_rule, allowed_room_ids), world_readable, guest_can_join) =
|
||||||
|
join3(join_rule, world_readable, guest_can_join).await;
|
||||||
|
|
||||||
|
user_can_see_summary(
|
||||||
|
services,
|
||||||
|
room_id,
|
||||||
|
&join_rule,
|
||||||
|
guest_can_join,
|
||||||
|
world_readable,
|
||||||
|
&allowed_room_ids,
|
||||||
|
sender_user,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let canonical_alias = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.get_canonical_alias(room_id)
|
||||||
|
.ok();
|
||||||
|
let name = services.rooms.state_accessor.get_name(room_id).ok();
|
||||||
|
let topic = services.rooms.state_accessor.get_room_topic(room_id).ok();
|
||||||
|
let room_type = services.rooms.state_accessor.get_room_type(room_id).ok();
|
||||||
|
let avatar_url = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.get_avatar(room_id)
|
||||||
|
.map(|res| res.into_option().unwrap_or_default().url);
|
||||||
|
let room_version = services.rooms.state.get_room_version(room_id).ok();
|
||||||
|
let encryption = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.get_room_encryption(room_id)
|
||||||
|
.ok();
|
||||||
|
let num_joined_members = services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.room_joined_count(room_id)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let (
|
||||||
|
canonical_alias,
|
||||||
|
name,
|
||||||
|
num_joined_members,
|
||||||
|
topic,
|
||||||
|
avatar_url,
|
||||||
|
room_type,
|
||||||
|
room_version,
|
||||||
|
encryption,
|
||||||
|
) = futures::join!(
|
||||||
|
canonical_alias,
|
||||||
|
name,
|
||||||
|
num_joined_members,
|
||||||
|
topic,
|
||||||
|
avatar_url,
|
||||||
|
room_type,
|
||||||
|
room_version,
|
||||||
|
encryption,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(get_summary::msc3266::Response {
|
||||||
|
room_id: room_id.to_owned(),
|
||||||
|
canonical_alias,
|
||||||
|
avatar_url,
|
||||||
|
guest_can_join,
|
||||||
|
name,
|
||||||
|
num_joined_members: num_joined_members.try_into().unwrap_or_default(),
|
||||||
|
topic,
|
||||||
|
world_readable,
|
||||||
|
join_rule,
|
||||||
|
room_type,
|
||||||
|
room_version,
|
||||||
|
membership: if let Some(sender_user) = sender_user {
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.get_member(room_id, sender_user)
|
||||||
|
.await
|
||||||
|
.map_or(Some(MembershipState::Leave), |content| Some(content.membership))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
encryption,
|
||||||
|
allowed_room_ids,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// used by MSC3266 to fetch a room's info if we do not know about it
|
||||||
|
async fn remote_room_summary_hierarchy_response(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
|
sender_user: Option<&UserId>,
|
||||||
|
) -> Result<SpaceHierarchyParentSummary> {
|
||||||
|
if !services.config.allow_federation {
|
||||||
|
return Err!(Request(Forbidden("Federation is disabled.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if services.rooms.metadata.is_disabled(room_id).await {
|
||||||
|
return Err!(Request(Forbidden(
|
||||||
|
"Federaton of room {room_id} is currently disabled on this server."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = get_hierarchy::v1::Request::new(room_id.to_owned());
|
||||||
|
|
||||||
|
let mut requests: FuturesUnordered<_> = servers
|
||||||
|
.iter()
|
||||||
|
.map(|server| {
|
||||||
|
services
|
||||||
|
.sending
|
||||||
|
.send_federation_request(server, request.clone())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
while let Some(Ok(response)) = requests.next().await {
|
||||||
|
let room = response.room.clone();
|
||||||
|
if room.room_id != room_id {
|
||||||
|
debug_warn!(
|
||||||
|
"Room ID {} returned does not belong to the requested room ID {}",
|
||||||
|
room.room_id,
|
||||||
|
room_id
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user_can_see_summary(
|
||||||
|
services,
|
||||||
|
room_id,
|
||||||
|
&room.join_rule,
|
||||||
|
room.guest_can_join,
|
||||||
|
room.world_readable,
|
||||||
|
&room.allowed_room_ids,
|
||||||
|
sender_user,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|()| room);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err!(Request(NotFound(
|
||||||
|
"Room is unknown to this server and was unable to fetch over federation with the \
|
||||||
|
provided servers available"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn user_can_see_summary(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
join_rule: &SpaceRoomJoinRule,
|
||||||
|
guest_can_join: bool,
|
||||||
|
world_readable: bool,
|
||||||
|
allowed_room_ids: &[OwnedRoomId],
|
||||||
|
sender_user: Option<&UserId>,
|
||||||
|
) -> Result {
|
||||||
|
match sender_user {
|
||||||
|
| Some(sender_user) => {
|
||||||
|
let user_can_see_state_events = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.user_can_see_state_events(sender_user, room_id);
|
||||||
|
let is_guest = services.users.is_deactivated(sender_user).unwrap_or(false);
|
||||||
|
let user_in_allowed_restricted_room = allowed_room_ids
|
||||||
|
.iter()
|
||||||
|
.stream()
|
||||||
|
.any(|room| services.rooms.state_cache.is_joined(sender_user, room));
|
||||||
|
|
||||||
|
let (user_can_see_state_events, is_guest, user_in_allowed_restricted_room) =
|
||||||
|
join3(user_can_see_state_events, is_guest, user_in_allowed_restricted_room)
|
||||||
|
.boxed()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if user_can_see_state_events
|
||||||
|
|| (is_guest && guest_can_join)
|
||||||
|
|| matches!(&join_rule, &Public | &Knock | &KnockRestricted)
|
||||||
|
|| user_in_allowed_restricted_room
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err!(Request(Forbidden(
|
||||||
|
"Room is not world readable, not publicly accessible/joinable, restricted room \
|
||||||
|
conditions not met, and guest access is forbidden. Not allowed to see details \
|
||||||
|
of this room."
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
| None => {
|
||||||
|
if matches!(join_rule, Public | Knock | KnockRestricted) || world_readable {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err!(Request(Forbidden(
|
||||||
|
"Room is not world readable or publicly accessible/joinable, authentication is \
|
||||||
|
required"
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -103,7 +103,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
// Use the m.room.tombstone event as the predecessor
|
// Use the m.room.tombstone event as the predecessor
|
||||||
let predecessor = Some(ruma::events::room::create::PreviousRoom::new(
|
let predecessor = Some(ruma::events::room::create::PreviousRoom::new(
|
||||||
body.room_id.clone(),
|
body.room_id.clone(),
|
||||||
(*tombstone_event_id).to_owned(),
|
Some(tombstone_event_id),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Send a m.room.create event containing a predecessor field and the applicable
|
// Send a m.room.create event containing a predecessor field and the applicable
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::Err;
|
use conduwuit::{Err, Error, Result};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId,
|
OwnedRoomId,
|
||||||
|
@ -14,16 +14,14 @@ use ruma::{
|
||||||
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key,
|
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key,
|
||||||
set_profile_key, set_timezone_key,
|
set_profile_key, set_timezone_key,
|
||||||
},
|
},
|
||||||
room::get_summary,
|
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
events::room::member::MembershipState,
|
|
||||||
presence::PresenceState,
|
presence::PresenceState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{update_avatar_url, update_displayname};
|
use super::{update_avatar_url, update_displayname};
|
||||||
use crate::{Error, Result, Ruma, RumaResponse};
|
use crate::Ruma;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
||||||
///
|
///
|
||||||
|
@ -38,13 +36,10 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||||
InsecureClientIp(client): InsecureClientIp,
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<mutual_rooms::unstable::Request>,
|
body: Ruma<mutual_rooms::unstable::Request>,
|
||||||
) -> Result<mutual_rooms::unstable::Response> {
|
) -> Result<mutual_rooms::unstable::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
if sender_user == &body.user_id {
|
if sender_user == body.user_id {
|
||||||
return Err(Error::BadRequest(
|
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
|
||||||
ErrorKind::Unknown,
|
|
||||||
"You cannot request rooms in common with yourself.",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services.users.exists(&body.user_id).await {
|
if !services.users.exists(&body.user_id).await {
|
||||||
|
@ -65,129 +60,6 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`
|
|
||||||
///
|
|
||||||
/// Returns a short description of the state of a room.
|
|
||||||
///
|
|
||||||
/// This is the "wrong" endpoint that some implementations/clients may use
|
|
||||||
/// according to the MSC. Request and response bodies are the same as
|
|
||||||
/// `get_room_summary`.
|
|
||||||
///
|
|
||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
|
||||||
pub(crate) async fn get_room_summary_legacy(
|
|
||||||
State(services): State<crate::State>,
|
|
||||||
InsecureClientIp(client): InsecureClientIp,
|
|
||||||
body: Ruma<get_summary::msc3266::Request>,
|
|
||||||
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
|
||||||
get_room_summary(State(services), InsecureClientIp(client), body)
|
|
||||||
.await
|
|
||||||
.map(RumaResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`
|
|
||||||
///
|
|
||||||
/// Returns a short description of the state of a room.
|
|
||||||
///
|
|
||||||
/// TODO: support fetching remote room info if we don't know the room
|
|
||||||
///
|
|
||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
|
||||||
pub(crate) async fn get_room_summary(
|
|
||||||
State(services): State<crate::State>,
|
|
||||||
InsecureClientIp(client): InsecureClientIp,
|
|
||||||
body: Ruma<get_summary::msc3266::Request>,
|
|
||||||
) -> Result<get_summary::msc3266::Response> {
|
|
||||||
let sender_user = body.sender_user.as_ref();
|
|
||||||
|
|
||||||
let room_id = services.rooms.alias.resolve(&body.room_id_or_alias).await?;
|
|
||||||
|
|
||||||
if !services.rooms.metadata.exists(&room_id).await {
|
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if sender_user.is_none()
|
|
||||||
&& !services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.is_world_readable(&room_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"Room is not world readable, authentication is required",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(get_summary::msc3266::Response {
|
|
||||||
room_id: room_id.clone(),
|
|
||||||
canonical_alias: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_canonical_alias(&room_id)
|
|
||||||
.await
|
|
||||||
.ok(),
|
|
||||||
avatar_url: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_avatar(&room_id)
|
|
||||||
.await
|
|
||||||
.into_option()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.url,
|
|
||||||
guest_can_join: services.rooms.state_accessor.guest_can_join(&room_id).await,
|
|
||||||
name: services.rooms.state_accessor.get_name(&room_id).await.ok(),
|
|
||||||
num_joined_members: services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_joined_count(&room_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(0)
|
|
||||||
.try_into()?,
|
|
||||||
topic: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_room_topic(&room_id)
|
|
||||||
.await
|
|
||||||
.ok(),
|
|
||||||
world_readable: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.is_world_readable(&room_id)
|
|
||||||
.await,
|
|
||||||
join_rule: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_join_rule(&room_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default()
|
|
||||||
.0,
|
|
||||||
room_type: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_room_type(&room_id)
|
|
||||||
.await
|
|
||||||
.ok(),
|
|
||||||
room_version: services.rooms.state.get_room_version(&room_id).await.ok(),
|
|
||||||
membership: if let Some(sender_user) = sender_user {
|
|
||||||
services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_member(&room_id, sender_user)
|
|
||||||
.await
|
|
||||||
.map_or_else(|_| MembershipState::Leave, |content| content.membership)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
encryption: services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_room_encryption(&room_id)
|
|
||||||
.await
|
|
||||||
.ok(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `DELETE /_matrix/client/unstable/uk.tcpip.msc4133/profile/:user_id/us.cloke.msc4175.tz`
|
/// # `DELETE /_matrix/client/unstable/uk.tcpip.msc4133/profile/:user_id/us.cloke.msc4175.tz`
|
||||||
///
|
///
|
||||||
/// Deletes the `tz` (timezone) of a user, as per MSC4133 and MSC4175.
|
/// Deletes the `tz` (timezone) of a user, as per MSC4133 and MSC4175.
|
||||||
|
|
|
@ -27,7 +27,6 @@ use ruma::{
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
StateEventType,
|
StateEventType,
|
||||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
|
||||||
space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
|
space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
|
@ -306,25 +305,18 @@ async fn get_room_summary(
|
||||||
children_state: Vec<Raw<HierarchySpaceChildEvent>>,
|
children_state: Vec<Raw<HierarchySpaceChildEvent>>,
|
||||||
identifier: &Identifier<'_>,
|
identifier: &Identifier<'_>,
|
||||||
) -> Result<SpaceHierarchyParentSummary, Error> {
|
) -> Result<SpaceHierarchyParentSummary, Error> {
|
||||||
let join_rule = self
|
let (join_rule, allowed_room_ids) = self
|
||||||
.services
|
.services
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
|
.get_space_join_rule(room_id)
|
||||||
.await
|
.await;
|
||||||
.map_or(JoinRule::Invite, |c: RoomJoinRulesEventContent| c.join_rule);
|
|
||||||
|
|
||||||
let allowed_room_ids = self
|
|
||||||
.services
|
|
||||||
.state_accessor
|
|
||||||
.allowed_room_ids(join_rule.clone());
|
|
||||||
|
|
||||||
let join_rule = join_rule.clone().into();
|
|
||||||
let is_accessible_child = self
|
let is_accessible_child = self
|
||||||
.is_accessible_child(room_id, &join_rule, identifier, &allowed_room_ids)
|
.is_accessible_child(room_id, &join_rule, identifier, &allowed_room_ids)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if !is_accessible_child {
|
if !is_accessible_child {
|
||||||
return Err!(Request(Forbidden("User is not allowed to see the room",)));
|
return Err!(Request(Forbidden("User is not allowed to see the room")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = self.services.state_accessor.get_name(room_id).ok();
|
let name = self.services.state_accessor.get_name(room_id).ok();
|
||||||
|
@ -355,6 +347,14 @@ async fn get_room_summary(
|
||||||
.get_avatar(room_id)
|
.get_avatar(room_id)
|
||||||
.map(|res| res.into_option().unwrap_or_default().url);
|
.map(|res| res.into_option().unwrap_or_default().url);
|
||||||
|
|
||||||
|
let room_version = self.services.state.get_room_version(room_id).ok();
|
||||||
|
|
||||||
|
let encryption = self
|
||||||
|
.services
|
||||||
|
.state_accessor
|
||||||
|
.get_room_encryption(room_id)
|
||||||
|
.ok();
|
||||||
|
|
||||||
let (
|
let (
|
||||||
canonical_alias,
|
canonical_alias,
|
||||||
name,
|
name,
|
||||||
|
@ -364,6 +364,8 @@ async fn get_room_summary(
|
||||||
guest_can_join,
|
guest_can_join,
|
||||||
avatar_url,
|
avatar_url,
|
||||||
room_type,
|
room_type,
|
||||||
|
room_version,
|
||||||
|
encryption,
|
||||||
) = futures::join!(
|
) = futures::join!(
|
||||||
canonical_alias,
|
canonical_alias,
|
||||||
name,
|
name,
|
||||||
|
@ -372,7 +374,9 @@ async fn get_room_summary(
|
||||||
world_readable,
|
world_readable,
|
||||||
guest_can_join,
|
guest_can_join,
|
||||||
avatar_url,
|
avatar_url,
|
||||||
room_type
|
room_type,
|
||||||
|
room_version,
|
||||||
|
encryption,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(SpaceHierarchyParentSummary {
|
Ok(SpaceHierarchyParentSummary {
|
||||||
|
@ -387,9 +391,9 @@ async fn get_room_summary(
|
||||||
allowed_room_ids,
|
allowed_room_ids,
|
||||||
join_rule,
|
join_rule,
|
||||||
room_id: room_id.to_owned(),
|
room_id: room_id.to_owned(),
|
||||||
num_joined_members: num_joined_members
|
num_joined_members: num_joined_members.try_into().unwrap_or_default(),
|
||||||
.try_into()
|
encryption,
|
||||||
.expect("user count should not be that big"),
|
room_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +491,8 @@ async fn cache_insert(
|
||||||
join_rule,
|
join_rule,
|
||||||
room_type,
|
room_type,
|
||||||
allowed_room_ids,
|
allowed_room_ids,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
} = child;
|
} = child;
|
||||||
|
|
||||||
let summary = SpaceHierarchyParentSummary {
|
let summary = SpaceHierarchyParentSummary {
|
||||||
|
@ -506,6 +512,8 @@ async fn cache_insert(
|
||||||
.map(PduEvent::into_stripped_spacechild_state_event)
|
.map(PduEvent::into_stripped_spacechild_state_event)
|
||||||
.collect()
|
.collect()
|
||||||
.await,
|
.await,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
};
|
};
|
||||||
|
|
||||||
cache.insert(current_room.to_owned(), Some(CachedSpaceHierarchySummary { summary }));
|
cache.insert(current_room.to_owned(), Some(CachedSpaceHierarchySummary { summary }));
|
||||||
|
@ -527,7 +535,9 @@ impl From<CachedSpaceHierarchySummary> for SpaceHierarchyRoomsChunk {
|
||||||
join_rule,
|
join_rule,
|
||||||
room_type,
|
room_type,
|
||||||
children_state,
|
children_state,
|
||||||
..
|
allowed_room_ids,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
} = value.summary;
|
} = value.summary;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -542,6 +552,9 @@ impl From<CachedSpaceHierarchySummary> for SpaceHierarchyRoomsChunk {
|
||||||
join_rule,
|
join_rule,
|
||||||
room_type,
|
room_type,
|
||||||
children_state,
|
children_state,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
|
allowed_room_ids,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,7 +575,9 @@ pub fn summary_to_chunk(summary: SpaceHierarchyParentSummary) -> SpaceHierarchyR
|
||||||
join_rule,
|
join_rule,
|
||||||
room_type,
|
room_type,
|
||||||
children_state,
|
children_state,
|
||||||
..
|
allowed_room_ids,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
} = summary;
|
} = summary;
|
||||||
|
|
||||||
SpaceHierarchyRoomsChunk {
|
SpaceHierarchyRoomsChunk {
|
||||||
|
@ -577,5 +592,8 @@ pub fn summary_to_chunk(summary: SpaceHierarchyParentSummary) -> SpaceHierarchyR
|
||||||
join_rule,
|
join_rule,
|
||||||
room_type,
|
room_type,
|
||||||
children_state,
|
children_state,
|
||||||
|
encryption,
|
||||||
|
room_version,
|
||||||
|
allowed_room_ids,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,22 +129,34 @@ impl Service {
|
||||||
.map(|c: RoomTopicEventContent| c.topic)
|
.map(|c: RoomTopicEventContent| c.topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the join rule (`SpaceRoomJoinRule`) for a given room
|
/// Returns the space join rule (`SpaceRoomJoinRule`) for a given room and
|
||||||
pub async fn get_join_rule(
|
/// any allowed room IDs if available. Will default to Invite and empty vec
|
||||||
|
/// if doesnt exist or invalid,
|
||||||
|
pub async fn get_space_join_rule(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<(SpaceRoomJoinRule, Vec<OwnedRoomId>)> {
|
) -> (SpaceRoomJoinRule, Vec<OwnedRoomId>) {
|
||||||
self.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
|
self.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
|
||||||
.await
|
.await
|
||||||
.map(|c: RoomJoinRulesEventContent| {
|
.map_or_else(
|
||||||
(c.join_rule.clone().into(), self.allowed_room_ids(c.join_rule))
|
|_| (SpaceRoomJoinRule::Invite, vec![]),
|
||||||
})
|
|c: RoomJoinRulesEventContent| {
|
||||||
.or_else(|_| Ok((SpaceRoomJoinRule::Invite, vec![])))
|
(c.join_rule.clone().into(), self.allowed_room_ids(c.join_rule))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the join rules for a given room (`JoinRule` type). Will default
|
||||||
|
/// to Invite if doesnt exist or invalid
|
||||||
|
pub async fn get_join_rules(&self, room_id: &RoomId) -> JoinRule {
|
||||||
|
self.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
|
||||||
|
.await
|
||||||
|
.map_or_else(|_| JoinRule::Invite, |c: RoomJoinRulesEventContent| (c.join_rule))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an empty vec if not a restricted room
|
/// Returns an empty vec if not a restricted room
|
||||||
pub fn allowed_room_ids(&self, join_rule: JoinRule) -> Vec<OwnedRoomId> {
|
pub fn allowed_room_ids(&self, join_rule: JoinRule) -> Vec<OwnedRoomId> {
|
||||||
let mut room_ids = Vec::with_capacity(1);
|
let mut room_ids = Vec::with_capacity(1); // restricted rooms generally only have 1 allowed room ID
|
||||||
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rule {
|
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rule {
|
||||||
for rule in r.allow {
|
for rule in r.allow {
|
||||||
if let AllowRule::RoomMembership(RoomMembership { room_id: membership }) = rule {
|
if let AllowRule::RoomMembership(RoomMembership { room_id: membership }) = rule {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue