Skip to main content

tuwunel_api_federation/
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		.profile
62		.allow_inbound_federation_lookups
63	{
64		return Err!(Request(Forbidden(
65			"Profile lookup over federation is not allowed on this homeserver.",
66		)));
67	}
68
69	if !services.globals.user_is_local(&body.user_id) {
70		return Err!(Request(InvalidParam("User does not belong to this server.",)));
71	}
72
73	if !services.users.exists(&body.user_id).await {
74		return Err!(Request(NotFound("Profile was not found.")));
75	}
76
77	match &body.field {
78		| Some(field) => {
79			let value = services
80				.profile
81				.profile_key(&body.user_id, field)
82				.await
83				.ok();
84
85			let response = value
86				.map(|value| ProfileFieldValue::new(field.as_str(), value))
87				.transpose()
88				.map_err(|_| {
89					err!(Database(
90						error!(user_id = %body.user_id, key = %field, "Invalid json in database profile value")
91					))
92				})?
93				.into_iter()
94				.collect();
95
96			Ok(response)
97		},
98		| None => {
99			let response = services
100				.profile
101				.all_profile_keys(&body.user_id)
102				.collect::<_>()
103				.await;
104
105			Ok(response)
106		},
107	}
108}