1use axum::extract::State;
2use futures::{FutureExt, TryFutureExt, TryStreamExt};
3use ruma::{
4 OwnedEventId, OwnedRoomAliasId, RoomId, UserId,
5 api::client::state::{
6 get_state_event_for_key::{self, v3::StateEventFormat},
7 get_state_events, send_state_event,
8 },
9 events::{
10 AnyStateEventContent, StateEventType,
11 room::{
12 canonical_alias::RoomCanonicalAliasEventContent,
13 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
14 join_rules::{JoinRule, RoomJoinRulesEventContent},
15 member::{MembershipState, RoomMemberEventContent},
16 server_acl::RoomServerAclEventContent,
17 },
18 },
19 serde::Raw,
20};
21use serde_json::json;
22use tuwunel_core::{
23 Err, Result, err, is_false,
24 matrix::{Event, pdu::PduBuilder},
25 utils::{BoolExt, stream::TryBroadbandExt},
26};
27use tuwunel_service::Services;
28
29use crate::{Ruma, RumaResponse, client::with_membership};
30
31pub(crate) async fn send_state_event_for_key_route(
35 State(services): State<crate::State>,
36 body: Ruma<send_state_event::v3::Request>,
37) -> Result<send_state_event::v3::Response> {
38 let sender_user = body.sender_user();
39
40 Ok(send_state_event::v3::Response {
41 event_id: send_state_event_for_key_helper(
42 &services,
43 sender_user,
44 &body.room_id,
45 &body.event_type,
46 &body.body.body,
47 &body.state_key,
48 if body.appservice_info.is_some() {
49 body.timestamp
50 } else {
51 None
52 },
53 )
54 .await?,
55 })
56}
57
58pub(crate) async fn send_state_event_for_empty_key_route(
62 State(services): State<crate::State>,
63 body: Ruma<send_state_event::v3::Request>,
64) -> Result<RumaResponse<send_state_event::v3::Response>> {
65 send_state_event_for_key_route(State(services), body)
66 .boxed()
67 .await
68 .map(RumaResponse)
69}
70
71pub(crate) async fn get_state_events_route(
78 State(services): State<crate::State>,
79 body: Ruma<get_state_events::v3::Request>,
80) -> Result<get_state_events::v3::Response> {
81 let sender_user = body.sender_user();
82
83 if !services
84 .state_accessor
85 .user_can_see_state_events(sender_user, &body.room_id)
86 .await
87 {
88 return Err!(Request(Forbidden("You don't have permission to view the room state.")));
89 }
90
91 let encrypted = services
92 .state_accessor
93 .is_encrypted_room(&body.room_id)
94 .await;
95
96 let room_state = services
97 .state_accessor
98 .room_state_full_pdus(&body.room_id)
99 .map_ok(Event::into_pdu)
100 .broad_and_then(async |pdu| {
101 Ok(with_membership(&services, pdu, sender_user, encrypted).await)
102 })
103 .map_ok(Event::into_format)
104 .try_collect()
105 .await?;
106
107 Ok(get_state_events::v3::Response { room_state })
108}
109
110pub(crate) async fn get_state_events_for_key_route(
119 State(services): State<crate::State>,
120 body: Ruma<get_state_event_for_key::v3::Request>,
121) -> Result<get_state_event_for_key::v3::Response> {
122 let sender_user = body.sender_user();
123
124 if !services
125 .state_accessor
126 .user_can_see_state_events(sender_user, &body.room_id)
127 .await
128 {
129 return Err!(Request(NotFound(debug_warn!(
130 "You don't have permission to view the room state."
131 ))));
132 }
133
134 let event = services
135 .state_accessor
136 .room_state_get(&body.room_id, &body.event_type, &body.state_key)
137 .await
138 .map_err(|e| {
139 err!(Request(NotFound(debug_warn!(
140 room_id = ?body.room_id,
141 event_type = ?body.event_type,
142 "Failed to get state event: {e}.",
143 ))))
144 })?;
145
146 let event_or_content = match body.format {
147 | StateEventFormat::Event => json!({
148 "content": event.content(),
149 "event_id": event.event_id(),
150 "origin_server_ts": event.origin_server_ts(),
151 "room_id": event.room_id(),
152 "sender": event.sender(),
153 "state_key": event.state_key(),
154 "type": event.kind(),
155 "unsigned": event.unsigned(),
156 }),
157
158 | _ => event.get_content_as_value(),
159 };
160
161 let event_or_content =
162 serde_json::value::to_raw_value(&event_or_content).expect("serializable JSON value");
163
164 Ok(get_state_event_for_key::v3::Response::new(event_or_content))
165}
166
167pub(crate) async fn get_state_events_for_empty_key_route(
176 State(services): State<crate::State>,
177 body: Ruma<get_state_event_for_key::v3::Request>,
178) -> Result<RumaResponse<get_state_event_for_key::v3::Response>> {
179 get_state_events_for_key_route(State(services), body)
180 .await
181 .map(RumaResponse)
182}
183
184async fn send_state_event_for_key_helper(
185 services: &Services,
186 sender: &UserId,
187 room_id: &RoomId,
188 event_type: &StateEventType,
189 json: &Raw<AnyStateEventContent>,
190 state_key: &str,
191 timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
192) -> Result<OwnedEventId> {
193 allowed_to_send_state_event(services, room_id, event_type, state_key, json).await?;
194 let state_lock = services.state.mutex.lock(room_id).await;
195 let event_id = services
196 .timeline
197 .build_and_append_pdu(
198 PduBuilder {
199 event_type: event_type.to_string().into(),
200 content: serde_json::from_str(json.json().get())?,
201 state_key: Some(state_key.into()),
202 timestamp,
203 ..Default::default()
204 },
205 sender,
206 room_id,
207 &state_lock,
208 )
209 .boxed()
210 .await?;
211
212 Ok(event_id)
213}
214
215async fn allowed_to_send_state_event(
216 services: &Services,
217 room_id: &RoomId,
218 event_type: &StateEventType,
219 state_key: &str,
220 json: &Raw<AnyStateEventContent>,
221) -> Result {
222 match event_type {
223 | StateEventType::RoomCreate => {
224 return Err!(Request(BadJson(debug_warn!(
225 ?room_id,
226 "You cannot update m.room.create after a room has been created."
227 ))));
228 },
229 | StateEventType::RoomServerAcl => {
230 match json.deserialize_as_unchecked::<RoomServerAclEventContent>() {
233 | Ok(acl_content) => {
234 if acl_content.allow_is_empty() {
235 return Err!(Request(BadJson(debug_warn!(
236 ?room_id,
237 "Sending an ACL event with an empty allow key will permanently \
238 brick the room for non-tuwunel's as this equates to no servers \
239 being allowed to participate in this room."
240 ))));
241 }
242
243 if acl_content.deny_contains("*") && acl_content.allow_contains("*") {
244 return Err!(Request(BadJson(debug_warn!(
245 ?room_id,
246 "Sending an ACL event with a deny and allow key value of \"*\" will \
247 permanently brick the room for non-tuwunel's as this equates to no \
248 servers being allowed to participate in this room."
249 ))));
250 }
251
252 if acl_content.deny_contains("*")
253 && !acl_content.is_allowed(services.globals.server_name())
254 && !acl_content.allow_contains(services.globals.server_name().as_str())
255 {
256 return Err!(Request(BadJson(debug_warn!(
257 ?room_id,
258 "Sending an ACL event with a deny key value of \"*\" and without \
259 your own server name in the allow key will result in you being \
260 unable to participate in this room."
261 ))));
262 }
263
264 if !acl_content.allow_contains("*")
265 && !acl_content.is_allowed(services.globals.server_name())
266 && !acl_content.allow_contains(services.globals.server_name().as_str())
267 {
268 return Err!(Request(BadJson(debug_warn!(
269 ?room_id,
270 "Sending an ACL event for an allow key without \"*\" and without \
271 your own server name in the allow key will result in you being \
272 unable to participate in this room."
273 ))));
274 }
275 },
276 | Err(e) => {
277 return Err!(Request(BadJson(debug_warn!(
278 "Room server ACL event is invalid: {e}"
279 ))));
280 },
281 }
282 },
283 | StateEventType::RoomEncryption =>
284 if !services.config.allow_encryption {
286 return Err!(Request(Forbidden("Encryption is disabled on this homeserver.")));
287 },
288 | StateEventType::RoomJoinRules => {
289 if let Ok(admin_room_id) = services.admin.get_admin_room().await
291 && admin_room_id == room_id
292 {
293 match json.deserialize_as_unchecked::<RoomJoinRulesEventContent>() {
294 | Ok(join_rule) =>
295 if join_rule.join_rule == JoinRule::Public {
296 return Err!(Request(Forbidden(
297 "Admin room is a sensitive room, it cannot be made public"
298 )));
299 },
300 | Err(e) => {
301 return Err!(Request(BadJson(debug_warn!(
302 "Room join rules event is invalid: {e}"
303 ))));
304 },
305 }
306 }
307 },
308 | StateEventType::RoomHistoryVisibility => {
309 if let Ok(admin_room_id) = services.admin.get_admin_room().await {
311 match json.deserialize_as_unchecked::<RoomHistoryVisibilityEventContent>() {
312 | Ok(visibility_content) => {
313 if admin_room_id == room_id
314 && visibility_content.history_visibility
315 == HistoryVisibility::WorldReadable
316 {
317 return Err!(Request(Forbidden(
318 "Admin room is a sensitive room, it cannot be made world \
319 readable (public room history)."
320 )));
321 }
322 },
323 | Err(e) => {
324 return Err!(Request(BadJson(debug_warn!(
325 "Room history visibility event is invalid: {e}"
326 ))));
327 },
328 }
329 }
330 },
331 | StateEventType::RoomCanonicalAlias => {
332 match json.deserialize_as_unchecked::<RoomCanonicalAliasEventContent>() {
333 | Ok(canonical_alias_content) => {
334 let current_event = services
335 .state_accessor
336 .room_state_get_content::<RoomCanonicalAliasEventContent>(
337 room_id,
338 &StateEventType::RoomCanonicalAlias,
339 "",
340 )
341 .await
342 .ok();
343
344 let current_aliases: Vec<OwnedRoomAliasId> = current_event
345 .map(|content| content.aliases().cloned().collect())
346 .unwrap_or_default();
347
348 let aliases = canonical_alias_content
349 .aliases()
350 .filter(|alias| !current_aliases.contains(alias));
351
352 for alias in aliases {
353 let (alias_room_id, _servers) = services
354 .alias
355 .resolve_alias(alias)
356 .await
357 .map_err(|e| {
358 err!(Request(BadAlias("Failed resolving alias \"{alias}\": {e}")))
359 })?;
360
361 if alias_room_id != room_id {
362 return Err!(Request(BadAlias(
363 "Room alias {alias} does not belong to room {room_id}"
364 )));
365 }
366 }
367 },
368 | Err(e) => {
369 return Err!(Request(InvalidParam(debug_warn!(
370 "Room canonical alias event is invalid: {e}"
371 ))));
372 },
373 }
374 },
375 | StateEventType::RoomMember => {
376 match json.deserialize_as_unchecked::<RoomMemberEventContent>() {
377 | Ok(membership_content) => {
378 let Ok(target_user) = UserId::parse(state_key) else {
379 return Err!(Request(BadJson(
380 "Membership event has invalid or non-existent state key"
381 )));
382 };
383
384 if membership_content.membership == MembershipState::Invite
385 && services.globals.user_is_local(&target_user)
386 && services.users.invites_blocked(&target_user).await
387 {
388 return Err!(Request(InviteBlocked(
389 "{target_user} has blocked invites."
390 )));
391 }
392
393 if let Some(authorising_user) =
394 membership_content.join_authorized_via_users_server
395 {
396 if membership_content.membership != MembershipState::Join {
397 return Err!(Request(BadJson(
398 "join_authorised_via_users_server is only for member joins"
399 )));
400 }
401
402 if !services.globals.user_is_local(&authorising_user) {
403 return Err!(Request(InvalidParam(
404 "Authorising user {authorising_user} does not belong to this \
405 homeserver"
406 )));
407 }
408
409 services
410 .state_cache
411 .is_joined(&authorising_user, room_id)
412 .map(is_false!())
413 .map(BoolExt::into_result)
414 .map_err(|()| {
415 err!(Request(InvalidParam(
416 "Authorising user {authorising_user} is not in the room. \
417 They cannot authorise the join."
418 )))
419 })
420 .await?;
421 }
422 },
423 | Err(e) => {
424 return Err!(Request(BadJson(
425 "Membership content must have a valid JSON body with at least a valid \
426 membership state: {e}"
427 )));
428 },
429 }
430 },
431 | _ => (),
432 }
433
434 Ok(())
435}