Skip to main content

tuwunel_api/client/
capabilities.rs

1use std::collections::BTreeMap;
2
3use axum::extract::State;
4#[allow(deprecated)]
5use ruma::api::client::discovery::get_capabilities::v3::{
6	SetAvatarUrlCapability, SetDisplayNameCapability,
7};
8use ruma::{
9	RoomVersionId,
10	api::client::discovery::{
11		get_capabilities,
12		get_capabilities::v3::{
13			AccountModerationCapability, Capabilities, ChangePasswordCapability,
14			ForgetForcedUponLeaveCapability, GetLoginTokenCapability, ProfileFieldsCapability,
15			RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
16		},
17	},
18};
19use serde_json::json;
20use tuwunel_core::Result;
21
22use crate::Ruma;
23
24/// # `GET /_matrix/client/v3/capabilities`
25///
26/// Get information on the supported feature set and other relevant capabilities
27/// of this server.
28#[allow(deprecated)]
29pub(crate) async fn get_capabilities_route(
30	State(services): State<crate::State>,
31	body: Ruma<get_capabilities::v3::Request>,
32) -> Result<get_capabilities::v3::Response> {
33	let available: BTreeMap<RoomVersionId, RoomVersionStability> = services
34		.config
35		.supported_room_versions()
36		.collect();
37
38	let mut capabilities = Capabilities::default();
39	capabilities.room_versions = RoomVersionsCapability {
40		available,
41		default: services
42			.server
43			.config
44			.default_room_version
45			.clone(),
46	};
47
48	// MSC3283: deprecated displayname/avatar capabilities for pre-1.16 clients.
49	capabilities.set_displayname = SetDisplayNameCapability::new(true);
50	capabilities.set_avatar_url = SetAvatarUrlCapability::new(true);
51
52	// 3PID add/remove is available only when the email subsystem can send.
53	capabilities.thirdparty_id_changes =
54		ThirdPartyIdChangesCapability { enabled: services.sendmail.is_enabled() };
55
56	capabilities.get_login_token = GetLoginTokenCapability {
57		enabled: services.server.config.login_via_existing_session,
58	};
59
60	capabilities.profile_fields = ProfileFieldsCapability::new(true).into();
61
62	capabilities.change_password = ChangePasswordCapability {
63		enabled: services.server.config.login_with_password,
64	};
65
66	capabilities.forget_forced_upon_leave =
67		ForgetForcedUponLeaveCapability::new(services.config.forget_forced_upon_leave);
68
69	capabilities.set(
70		"org.matrix.msc4267.forget_forced_upon_leave",
71		json!({"enabled": services.config.forget_forced_upon_leave}),
72	)?;
73
74	// MSC4452: enabled mirrors the per-URL gate; empty allowlists 403 every URL.
75	let preview_url_enabled = !services
76		.config
77		.url_preview_domain_contains_allowlist
78		.is_empty()
79		|| !services
80			.config
81			.url_preview_domain_explicit_allowlist
82			.is_empty()
83		|| !services
84			.config
85			.url_preview_url_contains_allowlist
86			.is_empty();
87
88	capabilities
89		.set("io.element.msc4452.preview_url", json!({"enabled": preview_url_enabled}))?;
90
91	// MSC4323: advertise admin moderation only to admins; absence implies
92	// neither suspend nor lock is available to the caller.
93	if services
94		.admin
95		.user_is_admin(body.sender_user())
96		.await
97	{
98		capabilities.account_moderation = AccountModerationCapability::new(true, true);
99	}
100
101	Ok(get_capabilities::v3::Response { capabilities })
102}