Skip to main content

tuwunel_api/server/
query.rs

1use axum::extract::State;
2use futures::StreamExt;
3use rand::seq::SliceRandom;
4use ruma::{
5	OwnedServerName,
6	api::federation::query::{get_profile_information, get_room_information},
7	profile::ProfileFieldValue,
8};
9use tuwunel_core::{Err, Result, err};
10
11use crate::Ruma;
12
13/// # `GET /_matrix/federation/v1/query/directory`
14///
15/// Resolve a room alias to a room id.
16pub(crate) async fn get_room_information_route(
17	State(services): State<crate::State>,
18	body: Ruma<get_room_information::v1::Request>,
19) -> Result<get_room_information::v1::Response> {
20	let room_id = services
21		.alias
22		.resolve_local_alias(&body.room_alias)
23		.await
24		.map_err(|_| err!(Request(NotFound("Room alias not found."))))?;
25
26	let mut servers: Vec<OwnedServerName> = services
27		.state_cache
28		.room_servers(&room_id)
29		.map(ToOwned::to_owned)
30		.collect()
31		.await;
32
33	servers.sort_unstable();
34	servers.dedup();
35
36	servers.shuffle(&mut rand::rng());
37
38	// insert our server as the very first choice if in list
39	if let Some(server_index) = servers
40		.iter()
41		.position(|server| server == services.globals.server_name())
42	{
43		servers.swap_remove(server_index);
44		servers.insert(0, services.globals.server_name().to_owned());
45	}
46
47	Ok(get_room_information::v1::Response { room_id, servers })
48}
49
50/// # `GET /_matrix/federation/v1/query/profile`
51///
52///
53/// Gets information on a profile.
54pub(crate) async fn get_profile_information_route(
55	State(services): State<crate::State>,
56	body: Ruma<get_profile_information::v1::Request>,
57) -> Result<get_profile_information::v1::Response> {
58	if !services
59		.server
60		.config
61		.allow_inbound_profile_lookup_federation_requests
62	{
63		return Err!(Request(Forbidden(
64			"Profile lookup over federation is not allowed on this homeserver.",
65		)));
66	}
67
68	if !services.globals.user_is_local(&body.user_id) {
69		return Err!(Request(InvalidParam("User does not belong to this server.",)));
70	}
71
72	match &body.field {
73		| Some(field) => {
74			let value = services
75				.profile
76				.profile_key(&body.user_id, field)
77				.await
78				.ok();
79
80			let response = value
81				.map(|value| ProfileFieldValue::new(field.as_str(), value))
82				.transpose()
83				.map_err(|_| {
84					err!(Database(
85						error!(user_id = %body.user_id, key = %field, "Invalid json in database profile value")
86					))
87				})?
88				.into_iter()
89				.collect();
90
91			Ok(response)
92		},
93		| None => {
94			let response = services
95				.profile
96				.all_profile_keys(&body.user_id)
97				.collect::<_>()
98				.await;
99
100			Ok(response)
101		},
102	}
103}