tuwunel_api/client/alias/
directory_room.rs1use axum::extract::State;
2use futures::StreamExt;
3use rand::seq::SliceRandom;
4use ruma::{
5 OwnedServerName, RoomAliasId, RoomId, UserId,
6 api::client::alias::{create_alias, delete_alias, get_alias},
7 events::{StateEventType, room::canonical_alias::RoomCanonicalAliasEventContent},
8};
9use tuwunel_core::{Err, Result, debug, err, matrix::pdu::PduBuilder};
10use tuwunel_service::Services;
11
12use crate::Ruma;
13
14pub(crate) async fn create_alias_route(
18 State(services): State<crate::State>,
19 body: Ruma<create_alias::v3::Request>,
20) -> Result<create_alias::v3::Response> {
21 let sender_user = body.sender_user();
22
23 services
24 .alias
25 .creation_check(sender_user, body.appservice_info.as_ref())
26 .await?;
27
28 services
29 .alias
30 .appservice_checks(&body.room_alias, &body.appservice_info)
31 .await?;
32
33 if services
36 .config
37 .forbidden_alias_names
38 .is_match(body.room_alias.alias())
39 {
40 return Err!(Request(Forbidden("Room alias is forbidden.")));
41 }
42
43 if services
44 .alias
45 .resolve_local_alias(&body.room_alias)
46 .await
47 .is_ok()
48 {
49 return Err!(Conflict("Alias already exists."));
50 }
51
52 services
53 .alias
54 .set_alias_by(&body.room_alias, &body.room_id, sender_user)?;
55
56 Ok(create_alias::v3::Response::new())
57}
58
59pub(crate) async fn delete_alias_route(
67 State(services): State<crate::State>,
68 body: Ruma<delete_alias::v3::Request>,
69) -> Result<delete_alias::v3::Response> {
70 let sender_user = body.sender_user();
71 services
72 .alias
73 .appservice_checks(&body.room_alias, &body.appservice_info)
74 .await?;
75
76 let room_id = services
77 .alias
78 .remove_alias_by(&body.room_alias, sender_user)
79 .await?;
80
81 retire_canonical_alias(&services, &room_id, &body.room_alias, sender_user)
82 .await
83 .inspect_err(|e| debug!(%room_id, "Not updating canonical alias: {e}"))
84 .ok();
85
86 Ok(delete_alias::v3::Response::new())
87}
88
89pub(crate) async fn get_alias_route(
93 State(services): State<crate::State>,
94 body: Ruma<get_alias::v3::Request>,
95) -> Result<get_alias::v3::Response> {
96 let room_alias = body.body.room_alias;
97
98 let (room_id, servers) = services
99 .alias
100 .resolve_alias(&room_alias)
101 .await
102 .map_err(|_| err!(Request(NotFound("Room with alias not found."))))?;
103
104 let servers = room_available_servers(&services, &room_id, &room_alias, servers).await;
105 debug!(?room_alias, ?room_id, "available servers: {servers:?}");
106
107 Ok(get_alias::v3::Response::new(room_id, servers))
108}
109
110async fn retire_canonical_alias(
116 services: &Services,
117 room_id: &RoomId,
118 deleted: &RoomAliasId,
119 sender_user: &UserId,
120) -> Result {
121 let state_lock = services.state.mutex.lock(room_id).await;
122
123 let Ok(content) = services
124 .state_accessor
125 .room_state_get_content::<RoomCanonicalAliasEventContent>(
126 room_id,
127 &StateEventType::RoomCanonicalAlias,
128 "",
129 )
130 .await
131 else {
132 return Ok(());
133 };
134
135 if !content.aliases().any(|alias| alias == deleted) {
136 return Ok(());
137 }
138
139 let content = RoomCanonicalAliasEventContent {
140 alias: content.alias.filter(|alias| alias != deleted),
141 alt_aliases: content
142 .alt_aliases
143 .into_iter()
144 .filter(|alt| alt != deleted)
145 .collect(),
146 };
147
148 services
149 .timeline
150 .build_and_append_pdu(PduBuilder::state("", &content), sender_user, room_id, &state_lock)
151 .await
152 .map(|_| ())
153}
154
155async fn room_available_servers(
156 services: &Services,
157 room_id: &RoomId,
158 room_alias: &RoomAliasId,
159 pre_servers: Vec<OwnedServerName>,
160) -> Vec<OwnedServerName> {
161 let mut servers: Vec<OwnedServerName> = services
163 .state_cache
164 .room_servers(room_id)
165 .map(ToOwned::to_owned)
166 .collect()
167 .await;
168
169 servers.extend(pre_servers);
172
173 servers.sort_unstable();
174 servers.dedup();
175
176 servers.shuffle(&mut rand::rng());
178
179 if let Some(server_index) = servers
182 .iter()
183 .position(|server_name| services.globals.server_is_ours(server_name))
184 {
185 servers.swap(0, server_index);
186 } else if let Some(alias_server_index) = servers
187 .iter()
188 .position(|server| server == room_alias.server_name())
189 {
190 servers.swap(0, alias_server_index);
191 }
192
193 servers
194}