tuwunel_api/client/membership/
join.rs1use axum::extract::State;
2use futures::FutureExt;
3use ruma::{
4 CanonicalJsonObject, CanonicalJsonValue, RoomId,
5 api::client::membership::{join_room_by_id, join_room_by_id_or_alias},
6};
7use tuwunel_core::{Result, warn};
8
9use super::banned_room_check;
10use crate::{ClientIp, Ruma};
11
12#[tracing::instrument(skip_all, fields(%client), name = "join")]
21pub(crate) async fn join_room_by_id_route(
22 State(services): State<crate::State>,
23 ClientIp(client): ClientIp,
24 body: Ruma<join_room_by_id::v3::Request>,
25) -> Result<join_room_by_id::v3::Response> {
26 let sender_user = body.sender_user();
27
28 let room_id: &RoomId = &body.room_id;
29
30 banned_room_check(&services, sender_user, room_id, None, client).await?;
31
32 let extra_content = extra_member_content(body.json_body.as_ref());
33
34 let mut errors = 0_usize;
35 while let Err(e) = services
36 .membership
37 .join(
38 sender_user,
39 room_id,
40 None,
41 body.reason.clone(),
42 &[],
43 body.appservice_info.is_some(),
44 extra_content.clone(),
45 )
46 .boxed()
47 .await
48 {
49 errors = errors.saturating_add(1);
50 if errors >= services.config.max_join_attempts_per_join_request {
51 warn!(
52 "Several servers failed. Giving up for this request. Try again for different \
53 server selection."
54 );
55 return Err(e);
56 }
57 }
58
59 Ok(join_room_by_id::v3::Response { room_id: room_id.to_owned() })
60}
61
62#[tracing::instrument(skip_all, fields(%client), name = "join")]
72pub(crate) async fn join_room_by_id_or_alias_route(
73 State(services): State<crate::State>,
74 ClientIp(client): ClientIp,
75 body: Ruma<join_room_by_id_or_alias::v3::Request>,
76) -> Result<join_room_by_id_or_alias::v3::Response> {
77 let sender_user = body.sender_user();
78 let appservice_info = &body.appservice_info;
79
80 let (room_id, servers) = services
81 .alias
82 .maybe_resolve_with_servers(&body.room_id_or_alias, Some(&body.via))
83 .await?;
84
85 banned_room_check(&services, sender_user, &room_id, Some(&body.room_id_or_alias), client)
86 .await?;
87
88 let extra_content = extra_member_content(body.json_body.as_ref());
89
90 let mut errors = 0_usize;
91 while let Err(e) = services
92 .membership
93 .join(
94 sender_user,
95 &room_id,
96 Some(&body.room_id_or_alias),
97 body.reason.clone(),
98 &servers,
99 appservice_info.is_some(),
100 extra_content.clone(),
101 )
102 .boxed()
103 .await
104 {
105 errors = errors.saturating_add(1);
106 if errors >= services.config.max_join_attempts_per_join_request {
107 warn!(
108 "Several servers failed. Giving up for this request. Try again for different \
109 server selection."
110 );
111 return Err(e);
112 }
113 }
114
115 Ok(join_room_by_id_or_alias::v3::Response { room_id: room_id.clone() })
116}
117
118const RESERVED_JOIN_KEYS: [&str; 3] =
119 ["reason", "third_party_signed", "join_authorised_via_users_server"];
120
121fn extra_member_content(json_body: Option<&CanonicalJsonValue>) -> Option<CanonicalJsonObject> {
123 let CanonicalJsonValue::Object(object) = json_body? else {
124 return None;
125 };
126
127 let extra: CanonicalJsonObject = object
128 .iter()
129 .filter(|(key, _)| !RESERVED_JOIN_KEYS.contains(&key.as_str()))
130 .map(|(key, value)| (key.clone(), value.clone()))
131 .collect();
132
133 (!extra.is_empty()).then_some(extra)
134}