tuwunel_api/client/register/
available.rs1use axum::extract::State;
2use ruma::{UserId, api::client::account::get_username_availability};
3use tuwunel_core::{Err, Result};
4
5use super::is_matrix_appservice_irc;
6use crate::{ClientIp, Ruma};
7
8#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
20pub(crate) async fn get_register_available_route(
21 State(services): State<crate::State>,
22 ClientIp(client): ClientIp,
23 body: Ruma<get_username_availability::v3::Request>,
24) -> Result<get_username_availability::v3::Response> {
25 let is_irc = is_matrix_appservice_irc(body.appservice_info.as_ref());
26
27 if services
28 .config
29 .forbidden_usernames
30 .is_match(&body.username)
31 {
32 return Err!(Request(Forbidden("Username is forbidden")));
33 }
34
35 let body_username = if is_irc {
37 body.username.clone()
38 } else {
39 body.username.to_lowercase()
40 };
41
42 let user_id =
44 match UserId::parse_with_server_name(&body_username, services.globals.server_name()) {
45 | Ok(user_id) => {
46 if let Err(e) = user_id.validate_strict() {
47 if !is_irc {
51 return Err!(Request(InvalidUsername(debug_warn!(
52 "Username {body_username} contains disallowed characters or spaces: \
53 {e}"
54 ))));
55 }
56 }
57
58 user_id
59 },
60 | Err(e) => {
61 return Err!(Request(InvalidUsername(debug_warn!(
62 "Username {body_username} is not valid: {e}"
63 ))));
64 },
65 };
66
67 if services.users.exists(&user_id).await {
69 return Err!(Request(UserInUse("User ID is not available.")));
70 }
71
72 if let Some(ref info) = body.appservice_info
73 && !info.is_user_match(&user_id)
74 {
75 return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
76 }
77
78 if services
79 .appservice
80 .is_exclusive_user_id(&user_id)
81 .await
82 {
83 return Err!(Request(Exclusive("Username is reserved by an appservice.")));
84 }
85
86 Ok(get_username_availability::v3::Response { available: true })
87}