conduit/src/client_server/room.rs

330 lines
11 KiB
Rust
Raw Normal View History

2020-07-30 16:14:47 +00:00
use super::State;
use crate::{pdu::PduBuilder, ConduitResult, Database, Error, Ruma};
use ruma::{
api::client::{
error::ErrorKind,
r0::room::{self, create_room, get_room_event},
},
events::{
room::{guest_access, history_visibility, join_rules, member, name, topic},
EventType,
},
RoomAliasId, RoomId, RoomVersionId,
};
use std::{collections::BTreeMap, convert::TryFrom};
#[cfg(feature = "conduit_bin")]
use rocket::{get, post};
#[cfg_attr(
feature = "conduit_bin",
post("/_matrix/client/r0/createRoom", data = "<body>")
)]
pub fn create_room_route(
db: State<'_, Database>,
2020-09-08 15:32:03 +00:00
body: Ruma<create_room::Request<'_>>,
2020-07-30 16:14:47 +00:00
) -> ConduitResult<create_room::Response> {
let sender_id = body.sender_id.as_ref().expect("user is authenticated");
let room_id = RoomId::new(db.globals.server_name());
let alias = body
.room_alias_name
.as_ref()
.map_or(Ok(None), |localpart| {
// TODO: Check for invalid characters and maximum length
let alias =
RoomAliasId::try_from(format!("#{}:{}", localpart, db.globals.server_name()))
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias."))?;
if db.rooms.id_from_alias(&alias)?.is_some() {
Err(Error::BadRequest(
ErrorKind::RoomInUse,
"Room alias already exists.",
))
} else {
Ok(Some(alias))
}
})?;
let mut content = ruma::events::room::create::CreateEventContent::new(sender_id.clone());
2020-09-08 15:32:03 +00:00
content.federate = body.creation_content.federate;
content.predecessor = body.creation_content.predecessor.clone();
2020-07-30 16:14:47 +00:00
content.room_version = RoomVersionId::Version6;
// 1. The room create event
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomCreate,
content: serde_json::to_value(content).expect("event is valid, we just created it"),
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 2. Let the room creator join
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomMember,
content: serde_json::to_value(member::MemberEventContent {
membership: member::MembershipState::Join,
displayname: db.users.displayname(&sender_id)?,
avatar_url: db.users.avatar_url(&sender_id)?,
2020-09-08 15:32:03 +00:00
is_direct: Some(body.is_direct),
2020-07-30 16:14:47 +00:00
third_party_invite: None,
})
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some(sender_id.to_string()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 3. Power levels
let mut users = BTreeMap::new();
users.insert(sender_id.clone(), 100.into());
for invite_ in &body.invite {
users.insert(invite_.clone(), 100.into());
}
let power_levels_content = if let Some(power_levels) = &body.power_level_content_override {
serde_json::from_str(power_levels.json().get()).map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
})?
} else {
serde_json::to_value(ruma::events::room::power_levels::PowerLevelsEventContent {
ban: 50.into(),
events: BTreeMap::new(),
events_default: 0.into(),
invite: 50.into(),
kick: 50.into(),
redact: 50.into(),
state_default: 50.into(),
users,
users_default: 0.into(),
notifications: ruma::events::room::power_levels::NotificationPowerLevels {
room: 50.into(),
},
})
.expect("event is valid, we just created it")
};
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomPowerLevels,
content: power_levels_content,
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 4. Events set by preset
// Figure out preset. We need it for preset specific events
2020-09-08 15:32:03 +00:00
let preset = body.preset.unwrap_or_else(|| match body.visibility {
room::Visibility::Private => create_room::RoomPreset::PrivateChat,
room::Visibility::Public => create_room::RoomPreset::PublicChat,
});
2020-07-30 16:14:47 +00:00
// 4.1 Join Rules
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomJoinRules,
content: match preset {
create_room::RoomPreset::PublicChat => serde_json::to_value(
join_rules::JoinRulesEventContent::new(join_rules::JoinRule::Public),
)
.expect("event is valid, we just created it"),
// according to spec "invite" is the default
_ => serde_json::to_value(join_rules::JoinRulesEventContent::new(
join_rules::JoinRule::Invite,
))
.expect("event is valid, we just created it"),
},
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 4.2 History Visibility
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomHistoryVisibility,
content: serde_json::to_value(history_visibility::HistoryVisibilityEventContent::new(
history_visibility::HistoryVisibility::Shared,
))
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 4.3 Guest Access
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomGuestAccess,
content: match preset {
create_room::RoomPreset::PublicChat => {
serde_json::to_value(guest_access::GuestAccessEventContent::new(
guest_access::GuestAccess::Forbidden,
))
.expect("event is valid, we just created it")
}
_ => serde_json::to_value(guest_access::GuestAccessEventContent::new(
guest_access::GuestAccess::CanJoin,
))
.expect("event is valid, we just created it"),
},
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
// 5. Events listed in initial_state
2020-09-08 15:32:03 +00:00
for event in &body.initial_state {
let pdu_builder = serde_json::from_str::<PduBuilder>(
&serde_json::to_string(&event).expect("AnyInitialStateEvent::to_string always works"),
).map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid initial state event."))?;
2020-07-30 16:14:47 +00:00
// Silently skip encryption events if they are not allowed
2020-09-08 15:32:03 +00:00
if pdu_builder.event_type == EventType::RoomEncryption && db.globals.encryption_disabled()
{
2020-07-30 16:14:47 +00:00
continue;
}
2020-09-08 15:32:03 +00:00
db.rooms
.build_and_append_pdu(pdu_builder, &db.globals, &db.account_data)?;
2020-07-30 16:14:47 +00:00
}
// 6. Events implied by name and topic
if let Some(name) = &body.name {
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomName,
content: serde_json::to_value(
name::NameEventContent::new(name.clone()).map_err(|_| {
Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid.")
})?,
)
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
}
if let Some(topic) = &body.topic {
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomTopic,
content: serde_json::to_value(topic::TopicEventContent {
topic: topic.clone(),
})
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some("".to_owned()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
}
// 7. Events implied by invite (and TODO: invite_3pid)
for user in &body.invite {
db.rooms.build_and_append_pdu(
2020-07-30 16:14:47 +00:00
PduBuilder {
room_id: room_id.clone(),
sender: sender_id.clone(),
event_type: EventType::RoomMember,
content: serde_json::to_value(member::MemberEventContent {
membership: member::MembershipState::Invite,
displayname: db.users.displayname(&user)?,
avatar_url: db.users.avatar_url(&user)?,
2020-09-08 15:32:03 +00:00
is_direct: Some(body.is_direct),
2020-07-30 16:14:47 +00:00
third_party_invite: None,
})
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some(user.to_string()),
redacts: None,
},
&db.globals,
&db.account_data,
)?;
}
// Homeserver specific stuff
if let Some(alias) = alias {
db.rooms.set_alias(&alias, Some(&room_id), &db.globals)?;
}
2020-09-08 15:32:03 +00:00
if body.visibility == room::Visibility::Public {
2020-07-30 16:14:47 +00:00
db.rooms.set_public(&room_id, true)?;
}
Ok(create_room::Response::new(room_id).into())
2020-07-30 16:14:47 +00:00
}
#[cfg_attr(
feature = "conduit_bin",
get("/_matrix/client/r0/rooms/<_>/event/<_>", data = "<body>")
)]
pub fn get_room_event_route(
db: State<'_, Database>,
2020-09-08 15:32:03 +00:00
body: Ruma<get_room_event::Request<'_>>,
2020-07-30 16:14:47 +00:00
) -> ConduitResult<get_room_event::Response> {
let sender_id = body.sender_id.as_ref().expect("user is authenticated");
if !db.rooms.is_joined(sender_id, &body.room_id)? {
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"You don't have permission to view this room.",
));
}
Ok(get_room_event::Response {
event: db
.rooms
.get_pdu(&body.event_id)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Event not found."))?
.to_room_event(),
}
.into())
}