1use std::collections::BTreeMap;
2
3use axum::extract::State;
4use futures::{FutureExt, StreamExt};
5use itertools::Itertools;
6use ruma::{
7 CanonicalJsonObject, EventEncryptionAlgorithm, Int, OwnedRoomAliasId, OwnedRoomId,
8 OwnedUserId, RoomAliasId, RoomId, RoomVersionId, UserId,
9 api::client::room::{
10 self,
11 create_room::{
12 self, RoomPowerLevelsContentOverride,
13 v3::{CreationContent, RoomPreset},
14 },
15 },
16 events::{
17 StateEventType, TimelineEventType,
18 room::{
19 canonical_alias::RoomCanonicalAliasEventContent,
20 create::RoomCreateEventContent,
21 encryption::RoomEncryptionEventContent,
22 guest_access::{GuestAccess, RoomGuestAccessEventContent},
23 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
24 join_rules::{JoinRule, RoomJoinRulesEventContent},
25 member::{MembershipState, RoomMemberEventContent},
26 name::RoomNameEventContent,
27 power_levels::RoomPowerLevelsEventContent,
28 topic::RoomTopicEventContent,
29 },
30 },
31 int,
32 room_version_rules::{RoomIdFormatVersion, RoomVersionRules},
33 serde::{JsonObject, Raw},
34};
35use serde::Deserialize;
36use serde_json::{json, value::to_raw_value};
37use tuwunel_core::{
38 Err, Result, debug_info, debug_warn, err, info,
39 matrix::{
40 StateKey,
41 pdu::{Content, PduBuilder},
42 room_version,
43 },
44 utils::{BoolExt, IterStream, ReadyExt, option::OptionExt},
45 warn,
46};
47use tuwunel_service::{Services, appservice::RegistrationInfo, rooms::state::RoomMutexGuard};
48
49use crate::{Ruma, client::utils::invite_check};
50
51pub(crate) async fn create_room_route(
52 State(services): State<crate::State>,
53 body: Ruma<create_room::v3::Request>,
54) -> Result<create_room::v3::Response> {
55 can_create_room_check(&services, &body).await?;
56 can_publish_directory_check(&services, &body).await?;
57
58 let preset = body
60 .preset
61 .clone()
62 .unwrap_or(match &body.visibility {
63 | room::Visibility::Public => RoomPreset::PublicChat,
64 | _ => RoomPreset::PrivateChat, });
66
67 let (room_version, version_rules) = body
69 .room_version
70 .as_ref()
71 .map_or(Ok(&services.server.config.default_room_version), |version| {
72 services
73 .config
74 .supported_room_version(version)
75 .then_ok_or_else(version, || {
76 err!(Request(UnsupportedRoomVersion(
77 "This server does not support room version {version:?}"
78 )))
79 })
80 })
81 .and_then(|version| Ok((version, room_version::rules(version)?)))?;
82
83 let alias = body
85 .room_alias_name
86 .as_ref()
87 .map_async(|alias| room_alias_check(&services, alias, body.appservice_info.as_ref()))
88 .await
89 .transpose()?;
90
91 let next_count = services.globals.next_count();
94
95 let (room_id, state_lock) = match version_rules.room_id_format {
97 | RoomIdFormatVersion::V1 =>
98 create_create_event_legacy(&services, &body, room_version, &version_rules).await?,
99 | RoomIdFormatVersion::V2 =>
100 create_create_event(&services, &body, &preset, room_version, &version_rules)
101 .await
102 .map_err(|e| {
103 err!(Request(InvalidParam("Error while creating m.room.create event: {e}")))
104 })?,
105 };
106
107 let sender_user = body.sender_user();
108
109 apply_creator_join_pdu(&services, &body, sender_user, &room_id, &state_lock)
111 .boxed()
112 .await?;
113
114 apply_power_levels_pdu(
116 &services,
117 &body,
118 &preset,
119 &version_rules,
120 sender_user,
121 &room_id,
122 &state_lock,
123 )
124 .boxed()
125 .await?;
126
127 if let Some(room_alias_id) = &alias {
129 apply_canonical_alias_pdu(&services, room_alias_id, sender_user, &room_id, &state_lock)
130 .boxed()
131 .await?;
132 }
133
134 let initial_state =
136 apply_preset_state_pdus(&services, &body, &preset, sender_user, &room_id, &state_lock)
137 .boxed()
138 .await?;
139
140 apply_initial_state_pdus(
142 &services,
143 initial_state,
144 &preset,
145 sender_user,
146 &room_id,
147 &state_lock,
148 )
149 .boxed()
150 .await?;
151
152 apply_name_and_topic_pdus(&services, &body, sender_user, &room_id, &state_lock)
154 .boxed()
155 .await?;
156
157 drop(next_count);
158 drop(state_lock);
159
160 if (!body.invite.is_empty() || !body.invite_3pid.is_empty())
162 && invite_check(&services, sender_user, &room_id)
163 .await
164 .is_ok()
165 {
166 process_invites(&services, &body, sender_user, &room_id)
167 .boxed()
168 .await;
169 }
170
171 finalize_alias_and_directory(&services, &body, alias.as_deref(), sender_user, &room_id)
172 .await?;
173
174 info!("{sender_user} created a room with room ID {room_id}");
175
176 Ok(create_room::v3::Response::new(room_id))
177}
178
179async fn apply_creator_join_pdu(
180 services: &Services,
181 body: &Ruma<create_room::v3::Request>,
182 sender_user: &UserId,
183 room_id: &RoomId,
184 state_lock: &RoomMutexGuard,
185) -> Result {
186 let mut content = RoomMemberEventContent {
187 is_direct: Some(body.is_direct),
188 ..RoomMemberEventContent::new(MembershipState::Join)
189 };
190
191 services
192 .profile
193 .fill_profile_data(sender_user, &mut content)
194 .await;
195
196 services
197 .timeline
198 .build_and_append_pdu(
199 PduBuilder::state(sender_user.to_string(), &content),
200 sender_user,
201 room_id,
202 state_lock,
203 )
204 .await
205 .map(|_| ())
206}
207
208async fn apply_power_levels_pdu(
209 services: &Services,
210 body: &Ruma<create_room::v3::Request>,
211 preset: &RoomPreset,
212 version_rules: &RoomVersionRules,
213 sender_user: &UserId,
214 room_id: &RoomId,
215 state_lock: &RoomMutexGuard,
216) -> Result {
217 let users =
218 build_power_levels_users(services, body, preset, version_rules, sender_user).await;
219
220 let power_levels_content = default_power_levels_content(
221 version_rules,
222 body.power_level_content_override.as_ref(),
223 preset,
224 users,
225 )?;
226
227 services
228 .timeline
229 .build_and_append_pdu(
230 PduBuilder {
231 event_type: TimelineEventType::RoomPowerLevels,
232 content: to_raw_value(&power_levels_content)?.into(),
233 state_key: Some(StateKey::new()),
234 ..Default::default()
235 },
236 sender_user,
237 room_id,
238 state_lock,
239 )
240 .await
241 .map(|_| ())
242}
243
244async fn build_power_levels_users(
245 services: &Services,
246 body: &Ruma<create_room::v3::Request>,
247 preset: &RoomPreset,
248 version_rules: &RoomVersionRules,
249 sender_user: &UserId,
250) -> BTreeMap<OwnedUserId, Int> {
251 let seed = version_rules
252 .authorization
253 .explicitly_privilege_room_creators
254 .or(|| (sender_user.to_owned(), int!(100)))
255 .into_iter()
256 .collect::<BTreeMap<_, _>>();
257
258 let trusted_invitees = *preset == RoomPreset::TrustedPrivateChat
259 && !version_rules
260 .authorization
261 .additional_room_creators;
262
263 if !trusted_invitees {
264 return seed;
265 }
266
267 body.invite
268 .iter()
269 .stream()
270 .filter(|&invite| invite_allowed(services, sender_user, invite))
271 .ready_fold(seed, |mut users, invite| {
272 users.insert(invite.clone(), int!(100));
273 users
274 })
275 .await
276}
277
278async fn apply_canonical_alias_pdu(
279 services: &Services,
280 room_alias_id: &RoomAliasId,
281 sender_user: &UserId,
282 room_id: &RoomId,
283 state_lock: &RoomMutexGuard,
284) -> Result {
285 services
286 .timeline
287 .build_and_append_pdu(
288 PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
289 alias: Some(room_alias_id.to_owned()),
290 alt_aliases: vec![],
291 }),
292 sender_user,
293 room_id,
294 state_lock,
295 )
296 .await
297 .map(|_| ())
298}
299
300async fn apply_preset_state_pdus(
301 services: &Services,
302 body: &Ruma<create_room::v3::Request>,
303 preset: &RoomPreset,
304 sender_user: &UserId,
305 room_id: &RoomId,
306 state_lock: &RoomMutexGuard,
307) -> Result<Vec<InitialEvent>> {
308 let mut initial_state = body
309 .initial_state
310 .iter()
311 .map(|state| Ok(state.deserialize_as_unchecked::<InitialEvent>()?))
312 .filter_ok(|event| {
313 services.config.allow_encryption || event.event_type != StateEventType::RoomEncryption
314 })
315 .filter_ok(|event| {
316 if event.content.json().get() == "{}" {
320 debug_warn!("skipping empty initial state event of type {}", event.event_type);
321 false
322 } else {
323 true
324 }
325 })
326 .filter_ok(|event| body.name.is_none() || event.event_type != StateEventType::RoomName)
327 .filter_ok(|event| body.topic.is_none() || event.event_type != StateEventType::RoomTopic)
328 .collect::<Result<Vec<_>>>()?;
329
330 let join_rule_pdubuilder =
331 take_initial(&mut initial_state, &StateEventType::RoomJoinRules, "")
332 .map(Into::into)
333 .unwrap_or_else(|| {
334 PduBuilder::state(
335 String::new(),
336 &RoomJoinRulesEventContent::new(match preset {
337 | RoomPreset::PublicChat => JoinRule::Public,
338 | _ => JoinRule::Invite,
340 }),
341 )
342 });
343
344 let history_visibility_pdubuilder =
345 take_initial(&mut initial_state, &StateEventType::RoomHistoryVisibility, "")
346 .map(Into::into)
347 .unwrap_or_else(|| {
348 PduBuilder::state(
349 String::new(),
350 &RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
351 )
352 });
353
354 let guest_access_pdubuilder =
355 take_initial(&mut initial_state, &StateEventType::RoomGuestAccess, "")
356 .map(Into::into)
357 .unwrap_or_else(|| {
358 PduBuilder::state(
359 String::new(),
360 &RoomGuestAccessEventContent::new(match preset {
361 | RoomPreset::PublicChat => GuestAccess::Forbidden,
362 | _ => GuestAccess::CanJoin,
363 }),
364 )
365 });
366
367 services
369 .timeline
370 .build_and_append_pdu(join_rule_pdubuilder, sender_user, room_id, state_lock)
371 .boxed()
372 .await?;
373
374 services
376 .timeline
377 .build_and_append_pdu(history_visibility_pdubuilder, sender_user, room_id, state_lock)
378 .boxed()
379 .await?;
380
381 services
383 .timeline
384 .build_and_append_pdu(guest_access_pdubuilder, sender_user, room_id, state_lock)
385 .boxed()
386 .await?;
387
388 Ok(initial_state)
389}
390
391async fn apply_initial_state_pdus(
392 services: &Services,
393 initial_state: Vec<InitialEvent>,
394 preset: &RoomPreset,
395 sender_user: &UserId,
396 room_id: &RoomId,
397 state_lock: &RoomMutexGuard,
398) -> Result {
399 let is_encrypted = initial_state
400 .iter()
401 .any(|event| event.event_type == StateEventType::RoomEncryption);
402
403 for event in initial_state {
404 services
405 .timeline
406 .build_and_append_pdu(event.into(), sender_user, room_id, state_lock)
407 .boxed()
408 .await?;
409 }
410
411 if !services.config.allow_encryption || is_encrypted {
412 return Ok(());
413 }
414
415 let config = services
416 .config
417 .encryption_enabled_by_default_for_room_type
418 .as_deref();
419
420 let should_encrypt = match config {
421 | Some("all") => true,
422 | Some("invite") =>
423 matches!(preset, RoomPreset::PrivateChat | RoomPreset::TrustedPrivateChat),
424 | _ => false,
425 };
426
427 if !should_encrypt {
428 return Ok(());
429 }
430
431 let algorithm = EventEncryptionAlgorithm::MegolmV1AesSha2;
432 let content = RoomEncryptionEventContent::new(algorithm);
433 services
434 .timeline
435 .build_and_append_pdu(
436 PduBuilder::state(String::new(), &content),
437 sender_user,
438 room_id,
439 state_lock,
440 )
441 .boxed()
442 .await?;
443
444 Ok(())
445}
446
447async fn apply_name_and_topic_pdus(
448 services: &Services,
449 body: &Ruma<create_room::v3::Request>,
450 sender_user: &UserId,
451 room_id: &RoomId,
452 state_lock: &RoomMutexGuard,
453) -> Result {
454 if let Some(name) = &body.name {
455 services
456 .timeline
457 .build_and_append_pdu(
458 PduBuilder::state(String::new(), &RoomNameEventContent::new(name.clone())),
459 sender_user,
460 room_id,
461 state_lock,
462 )
463 .boxed()
464 .await?;
465 }
466
467 if let Some(topic) = &body.topic {
468 services
469 .timeline
470 .build_and_append_pdu(
471 PduBuilder::state(String::new(), &RoomTopicEventContent::new(topic.clone())),
472 sender_user,
473 room_id,
474 state_lock,
475 )
476 .boxed()
477 .await?;
478 }
479
480 Ok(())
481}
482
483async fn process_invites(
484 services: &Services,
485 body: &Ruma<create_room::v3::Request>,
486 sender_user: &UserId,
487 room_id: &RoomId,
488) {
489 body.invite
491 .iter()
492 .stream()
493 .filter(|&user_id| invite_allowed(services, sender_user, user_id))
494 .for_each(|user_id| async {
495 if let Err(e) = services
496 .membership
497 .invite(sender_user, user_id, room_id, None, body.is_direct)
498 .boxed()
499 .await
500 {
501 warn!(%e, "Failed to send invite");
502 }
503 })
504 .await;
505}
506
507async fn invite_allowed(services: &Services, sender_user: &UserId, invitee: &UserId) -> bool {
510 !(services
511 .users
512 .user_is_ignored(sender_user, invitee)
513 .await || services
514 .users
515 .user_is_ignored(invitee, sender_user)
516 .await || services.users.invites_blocked(invitee).await)
517}
518
519async fn finalize_alias_and_directory(
520 services: &Services,
521 body: &Ruma<create_room::v3::Request>,
522 alias: Option<&RoomAliasId>,
523 sender_user: &UserId,
524 room_id: &RoomId,
525) -> Result {
526 if let Some(alias) = alias {
527 services
528 .alias
529 .set_alias_by(alias, room_id, sender_user)?;
530 }
531
532 if body.visibility == room::Visibility::Public {
533 services.directory.set_public(room_id);
534
535 if services.server.config.admin_room_notices {
536 services
537 .admin
538 .send_text(&format!("{sender_user} made {room_id} public to the room directory"))
539 .await;
540 }
541 info!("{sender_user} made {0} public to the room directory", room_id);
542 }
543
544 Ok(())
545}
546
547async fn create_create_event(
548 services: &Services,
549 body: &Ruma<create_room::v3::Request>,
550 preset: &RoomPreset,
551 room_version: &RoomVersionId,
552 version_rules: &RoomVersionRules,
553) -> Result<(OwnedRoomId, RoomMutexGuard)> {
554 let _sender_user = body.sender_user();
555
556 let mut create_content = match &body.creation_content {
557 | Some(content) => {
558 let mut content = content
559 .deserialize_as_unchecked::<CanonicalJsonObject>()
560 .map_err(|e| {
561 err!(Request(BadJson(error!(
562 "Failed to deserialise content as canonical JSON: {e}"
563 ))))
564 })?;
565
566 if !services.config.federate_created_rooms
567 && (!services.config.allow_federation || !content.contains_key("m.federate"))
568 {
569 content.insert("m.federate".into(), json!(false).try_into()?);
570 }
571
572 content.insert(
573 "room_version".into(),
574 json!(room_version.as_str())
575 .try_into()
576 .map_err(|e| err!(Request(BadJson("Invalid creation content: {e}"))))?,
577 );
578
579 content
580 },
581 | None => {
582 let content = RoomCreateEventContent::new_v11();
583
584 let mut content =
585 serde_json::from_str::<CanonicalJsonObject>(to_raw_value(&content)?.get())?;
586
587 if !services.config.federate_created_rooms {
588 content.insert("m.federate".into(), json!(false).try_into()?);
589 }
590
591 content.insert("room_version".into(), json!(room_version.as_str()).try_into()?);
592 content
593 },
594 };
595
596 if version_rules
597 .authorization
598 .additional_room_creators
599 {
600 let mut additional_creators = body
601 .creation_content
602 .as_ref()
603 .and_then(|c| {
604 c.deserialize_as_unchecked::<CreationContent>()
605 .ok()
606 })
607 .unwrap_or_default()
608 .additional_creators;
609
610 if *preset == RoomPreset::TrustedPrivateChat {
611 additional_creators.extend(body.invite.clone());
612 }
613
614 additional_creators.sort();
615 additional_creators.dedup();
616 if !additional_creators.is_empty() {
617 create_content
618 .insert("additional_creators".into(), json!(additional_creators).try_into()?);
619 }
620 }
621
622 let room_id = ruma::room_id!("!thiswillbereplaced").to_owned();
624 let state_lock = services.state.mutex.lock(&room_id).await;
625 let create_event_id = services
626 .timeline
627 .build_and_append_pdu(
628 PduBuilder {
629 event_type: TimelineEventType::RoomCreate,
630 content: to_raw_value(&create_content)?.into(),
631 state_key: Some(StateKey::new()),
632 ..Default::default()
633 },
634 body.sender_user(),
635 &room_id,
636 &state_lock,
637 )
638 .boxed()
639 .await?;
640
641 drop(state_lock);
642
643 let room_id = OwnedRoomId::from_parts('!', create_event_id.localpart(), None)?;
645 let state_lock = services.state.mutex.lock(&room_id).await;
646
647 Ok((room_id, state_lock))
648}
649
650async fn create_create_event_legacy(
651 services: &Services,
652 body: &Ruma<create_room::v3::Request>,
653 room_version: &RoomVersionId,
654 _version_rules: &RoomVersionRules,
655) -> Result<(OwnedRoomId, RoomMutexGuard)> {
656 let room_id: OwnedRoomId = match &body.room_id {
657 | None => RoomId::new_v1(&services.server.name),
658 | Some(custom_id) => custom_room_id_check(services, custom_id).await?,
659 };
660
661 let state_lock = services.state.mutex.lock(&room_id).await;
662
663 let _short_id = services
664 .short
665 .get_or_create_shortroomid(&room_id)
666 .await;
667
668 let create_content = match &body.creation_content {
669 | Some(content) => {
670 use RoomVersionId::*;
671
672 let mut content = content
673 .deserialize_as_unchecked::<CanonicalJsonObject>()
674 .map_err(|e| {
675 err!(Request(BadJson(error!(
676 "Failed to deserialise content as canonical JSON: {e}"
677 ))))
678 })?;
679
680 match room_version {
681 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
682 content.insert(
683 "creator".into(),
684 json!(body.sender_user())
685 .try_into()
686 .map_err(|e| {
687 err!(Request(BadJson(debug_error!(
688 "Invalid creation content: {e}"
689 ))))
690 })?,
691 );
692 },
693 | _ => {
694 },
696 }
697
698 if !services.config.federate_created_rooms
699 && (!services.config.allow_federation || !content.contains_key("m.federate"))
700 {
701 content.insert("m.federate".into(), json!(false).try_into()?);
702 }
703
704 content.insert(
705 "room_version".into(),
706 json!(room_version.as_str())
707 .try_into()
708 .map_err(|e| err!(Request(BadJson("Invalid creation content: {e}"))))?,
709 );
710
711 content
712 },
713 | None => {
714 use RoomVersionId::*;
715
716 let content = match room_version {
717 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
718 RoomCreateEventContent::new_v1(body.sender_user().to_owned()),
719 | _ => RoomCreateEventContent::new_v11(),
720 };
721
722 let mut content =
723 serde_json::from_str::<CanonicalJsonObject>(to_raw_value(&content)?.get())?;
724
725 if !services.config.federate_created_rooms {
726 content.insert("m.federate".into(), json!(false).try_into()?);
727 }
728
729 content.insert("room_version".into(), json!(room_version.as_str()).try_into()?);
730 content
731 },
732 };
733
734 services
736 .timeline
737 .build_and_append_pdu(
738 PduBuilder {
739 event_type: TimelineEventType::RoomCreate,
740 content: to_raw_value(&create_content)?.into(),
741 state_key: Some(StateKey::new()),
742 ..Default::default()
743 },
744 body.sender_user(),
745 &room_id,
746 &state_lock,
747 )
748 .boxed()
749 .await?;
750
751 Ok((room_id, state_lock))
752}
753
754fn default_power_levels_content(
756 version_rules: &RoomVersionRules,
757 power_level_content_override: Option<&Raw<RoomPowerLevelsContentOverride>>,
758 preset: &RoomPreset,
759 users: BTreeMap<OwnedUserId, Int>,
760) -> Result<serde_json::Value> {
761 use serde_json::to_value;
762
763 let mut power_levels_content = RoomPowerLevelsEventContent::new(&version_rules.authorization);
764 power_levels_content.users = users;
765
766 let mut power_levels_content = to_value(power_levels_content)?;
767
768 power_levels_content["events"]["m.room.power_levels"] = json!(100);
771 power_levels_content["events"]["m.room.server_acl"] = json!(100);
772 power_levels_content["events"]["m.room.encryption"] = json!(100);
773 power_levels_content["events"]["m.room.history_visibility"] = json!(100);
774
775 if version_rules
776 .authorization
777 .explicitly_privilege_room_creators
778 {
779 power_levels_content["events"]["m.room.tombstone"] = json!(150);
780 } else {
781 power_levels_content["events"]["m.room.tombstone"] = json!(100);
782 }
783
784 power_levels_content["events"]["org.matrix.msc3381.poll.response"] = json!(0);
787 power_levels_content["events"]["m.poll.response"] = json!(0);
788
789 if *preset == RoomPreset::PublicChat {
792 power_levels_content["invite"] = json!(50);
793 power_levels_content["events"]["m.call.invite"] = json!(50);
794 power_levels_content["events"]["m.call"] = json!(50);
795 power_levels_content["events"]["m.call.member"] = json!(50);
796 power_levels_content["events"]["org.matrix.msc3401.call"] = json!(50);
797 power_levels_content["events"]["org.matrix.msc3401.call.member"] = json!(50);
798 }
799
800 if let Some(power_level_content_override) = power_level_content_override {
801 let json: JsonObject = serde_json::from_str(power_level_content_override.json().get())
802 .map_err(|e| err!(Request(BadJson("Invalid power_level_content_override: {e:?}"))))?;
803
804 for (key, value) in json {
805 power_levels_content[key] = value;
806 }
807 }
808
809 Ok(power_levels_content)
810}
811
812async fn room_alias_check(
814 services: &Services,
815 room_alias_name: &str,
816 appservice_info: Option<&RegistrationInfo>,
817) -> Result<OwnedRoomAliasId> {
818 if room_alias_name.contains(':') {
820 return Err!(Request(InvalidParam(
821 "Room alias contained `:` which is not allowed. Please note that this expects a \
822 localpart, not the full room alias.",
823 )));
824 } else if room_alias_name.contains(char::is_whitespace) {
825 return Err!(Request(InvalidParam(
826 "Room alias contained spaces which is not a valid room alias.",
827 )));
828 }
829
830 if services
832 .config
833 .forbidden_alias_names
834 .is_match(room_alias_name)
835 {
836 return Err!(Request(Unknown("Room alias name is forbidden.")));
837 }
838
839 let server_name = services.globals.server_name();
840 let full_room_alias = OwnedRoomAliasId::parse(format!("#{room_alias_name}:{server_name}"))
841 .map_err(|e| {
842 err!(Request(InvalidParam(debug_error!(
843 ?e,
844 ?room_alias_name,
845 "Failed to parse room alias.",
846 ))))
847 })?;
848
849 if services
850 .alias
851 .resolve_local_alias(&full_room_alias)
852 .await
853 .is_ok()
854 {
855 return Err!(Request(RoomInUse("Room alias already exists.")));
856 }
857
858 if let Some(info) = appservice_info {
859 if !info.aliases.is_match(full_room_alias.as_str()) {
860 return Err!(Request(Exclusive("Room alias is not in namespace.")));
861 }
862 } else if services
863 .appservice
864 .is_exclusive_alias(&full_room_alias)
865 .await
866 {
867 return Err!(Request(Exclusive("Room alias reserved by appservice.",)));
868 }
869
870 debug_info!("Full room alias: {full_room_alias}");
871
872 Ok(full_room_alias)
873}
874
875async fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
877 if services
879 .config
880 .forbidden_alias_names
881 .is_match(custom_room_id)
882 {
883 return Err!(Request(Unknown("Custom room ID is forbidden.")));
884 }
885
886 if custom_room_id.contains(':') {
887 return Err!(Request(InvalidParam(
888 "Custom room ID contained `:` which is not allowed. Please note that this expects a \
889 localpart, not the full room ID.",
890 )));
891 } else if custom_room_id.contains(char::is_whitespace) {
892 return Err!(Request(InvalidParam(
893 "Custom room ID contained spaces which is not valid."
894 )));
895 }
896
897 let server_name = services.globals.server_name();
898 let full_room_id = format!("!{custom_room_id}:{server_name}");
899
900 let room_id = OwnedRoomId::parse(full_room_id)
901 .inspect(|full_room_id| debug_info!(?full_room_id, "Full custom room ID"))
902 .inspect_err(|e| {
903 warn!(?e, ?custom_room_id, "Failed to create room with custom room ID");
904 })?;
905
906 if services
908 .short
909 .get_shortroomid(&room_id)
910 .await
911 .is_ok()
912 {
913 return Err!(Request(RoomInUse("Room with that custom room ID already exists",)));
914 }
915
916 Ok(room_id)
917}
918
919async fn can_publish_directory_check(
920 services: &Services,
921 body: &Ruma<create_room::v3::Request>,
922) -> Result {
923 if !services
924 .server
925 .config
926 .lockdown_public_room_directory
927 || body.appservice_info.is_some()
928 || body.visibility != room::Visibility::Public
929 || services
930 .admin
931 .user_is_admin(body.sender_user())
932 .await
933 {
934 return Ok(());
935 }
936
937 let msg = format!(
938 "Non-admin user {} tried to publish new to the directory while \
939 lockdown_public_room_directory is enabled",
940 body.sender_user(),
941 );
942
943 warn!("{msg}");
944 if services.server.config.admin_room_notices {
945 services.admin.notice(&msg).await;
946 }
947
948 Err!(Request(Forbidden("Publishing rooms to the room directory is not allowed")))
949}
950
951async fn can_create_room_check(
952 services: &Services,
953 body: &Ruma<create_room::v3::Request>,
954) -> Result {
955 if !services.config.allow_room_creation
956 && body.appservice_info.is_none()
957 && !services
958 .admin
959 .user_is_admin(body.sender_user())
960 .await
961 {
962 return Err!(Request(Forbidden("Room creation has been disabled.",)));
963 }
964
965 Ok(())
966}
967
968#[derive(Deserialize)]
969struct InitialEvent {
970 #[serde(rename = "type")]
971 event_type: StateEventType,
972
973 #[serde(default = "StateKey::new")]
974 state_key: StateKey,
975
976 content: Content,
977}
978
979impl From<InitialEvent> for PduBuilder {
980 fn from(value: InitialEvent) -> Self {
981 Self {
982 event_type: value.event_type.into(),
983 content: value.content,
984 unsigned: None,
985 state_key: Some(value.state_key),
986 redacts: None,
987 timestamp: None,
988 }
989 }
990}
991
992fn take_initial(
993 initial_state: &mut Vec<InitialEvent>,
994 event_type: &StateEventType,
995 state_key: &str,
996) -> Option<InitialEvent> {
997 initial_state
998 .extract_if(.., |event| &event.event_type == event_type && event.state_key == state_key)
999 .next()
1000}