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 .registration
30 .forbidden_usernames
31 .is_match(&body.username)
32 {
33 return Err!(Request(Forbidden("Username is forbidden")));
34 }
35
36 let body_username = if is_irc {
38 body.username.clone()
39 } else {
40 body.username.to_lowercase()
41 };
42
43 let user_id =
45 match UserId::parse_with_server_name(&body_username, services.globals.server_name()) {
46 | Ok(user_id) => {
47 if let Err(e) = user_id.validate_strict() {
48 if !is_irc {
52 return Err!(Request(InvalidUsername(debug_warn!(
53 "Username {body_username} contains disallowed characters or spaces: \
54 {e}"
55 ))));
56 }
57 }
58
59 user_id
60 },
61 | Err(e) => {
62 return Err!(Request(InvalidUsername(debug_warn!(
63 "Username {body_username} is not valid: {e}"
64 ))));
65 },
66 };
67
68 if services.users.exists(&user_id).await {
70 return Err!(Request(UserInUse("User ID is not available.")));
71 }
72
73 if let Some(ref info) = body.appservice_info
74 && !info.is_user_match(&user_id)
75 {
76 return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
77 }
78
79 if services
80 .appservice
81 .is_exclusive_user_id(&user_id)
82 .await
83 {
84 return Err!(Request(Exclusive("Username is reserved by an appservice.")));
85 }
86
87 Ok(get_username_availability::v3::Response { available: true })
88}