split api/client/room
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
7450c654ae
commit
f36757027e
5 changed files with 388 additions and 352 deletions
40
src/api/client/room/aliases.rs
Normal file
40
src/api/client/room/aliases.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use axum::extract::State;
|
||||
use conduit::{Error, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::api::client::{error::ErrorKind, room::aliases};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/aliases`
|
||||
///
|
||||
/// Lists all aliases of the room.
|
||||
///
|
||||
/// - Only users joined to the room are allowed to call this, or if
|
||||
/// `history_visibility` is world readable in the room
|
||||
pub(crate) async fn get_room_aliases_route(
|
||||
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
|
||||
) -> Result<aliases::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"You don't have permission to view this room.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(aliases::v3::Response {
|
||||
aliases: services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
use std::{cmp::max, collections::BTreeMap};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{debug_info, debug_warn, err, Err};
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt};
|
||||
use conduit::{debug_info, debug_warn, error, info, pdu::PduBuilder, warn, Err, Error, Result};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
room::{self, aliases, create_room, get_room_event, upgrade_room},
|
||||
room::{self, create_room},
|
||||
},
|
||||
events::{
|
||||
room::{
|
||||
|
@ -18,36 +18,18 @@ use ruma::{
|
|||
member::{MembershipState, RoomMemberEventContent},
|
||||
name::RoomNameEventContent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
StateEventType, TimelineEventType,
|
||||
TimelineEventType,
|
||||
},
|
||||
int,
|
||||
serde::{JsonObject, Raw},
|
||||
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomVersionId,
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
use tracing::{error, info, warn};
|
||||
use service::{appservice::RegistrationInfo, Services};
|
||||
|
||||
use super::invite_helper;
|
||||
use crate::{
|
||||
service::{appservice::RegistrationInfo, pdu::PduBuilder, Services},
|
||||
Error, Result, Ruma,
|
||||
};
|
||||
|
||||
/// Recommended transferable state events list from the spec
|
||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||
StateEventType::RoomServerAcl,
|
||||
StateEventType::RoomEncryption,
|
||||
StateEventType::RoomName,
|
||||
StateEventType::RoomAvatar,
|
||||
StateEventType::RoomTopic,
|
||||
StateEventType::RoomGuestAccess,
|
||||
StateEventType::RoomHistoryVisibility,
|
||||
StateEventType::RoomJoinRules,
|
||||
StateEventType::RoomPowerLevels,
|
||||
];
|
||||
use crate::{client::invite_helper, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/v3/createRoom`
|
||||
///
|
||||
|
@ -479,333 +461,6 @@ pub(crate) async fn create_room_route(
|
|||
Ok(create_room::v3::Response::new(room_id))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}`
|
||||
///
|
||||
/// Gets a single event.
|
||||
///
|
||||
/// - You have to currently be joined to the room (TODO: Respect history
|
||||
/// visibility)
|
||||
pub(crate) async fn get_room_event_route(
|
||||
State(services): State<crate::State>, ref body: Ruma<get_room_event::v3::Request>,
|
||||
) -> Result<get_room_event::v3::Response> {
|
||||
Ok(get_room_event::v3::Response {
|
||||
event: services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_owned(&body.event_id)
|
||||
.map_err(|_| err!(Request(NotFound("Event {} not found.", &body.event_id))))
|
||||
.and_then(|event| async move {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(body.sender_user(), &event.room_id, &body.event_id)
|
||||
.await
|
||||
.then_some(event)
|
||||
.ok_or_else(|| err!(Request(Forbidden("You don't have permission to view this event."))))
|
||||
})
|
||||
.map_ok(|mut event| {
|
||||
event.add_age().ok();
|
||||
event.to_room_event()
|
||||
})
|
||||
.await?,
|
||||
})
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/aliases`
|
||||
///
|
||||
/// Lists all aliases of the room.
|
||||
///
|
||||
/// - Only users joined to the room are allowed to call this, or if
|
||||
/// `history_visibility` is world readable in the room
|
||||
pub(crate) async fn get_room_aliases_route(
|
||||
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
|
||||
) -> Result<aliases::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"You don't have permission to view this room.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(aliases::v3::Response {
|
||||
aliases: services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
||||
///
|
||||
/// Upgrades the room.
|
||||
///
|
||||
/// - Creates a replacement room
|
||||
/// - Sends a tombstone event into the current room
|
||||
/// - Sender user joins the room
|
||||
/// - Transfers some state events
|
||||
/// - Moves local aliases
|
||||
/// - Modifies old room power levels to prevent users from speaking
|
||||
pub(crate) async fn upgrade_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
|
||||
) -> Result<upgrade_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
.globals
|
||||
.supported_room_versions()
|
||||
.contains(&body.new_version)
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnsupportedRoomVersion,
|
||||
"This server does not support that room version.",
|
||||
));
|
||||
}
|
||||
|
||||
// Create a replacement room
|
||||
let replacement_room = RoomId::new(services.globals.server_name());
|
||||
|
||||
let _short_id = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(&replacement_room)
|
||||
.await;
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||
// intended to be used any further Fail if the sender does not have the required
|
||||
// permissions
|
||||
let tombstone_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
String::new(),
|
||||
&RoomTombstoneEventContent {
|
||||
body: "This room has been replaced".to_owned(),
|
||||
replacement_room: replacement_room.clone(),
|
||||
},
|
||||
),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
let state_lock = services.rooms.state.mutex.lock(&replacement_room).await;
|
||||
|
||||
// Get the old room creation event
|
||||
let mut create_event_content: CanonicalJsonObject = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.map_err(|_| err!(Database("Found room without m.room.create event.")))?;
|
||||
|
||||
// Use the m.room.tombstone event as the predecessor
|
||||
let predecessor = Some(ruma::events::room::create::PreviousRoom::new(
|
||||
body.room_id.clone(),
|
||||
(*tombstone_event_id).to_owned(),
|
||||
));
|
||||
|
||||
// Send a m.room.create event containing a predecessor field and the applicable
|
||||
// room_version
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
match body.new_version {
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
create_event_content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Error forming creation event: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||
})?,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
// "creator" key no longer exists in V11+ rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
create_event_content.insert(
|
||||
"room_version".into(),
|
||||
json!(&body.new_version)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
create_event_content.insert(
|
||||
"predecessor".into(),
|
||||
json!(predecessor)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
|
||||
// Validate creation event content
|
||||
if serde_json::from_str::<CanonicalJsonObject>(
|
||||
to_raw_value(&create_event_content)
|
||||
.expect("Error forming creation event")
|
||||
.get(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_event_content).expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: Some(String::new()),
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Join the new room
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomMember,
|
||||
content: to_raw_value(&RoomMemberEventContent {
|
||||
membership: MembershipState::Join,
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: None,
|
||||
join_authorized_via_users_server: None,
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: Some(sender_user.to_string()),
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Replicate transferable state events to the new room
|
||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||
let event_content = match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, event_type, "")
|
||||
.await
|
||||
{
|
||||
Ok(v) => v.content.clone(),
|
||||
Err(_) => continue, // Skipping missing events.
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: event_content,
|
||||
state_key: Some(String::new()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Moves any local aliases to the new room
|
||||
let mut local_aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.boxed();
|
||||
|
||||
while let Some(alias) = local_aliases.next().await {
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(alias, sender_user)
|
||||
.await?;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(alias, &replacement_room, sender_user)?;
|
||||
}
|
||||
|
||||
// Get the old room power levels
|
||||
let power_levels_event_content: RoomPowerLevelsEventContent = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
.map_err(|_| err!(Database("Found room without m.room.power_levels event.")))?;
|
||||
|
||||
// Setting events_default and invite to the greater of 50 and users_default + 1
|
||||
let new_level = max(
|
||||
int!(50),
|
||||
power_levels_event_content
|
||||
.users_default
|
||||
.checked_add(int!(1))
|
||||
.ok_or_else(|| err!(Request(BadJson("users_default power levels event content is not valid"))))?,
|
||||
);
|
||||
|
||||
// Modify the power levels in the old room to prevent sending of events and
|
||||
// inviting new users
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
String::new(),
|
||||
&RoomPowerLevelsEventContent {
|
||||
events_default: new_level,
|
||||
invite: new_level,
|
||||
..power_levels_event_content
|
||||
},
|
||||
),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// Return the replacement room id
|
||||
Ok(upgrade_room::v3::Response {
|
||||
replacement_room,
|
||||
})
|
||||
}
|
||||
|
||||
/// creates the power_levels_content for the PDU builder
|
||||
fn default_power_levels_content(
|
||||
power_level_content_override: Option<&Raw<RoomPowerLevelsEventContent>>, visibility: &room::Visibility,
|
38
src/api/client/room/event.rs
Normal file
38
src/api/client/room/event.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use axum::extract::State;
|
||||
use conduit::{err, Result};
|
||||
use futures::TryFutureExt;
|
||||
use ruma::api::client::room::get_room_event;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}`
|
||||
///
|
||||
/// Gets a single event.
|
||||
///
|
||||
/// - You have to currently be joined to the room (TODO: Respect history
|
||||
/// visibility)
|
||||
pub(crate) async fn get_room_event_route(
|
||||
State(services): State<crate::State>, ref body: Ruma<get_room_event::v3::Request>,
|
||||
) -> Result<get_room_event::v3::Response> {
|
||||
Ok(get_room_event::v3::Response {
|
||||
event: services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_owned(&body.event_id)
|
||||
.map_err(|_| err!(Request(NotFound("Event {} not found.", &body.event_id))))
|
||||
.and_then(|event| async move {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(body.sender_user(), &event.room_id, &body.event_id)
|
||||
.await
|
||||
.then_some(event)
|
||||
.ok_or_else(|| err!(Request(Forbidden("You don't have permission to view this event."))))
|
||||
})
|
||||
.map_ok(|mut event| {
|
||||
event.add_age().ok();
|
||||
event.to_room_event()
|
||||
})
|
||||
.await?,
|
||||
})
|
||||
}
|
9
src/api/client/room/mod.rs
Normal file
9
src/api/client/room/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
mod aliases;
|
||||
mod create;
|
||||
mod event;
|
||||
mod upgrade;
|
||||
|
||||
pub(crate) use self::{
|
||||
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
|
||||
upgrade::upgrade_room_route,
|
||||
};
|
294
src/api/client/room/upgrade.rs
Normal file
294
src/api/client/room/upgrade.rs
Normal file
|
@ -0,0 +1,294 @@
|
|||
use std::cmp::max;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{err, info, pdu::PduBuilder, Error, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, room::upgrade_room},
|
||||
events::{
|
||||
room::{
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
},
|
||||
StateEventType, TimelineEventType,
|
||||
},
|
||||
int, CanonicalJsonObject, RoomId, RoomVersionId,
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// Recommended transferable state events list from the spec
|
||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||
StateEventType::RoomServerAcl,
|
||||
StateEventType::RoomEncryption,
|
||||
StateEventType::RoomName,
|
||||
StateEventType::RoomAvatar,
|
||||
StateEventType::RoomTopic,
|
||||
StateEventType::RoomGuestAccess,
|
||||
StateEventType::RoomHistoryVisibility,
|
||||
StateEventType::RoomJoinRules,
|
||||
StateEventType::RoomPowerLevels,
|
||||
];
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
||||
///
|
||||
/// Upgrades the room.
|
||||
///
|
||||
/// - Creates a replacement room
|
||||
/// - Sends a tombstone event into the current room
|
||||
/// - Sender user joins the room
|
||||
/// - Transfers some state events
|
||||
/// - Moves local aliases
|
||||
/// - Modifies old room power levels to prevent users from speaking
|
||||
pub(crate) async fn upgrade_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
|
||||
) -> Result<upgrade_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
.globals
|
||||
.supported_room_versions()
|
||||
.contains(&body.new_version)
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnsupportedRoomVersion,
|
||||
"This server does not support that room version.",
|
||||
));
|
||||
}
|
||||
|
||||
// Create a replacement room
|
||||
let replacement_room = RoomId::new(services.globals.server_name());
|
||||
|
||||
let _short_id = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(&replacement_room)
|
||||
.await;
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||
// intended to be used any further Fail if the sender does not have the required
|
||||
// permissions
|
||||
let tombstone_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
String::new(),
|
||||
&RoomTombstoneEventContent {
|
||||
body: "This room has been replaced".to_owned(),
|
||||
replacement_room: replacement_room.clone(),
|
||||
},
|
||||
),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
let state_lock = services.rooms.state.mutex.lock(&replacement_room).await;
|
||||
|
||||
// Get the old room creation event
|
||||
let mut create_event_content: CanonicalJsonObject = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.map_err(|_| err!(Database("Found room without m.room.create event.")))?;
|
||||
|
||||
// Use the m.room.tombstone event as the predecessor
|
||||
let predecessor = Some(ruma::events::room::create::PreviousRoom::new(
|
||||
body.room_id.clone(),
|
||||
(*tombstone_event_id).to_owned(),
|
||||
));
|
||||
|
||||
// Send a m.room.create event containing a predecessor field and the applicable
|
||||
// room_version
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
match body.new_version {
|
||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
create_event_content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Error forming creation event: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||
})?,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
// "creator" key no longer exists in V11+ rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
create_event_content.insert(
|
||||
"room_version".into(),
|
||||
json!(&body.new_version)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
create_event_content.insert(
|
||||
"predecessor".into(),
|
||||
json!(predecessor)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
|
||||
// Validate creation event content
|
||||
if serde_json::from_str::<CanonicalJsonObject>(
|
||||
to_raw_value(&create_event_content)
|
||||
.expect("Error forming creation event")
|
||||
.get(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_event_content).expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: Some(String::new()),
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Join the new room
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomMember,
|
||||
content: to_raw_value(&RoomMemberEventContent {
|
||||
membership: MembershipState::Join,
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: None,
|
||||
join_authorized_via_users_server: None,
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: Some(sender_user.to_string()),
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Replicate transferable state events to the new room
|
||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||
let event_content = match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, event_type, "")
|
||||
.await
|
||||
{
|
||||
Ok(v) => v.content.clone(),
|
||||
Err(_) => continue, // Skipping missing events.
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: event_content,
|
||||
state_key: Some(String::new()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Moves any local aliases to the new room
|
||||
let mut local_aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.boxed();
|
||||
|
||||
while let Some(alias) = local_aliases.next().await {
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(alias, sender_user)
|
||||
.await?;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(alias, &replacement_room, sender_user)?;
|
||||
}
|
||||
|
||||
// Get the old room power levels
|
||||
let power_levels_event_content: RoomPowerLevelsEventContent = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
.map_err(|_| err!(Database("Found room without m.room.power_levels event.")))?;
|
||||
|
||||
// Setting events_default and invite to the greater of 50 and users_default + 1
|
||||
let new_level = max(
|
||||
int!(50),
|
||||
power_levels_event_content
|
||||
.users_default
|
||||
.checked_add(int!(1))
|
||||
.ok_or_else(|| err!(Request(BadJson("users_default power levels event content is not valid"))))?,
|
||||
);
|
||||
|
||||
// Modify the power levels in the old room to prevent sending of events and
|
||||
// inviting new users
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(
|
||||
String::new(),
|
||||
&RoomPowerLevelsEventContent {
|
||||
events_default: new_level,
|
||||
invite: new_level,
|
||||
..power_levels_event_content
|
||||
},
|
||||
),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// Return the replacement room id
|
||||
Ok(upgrade_room::v3::Response {
|
||||
replacement_room,
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue