Merge remote-tracking branch 'origin/next' into complement-improvements

This commit is contained in:
Jonathan de Jong 2023-01-26 18:19:39 +01:00
commit b158896396
84 changed files with 2060 additions and 1316 deletions

View file

@ -4,8 +4,8 @@ use ruma::{
api::client::{
account::{
change_password, deactivate, get_3pids, get_username_availability, register,
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, whoami,
ThirdPartyIdRemovalStatus,
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn,
whoami, ThirdPartyIdRemovalStatus,
},
error::ErrorKind,
uiaa::{AuthFlow, AuthType, UiaaInfo},
@ -30,7 +30,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
///
/// Note: This will not reserve the username, so the username might become invalid when trying to register
pub async fn get_register_available_route(
body: Ruma<get_username_availability::v3::IncomingRequest>,
body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
// Validate user id
let user_id = UserId::parse_with_server_name(
@ -73,9 +73,7 @@ pub async fn get_register_available_route(
/// - If type is not guest and no username is given: Always fails after UIAA check
/// - Creates a new account and populates it with default account data
/// - If `inhibit_login` is false: Creates a device and returns device id and access_token
pub async fn register_route(
body: Ruma<register::v3::IncomingRequest>,
) -> Result<register::v3::Response> {
pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
if !services().globals.allow_registration() && !body.from_appservice {
return Err(Error::BadRequest(
ErrorKind::Forbidden,
@ -227,8 +225,7 @@ pub async fn register_route(
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"New user {} registered on this server.",
user_id
"New user {user_id} registered on this server."
)));
// If this is the first real user, grant them admin privileges
@ -266,7 +263,7 @@ pub async fn register_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub async fn change_password_route(
body: Ruma<change_password::v3::IncomingRequest>,
body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@ -320,8 +317,7 @@ pub async fn change_password_route(
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {} changed their password.",
sender_user
"User {sender_user} changed their password."
)));
Ok(change_password::v3::Response {})
@ -354,7 +350,7 @@ pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3:
/// - Triggers device list updates
/// - Removes ability to log in again
pub async fn deactivate_route(
body: Ruma<deactivate::v3::IncomingRequest>,
body: Ruma<deactivate::v3::Request>,
) -> Result<deactivate::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@ -398,8 +394,7 @@ pub async fn deactivate_route(
services()
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {} deactivated their account.",
sender_user
"User {sender_user} deactivated their account."
)));
Ok(deactivate::v3::Response {
@ -426,7 +421,7 @@ pub async fn third_party_route(
///
/// - 403 signals that The homeserver does not allow the third party identifier as a contact option.
pub async fn request_3pid_management_token_via_email_route(
_body: Ruma<request_3pid_management_token_via_email::v3::IncomingRequest>,
_body: Ruma<request_3pid_management_token_via_email::v3::Request>,
) -> Result<request_3pid_management_token_via_email::v3::Response> {
Err(Error::BadRequest(
ErrorKind::ThreepidDenied,
@ -440,7 +435,7 @@ pub async fn request_3pid_management_token_via_email_route(
///
/// - 403 signals that The homeserver does not allow the third party identifier as a contact option.
pub async fn request_3pid_management_token_via_msisdn_route(
_body: Ruma<request_3pid_management_token_via_msisdn::v3::IncomingRequest>,
_body: Ruma<request_3pid_management_token_via_msisdn::v3::Request>,
) -> Result<request_3pid_management_token_via_msisdn::v3::Response> {
Err(Error::BadRequest(
ErrorKind::ThreepidDenied,

View file

@ -9,14 +9,14 @@ use ruma::{
},
federation,
},
RoomAliasId,
OwnedRoomAliasId,
};
/// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
///
/// Creates a new room alias on this server.
pub async fn create_alias_route(
body: Ruma<create_alias::v3::IncomingRequest>,
body: Ruma<create_alias::v3::Request>,
) -> Result<create_alias::v3::Response> {
if body.room_alias.server_name() != services().globals.server_name() {
return Err(Error::BadRequest(
@ -49,7 +49,7 @@ pub async fn create_alias_route(
/// - TODO: additional access control checks
/// - TODO: Update canonical alias event
pub async fn delete_alias_route(
body: Ruma<delete_alias::v3::IncomingRequest>,
body: Ruma<delete_alias::v3::Request>,
) -> Result<delete_alias::v3::Response> {
if body.room_alias.server_name() != services().globals.server_name() {
return Err(Error::BadRequest(
@ -71,18 +71,22 @@ pub async fn delete_alias_route(
///
/// - TODO: Suggest more servers to join via
pub async fn get_alias_route(
body: Ruma<get_alias::v3::IncomingRequest>,
body: Ruma<get_alias::v3::Request>,
) -> Result<get_alias::v3::Response> {
get_alias_helper(&body.room_alias).await
get_alias_helper(body.body.room_alias).await
}
pub(crate) async fn get_alias_helper(room_alias: &RoomAliasId) -> Result<get_alias::v3::Response> {
pub(crate) async fn get_alias_helper(
room_alias: OwnedRoomAliasId,
) -> Result<get_alias::v3::Response> {
if room_alias.server_name() != services().globals.server_name() {
let response = services()
.sending
.send_federation_request(
room_alias.server_name(),
federation::query::get_room_information::v1::Request { room_alias },
federation::query::get_room_information::v1::Request {
room_alias: room_alias.to_owned(),
},
)
.await?;
@ -93,7 +97,7 @@ pub(crate) async fn get_alias_helper(room_alias: &RoomAliasId) -> Result<get_ali
}
let mut room_id = None;
match services().rooms.alias.resolve_local_alias(room_alias)? {
match services().rooms.alias.resolve_local_alias(&room_alias)? {
Some(r) => room_id = Some(r),
None => {
for (_id, registration) in services().appservice.all()? {
@ -115,7 +119,9 @@ pub(crate) async fn get_alias_helper(room_alias: &RoomAliasId) -> Result<get_ali
.sending
.send_appservice_request(
registration,
appservice::query::query_room_alias::v1::Request { room_alias },
appservice::query::query_room_alias::v1::Request {
room_alias: room_alias.clone(),
},
)
.await
.is_ok()
@ -124,7 +130,7 @@ pub(crate) async fn get_alias_helper(room_alias: &RoomAliasId) -> Result<get_ali
services()
.rooms
.alias
.resolve_local_alias(room_alias)?
.resolve_local_alias(&room_alias)?
.ok_or_else(|| {
Error::bad_config("Appservice lied to us. Room does not exist.")
})?,

View file

@ -28,7 +28,7 @@ pub async fn create_backup_version_route(
///
/// Update information about an existing backup. Only `auth_data` can be modified.
pub async fn update_backup_version_route(
body: Ruma<update_backup_version::v3::IncomingRequest>,
body: Ruma<update_backup_version::v3::Request>,
) -> Result<update_backup_version::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
services()
@ -66,7 +66,7 @@ pub async fn get_latest_backup_info_route(
///
/// Get information about an existing backup.
pub async fn get_backup_info_route(
body: Ruma<get_backup_info::v3::IncomingRequest>,
body: Ruma<get_backup_info::v3::Request>,
) -> Result<get_backup_info::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let algorithm = services()
@ -96,7 +96,7 @@ pub async fn get_backup_info_route(
///
/// - Deletes both information about the backup, as well as all key data related to the backup
pub async fn delete_backup_version_route(
body: Ruma<delete_backup_version::v3::IncomingRequest>,
body: Ruma<delete_backup_version::v3::Request>,
) -> Result<delete_backup_version::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -115,7 +115,7 @@ pub async fn delete_backup_version_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub async fn add_backup_keys_route(
body: Ruma<add_backup_keys::v3::IncomingRequest>,
body: Ruma<add_backup_keys::v3::Request>,
) -> Result<add_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -162,7 +162,7 @@ pub async fn add_backup_keys_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub async fn add_backup_keys_for_room_route(
body: Ruma<add_backup_keys_for_room::v3::IncomingRequest>,
body: Ruma<add_backup_keys_for_room::v3::Request>,
) -> Result<add_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -207,7 +207,7 @@ pub async fn add_backup_keys_for_room_route(
/// - Adds the keys to the backup
/// - Returns the new number of keys in this backup and the etag
pub async fn add_backup_keys_for_session_route(
body: Ruma<add_backup_keys_for_session::v3::IncomingRequest>,
body: Ruma<add_backup_keys_for_session::v3::Request>,
) -> Result<add_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -246,7 +246,7 @@ pub async fn add_backup_keys_for_session_route(
///
/// Retrieves all keys from the backup.
pub async fn get_backup_keys_route(
body: Ruma<get_backup_keys::v3::IncomingRequest>,
body: Ruma<get_backup_keys::v3::Request>,
) -> Result<get_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -259,7 +259,7 @@ pub async fn get_backup_keys_route(
///
/// Retrieves all keys from the backup for a given room.
pub async fn get_backup_keys_for_room_route(
body: Ruma<get_backup_keys_for_room::v3::IncomingRequest>,
body: Ruma<get_backup_keys_for_room::v3::Request>,
) -> Result<get_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -274,7 +274,7 @@ pub async fn get_backup_keys_for_room_route(
///
/// Retrieves a key from the backup.
pub async fn get_backup_keys_for_session_route(
body: Ruma<get_backup_keys_for_session::v3::IncomingRequest>,
body: Ruma<get_backup_keys_for_session::v3::Request>,
) -> Result<get_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -293,7 +293,7 @@ pub async fn get_backup_keys_for_session_route(
///
/// Delete the keys from the backup.
pub async fn delete_backup_keys_route(
body: Ruma<delete_backup_keys::v3::IncomingRequest>,
body: Ruma<delete_backup_keys::v3::Request>,
) -> Result<delete_backup_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -316,7 +316,7 @@ pub async fn delete_backup_keys_route(
///
/// Delete the keys from the backup for a given room.
pub async fn delete_backup_keys_for_room_route(
body: Ruma<delete_backup_keys_for_room::v3::IncomingRequest>,
body: Ruma<delete_backup_keys_for_room::v3::Request>,
) -> Result<delete_backup_keys_for_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -339,7 +339,7 @@ pub async fn delete_backup_keys_for_room_route(
///
/// Delete a key from the backup.
pub async fn delete_backup_keys_for_session_route(
body: Ruma<delete_backup_keys_for_session::v3::IncomingRequest>,
body: Ruma<delete_backup_keys_for_session::v3::Request>,
) -> Result<delete_backup_keys_for_session::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -8,7 +8,7 @@ use std::collections::BTreeMap;
///
/// Get information on the supported feature set and other relevent capabilities of this server.
pub async fn get_capabilities_route(
_body: Ruma<get_capabilities::v3::IncomingRequest>,
_body: Ruma<get_capabilities::v3::Request>,
) -> Result<get_capabilities::v3::Response> {
let mut available = BTreeMap::new();
for room_version in &services().globals.unstable_room_versions {

View file

@ -17,7 +17,7 @@ use serde_json::{json, value::RawValue as RawJsonValue};
///
/// Sets some account data for the sender user.
pub async fn set_global_account_data_route(
body: Ruma<set_global_account_data::v3::IncomingRequest>,
body: Ruma<set_global_account_data::v3::Request>,
) -> Result<set_global_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -43,7 +43,7 @@ pub async fn set_global_account_data_route(
///
/// Sets some room account data for the sender user.
pub async fn set_room_account_data_route(
body: Ruma<set_room_account_data::v3::IncomingRequest>,
body: Ruma<set_room_account_data::v3::Request>,
) -> Result<set_room_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -69,7 +69,7 @@ pub async fn set_room_account_data_route(
///
/// Gets some account data for the sender user.
pub async fn get_global_account_data_route(
body: Ruma<get_global_account_data::v3::IncomingRequest>,
body: Ruma<get_global_account_data::v3::Request>,
) -> Result<get_global_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -89,7 +89,7 @@ pub async fn get_global_account_data_route(
///
/// Gets some room account data for the sender user.
pub async fn get_room_account_data_route(
body: Ruma<get_room_account_data::v3::IncomingRequest>,
body: Ruma<get_room_account_data::v3::Request>,
) -> Result<get_room_account_data::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -13,7 +13,7 @@ use tracing::error;
/// - Only works if the user is joined (TODO: always allow, but only show events if the user was
/// joined, depending on history_visibility)
pub async fn get_context_route(
body: Ruma<get_context::v3::IncomingRequest>,
body: Ruma<get_context::v3::Request>,
) -> Result<get_context::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");

View file

@ -28,7 +28,7 @@ pub async fn get_devices_route(
///
/// Get metadata on a single device of the sender user.
pub async fn get_device_route(
body: Ruma<get_device::v3::IncomingRequest>,
body: Ruma<get_device::v3::Request>,
) -> Result<get_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -44,7 +44,7 @@ pub async fn get_device_route(
///
/// Updates the metadata on a given device of the sender user.
pub async fn update_device_route(
body: Ruma<update_device::v3::IncomingRequest>,
body: Ruma<update_device::v3::Request>,
) -> Result<update_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -72,7 +72,7 @@ pub async fn update_device_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub async fn delete_device_route(
body: Ruma<delete_device::v3::IncomingRequest>,
body: Ruma<delete_device::v3::Request>,
) -> Result<delete_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@ -126,7 +126,7 @@ pub async fn delete_device_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub async fn delete_devices_route(
body: Ruma<delete_devices::v3::IncomingRequest>,
body: Ruma<delete_devices::v3::Request>,
) -> Result<delete_devices::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");

View file

@ -11,10 +11,7 @@ use ruma::{
},
federation,
},
directory::{
Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomJoinRule, PublicRoomsChunk,
RoomNetwork,
},
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork},
events::{
room::{
avatar::RoomAvatarEventContent,
@ -38,7 +35,7 @@ use tracing::{error, info, warn};
///
/// - Rooms are ordered by the number of joined members
pub async fn get_public_rooms_filtered_route(
body: Ruma<get_public_rooms_filtered::v3::IncomingRequest>,
body: Ruma<get_public_rooms_filtered::v3::Request>,
) -> Result<get_public_rooms_filtered::v3::Response> {
get_public_rooms_filtered_helper(
body.server.as_deref(),
@ -56,14 +53,14 @@ pub async fn get_public_rooms_filtered_route(
///
/// - Rooms are ordered by the number of joined members
pub async fn get_public_rooms_route(
body: Ruma<get_public_rooms::v3::IncomingRequest>,
body: Ruma<get_public_rooms::v3::Request>,
) -> Result<get_public_rooms::v3::Response> {
let response = get_public_rooms_filtered_helper(
body.server.as_deref(),
body.limit,
body.since.as_deref(),
&IncomingFilter::default(),
&IncomingRoomNetwork::Matrix,
&Filter::default(),
&RoomNetwork::Matrix,
)
.await?;
@ -81,16 +78,13 @@ pub async fn get_public_rooms_route(
///
/// - TODO: Access control checks
pub async fn set_room_visibility_route(
body: Ruma<set_room_visibility::v3::IncomingRequest>,
body: Ruma<set_room_visibility::v3::Request>,
) -> Result<set_room_visibility::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services().rooms.metadata.exists(&body.room_id)? {
// Return 404 if the room doesn't exist
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Room not found",
));
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
}
match &body.visibility {
@ -114,15 +108,11 @@ pub async fn set_room_visibility_route(
///
/// Gets the visibility of a given room in the room directory.
pub async fn get_room_visibility_route(
body: Ruma<get_room_visibility::v3::IncomingRequest>,
body: Ruma<get_room_visibility::v3::Request>,
) -> Result<get_room_visibility::v3::Response> {
if !services().rooms.metadata.exists(&body.room_id)? {
// Return 404 if the room doesn't exist
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Room not found",
));
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
}
Ok(get_room_visibility::v3::Response {
@ -138,8 +128,8 @@ pub(crate) async fn get_public_rooms_filtered_helper(
server: Option<&ServerName>,
limit: Option<UInt>,
since: Option<&str>,
filter: &IncomingFilter,
_network: &IncomingRoomNetwork,
filter: &Filter,
_network: &RoomNetwork,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(other_server) =
server.filter(|server| *server != services().globals.server_name().as_str())
@ -150,9 +140,9 @@ pub(crate) async fn get_public_rooms_filtered_helper(
other_server,
federation::directory::get_public_rooms_filtered::v1::Request {
limit,
since,
since: since.map(ToOwned::to_owned),
filter: Filter {
generic_search_term: filter.generic_search_term.as_deref(),
generic_search_term: filter.generic_search_term.clone(),
room_types: filter.room_types.clone(),
},
room_network: RoomNetwork::Matrix,
@ -371,7 +361,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
let prev_batch = if num_since == 0 {
None
} else {
Some(format!("p{}", num_since))
Some(format!("p{num_since}"))
};
let next_batch = if chunk.len() < limit as usize {

View file

@ -10,7 +10,7 @@ use ruma::api::client::{
///
/// - A user can only access their own filters
pub async fn get_filter_route(
body: Ruma<get_filter::v3::IncomingRequest>,
body: Ruma<get_filter::v3::Request>,
) -> Result<get_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let filter = match services().users.get_filter(sender_user, &body.filter_id)? {
@ -25,7 +25,7 @@ pub async fn get_filter_route(
///
/// Creates a new filter to be used by other endpoints.
pub async fn create_filter_route(
body: Ruma<create_filter::v3::IncomingRequest>,
body: Ruma<create_filter::v3::Request>,
) -> Result<create_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
Ok(create_filter::v3::Response::new(

View file

@ -65,9 +65,7 @@ pub async fn upload_keys_route(
/// - Always fetches users from other servers over federation
/// - Gets master keys, self-signing keys, user signing keys and device keys.
/// - The master and self-signing keys contain signatures that the user is allowed to see
pub async fn get_keys_route(
body: Ruma<get_keys::v3::IncomingRequest>,
) -> Result<get_keys::v3::Response> {
pub async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let response =
@ -93,7 +91,7 @@ pub async fn claim_keys_route(
///
/// - Requires UIAA to verify password
pub async fn upload_signing_keys_route(
body: Ruma<upload_signing_keys::v3::IncomingRequest>,
body: Ruma<upload_signing_keys::v3::Request>,
) -> Result<upload_signing_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@ -214,7 +212,7 @@ pub async fn upload_signatures_route(
///
/// - TODO: left users
pub async fn get_key_changes_route(
body: Ruma<get_key_changes::v3::IncomingRequest>,
body: Ruma<get_key_changes::v3::Request>,
) -> Result<get_key_changes::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -27,7 +27,7 @@ pub async fn get_media_config_route(
/// - Some metadata will be saved in the database
/// - Media will be saved in the media/ directory
pub async fn create_content_route(
body: Ruma<create_content::v3::IncomingRequest>,
body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let mxc = format!(
"mxc://{}/{}",
@ -57,7 +57,7 @@ pub async fn create_content_route(
pub async fn get_remote_content(
mxc: &str,
server_name: &ruma::ServerName,
media_id: &str,
media_id: String,
) -> Result<get_content::v3::Response, Error> {
let content_response = services()
.sending
@ -65,7 +65,7 @@ pub async fn get_remote_content(
server_name,
get_content::v3::Request {
allow_remote: false,
server_name,
server_name: server_name.to_owned(),
media_id,
},
)
@ -74,7 +74,7 @@ pub async fn get_remote_content(
services()
.media
.create(
mxc.to_string(),
mxc.to_owned(),
content_response.content_disposition.as_deref(),
content_response.content_type.as_deref(),
&content_response.file,
@ -90,7 +90,7 @@ pub async fn get_remote_content(
///
/// - Only allows federation if `allow_remote` is true
pub async fn get_content_route(
body: Ruma<get_content::v3::IncomingRequest>,
body: Ruma<get_content::v3::Request>,
) -> Result<get_content::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
@ -108,7 +108,7 @@ pub async fn get_content_route(
})
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
let remote_content_response =
get_remote_content(&mxc, &body.server_name, &body.media_id).await?;
get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?;
Ok(remote_content_response)
} else {
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
@ -121,7 +121,7 @@ pub async fn get_content_route(
///
/// - Only allows federation if `allow_remote` is true
pub async fn get_content_as_filename_route(
body: Ruma<get_content_as_filename::v3::IncomingRequest>,
body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<get_content_as_filename::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
@ -139,7 +139,7 @@ pub async fn get_content_as_filename_route(
})
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
let remote_content_response =
get_remote_content(&mxc, &body.server_name, &body.media_id).await?;
get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?;
Ok(get_content_as_filename::v3::Response {
content_disposition: Some(format!("inline: filename={}", body.filename)),
@ -158,7 +158,7 @@ pub async fn get_content_as_filename_route(
///
/// - Only allows federation if `allow_remote` is true
pub async fn get_content_thumbnail_route(
body: Ruma<get_content_thumbnail::v3::IncomingRequest>,
body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<get_content_thumbnail::v3::Response> {
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
@ -192,8 +192,8 @@ pub async fn get_content_thumbnail_route(
height: body.height,
width: body.width,
method: body.method.clone(),
server_name: &body.server_name,
media_id: &body.media_id,
server_name: body.server_name.clone(),
media_id: body.media_id.clone(),
},
)
.await?;

View file

@ -5,19 +5,23 @@ use ruma::{
membership::{
ban_user, forget_room, get_member_events, invite_user, join_room_by_id,
join_room_by_id_or_alias, joined_members, joined_rooms, kick_user, leave_room,
unban_user, IncomingThirdPartySigned,
unban_user, ThirdPartySigned,
},
},
federation::{self, membership::create_invite},
},
canonical_json::to_canonical_value,
events::{
room::member::{MembershipState, RoomMemberEventContent},
room::{
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, RoomMemberEventContent},
power_levels::RoomPowerLevelsEventContent,
},
RoomEventType, StateEventType,
},
serde::Base64,
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedUserId, RoomId, RoomVersionId, UserId,
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId,
};
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use std::{
@ -41,7 +45,7 @@ use super::get_alias_helper;
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_route(
body: Ruma<join_room_by_id::v3::IncomingRequest>,
body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -65,6 +69,7 @@ pub async fn join_room_by_id_route(
join_room_by_id_helper(
body.sender_user.as_deref(),
&body.room_id,
body.reason.clone(),
&servers,
body.third_party_signed.as_ref(),
)
@ -78,7 +83,7 @@ pub async fn join_room_by_id_route(
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_or_alias_route(
body: Ruma<join_room_by_id_or_alias::v3::IncomingRequest>,
body: Ruma<join_room_by_id_or_alias::v3::Request>,
) -> Result<join_room_by_id_or_alias::v3::Response> {
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
let body = body.body;
@ -104,7 +109,7 @@ pub async fn join_room_by_id_or_alias_route(
(servers, room_id)
}
Err(room_alias) => {
let response = get_alias_helper(&room_alias).await?;
let response = get_alias_helper(room_alias).await?;
(response.servers.into_iter().collect(), response.room_id)
}
@ -113,6 +118,7 @@ pub async fn join_room_by_id_or_alias_route(
let join_room_response = join_room_by_id_helper(
Some(sender_user),
&room_id,
body.reason.clone(),
&servers,
body.third_party_signed.as_ref(),
)
@ -129,11 +135,11 @@ pub async fn join_room_by_id_or_alias_route(
///
/// - This should always work if the user is currently joined.
pub async fn leave_room_route(
body: Ruma<leave_room::v3::IncomingRequest>,
body: Ruma<leave_room::v3::Request>,
) -> Result<leave_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
leave_room(sender_user, &body.room_id).await?;
leave_room(sender_user, &body.room_id, body.reason.clone()).await?;
Ok(leave_room::v3::Response::new())
}
@ -142,12 +148,19 @@ pub async fn leave_room_route(
///
/// Tries to send an invite event into the room.
pub async fn invite_user_route(
body: Ruma<invite_user::v3::IncomingRequest>,
body: Ruma<invite_user::v3::Request>,
) -> Result<invite_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if let invite_user::v3::IncomingInvitationRecipient::UserId { user_id } = &body.recipient {
invite_helper(sender_user, user_id, &body.room_id, false).await?;
if let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient {
invite_helper(
sender_user,
user_id,
&body.room_id,
body.reason.clone(),
false,
)
.await?;
Ok(invite_user::v3::Response {})
} else {
Err(Error::BadRequest(ErrorKind::NotFound, "User not found."))
@ -158,7 +171,7 @@ pub async fn invite_user_route(
///
/// Tries to send a kick event into the room.
pub async fn kick_user_route(
body: Ruma<kick_user::v3::IncomingRequest>,
body: Ruma<kick_user::v3::Request>,
) -> Result<kick_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -181,7 +194,7 @@ pub async fn kick_user_route(
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
event.membership = MembershipState::Leave;
// TODO: reason
event.reason = body.reason.clone();
let mutex_state = Arc::clone(
services()
@ -215,13 +228,9 @@ pub async fn kick_user_route(
/// # `POST /_matrix/client/r0/rooms/{roomId}/ban`
///
/// Tries to send a ban event into the room.
pub async fn ban_user_route(
body: Ruma<ban_user::v3::IncomingRequest>,
) -> Result<ban_user::v3::Response> {
pub async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
// TODO: reason
let event = services()
.rooms
.state_accessor
@ -238,7 +247,7 @@ pub async fn ban_user_route(
is_direct: None,
third_party_invite: None,
blurhash: services().users.blurhash(&body.user_id)?,
reason: None,
reason: body.reason.clone(),
join_authorized_via_users_server: None,
}),
|event| {
@ -284,7 +293,7 @@ pub async fn ban_user_route(
///
/// Tries to send an unban event into the room.
pub async fn unban_user_route(
body: Ruma<unban_user::v3::IncomingRequest>,
body: Ruma<unban_user::v3::Request>,
) -> Result<unban_user::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -307,6 +316,7 @@ pub async fn unban_user_route(
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
event.membership = MembershipState::Leave;
event.reason = body.reason.clone();
let mutex_state = Arc::clone(
services()
@ -346,7 +356,7 @@ pub async fn unban_user_route(
/// Note: Other devices of the user have no way of knowing the room was forgotten, so this has to
/// be called from every device
pub async fn forget_room_route(
body: Ruma<forget_room::v3::IncomingRequest>,
body: Ruma<forget_room::v3::Request>,
) -> Result<forget_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -382,7 +392,7 @@ pub async fn joined_rooms_route(
///
/// - Only works if the user is currently joined
pub async fn get_member_events_route(
body: Ruma<get_member_events::v3::IncomingRequest>,
body: Ruma<get_member_events::v3::Request>,
) -> Result<get_member_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -418,7 +428,7 @@ pub async fn get_member_events_route(
/// - The sender user must be in the room
/// - TODO: An appservice just needs a puppet joined
pub async fn joined_members_route(
body: Ruma<joined_members::v3::IncomingRequest>,
body: Ruma<joined_members::v3::Request>,
) -> Result<joined_members::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -458,8 +468,9 @@ pub async fn joined_members_route(
async fn join_room_by_id_helper(
sender_user: Option<&UserId>,
room_id: &RoomId,
reason: Option<String>,
servers: &[OwnedServerName],
_third_party_signed: Option<&IncomingThirdPartySigned>,
_third_party_signed: Option<&ThirdPartySigned>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = sender_user.expect("user is authenticated");
@ -480,33 +491,10 @@ async fn join_room_by_id_helper(
.state_cache
.server_in_room(services().globals.server_name(), room_id)?
{
let mut make_join_response_and_server = Err(Error::BadServerResponse(
"No server available to assist in joining.",
));
let (make_join_response, remote_server) =
make_join_request(sender_user, room_id, servers).await?;
for remote_server in servers {
let make_join_response = services()
.sending
.send_federation_request(
remote_server,
federation::membership::prepare_join_event::v1::Request {
room_id,
user_id: sender_user,
ver: &services().globals.supported_room_versions(),
},
)
.await;
make_join_response_and_server = make_join_response.map(|r| (r, remote_server));
if make_join_response_and_server.is_ok() {
break;
}
}
let (make_join_response, remote_server) = make_join_response_and_server?;
let room_version = match make_join_response.room_version {
let room_version_id = match make_join_response.room_version {
Some(room_version)
if services()
.globals
@ -554,7 +542,7 @@ async fn join_room_by_id_helper(
is_direct: None,
third_party_invite: None,
blurhash: services().users.blurhash(sender_user)?,
reason: None,
reason,
join_authorized_via_users_server,
})
.expect("event is valid, we just created it"),
@ -568,14 +556,14 @@ async fn join_room_by_id_helper(
services().globals.server_name().as_str(),
services().globals.keypair(),
&mut join_event_stub,
&room_version,
&room_version_id,
)
.expect("event is valid, we just created it");
// Generate event id
let event_id = format!(
"${}",
ruma::signatures::reference_hash(&join_event_stub, &room_version)
ruma::signatures::reference_hash(&join_event_stub, &room_version_id)
.expect("ruma can calculate reference hashes")
);
let event_id = <&EventId>::try_from(event_id.as_str())
@ -588,23 +576,67 @@ async fn join_room_by_id_helper(
);
// It has enough fields to be called a proper event now
let join_event = join_event_stub;
let mut join_event = join_event_stub;
let send_join_response = services()
.sending
.send_federation_request(
remote_server,
&remote_server,
federation::membership::create_join_event::v2::Request {
room_id,
event_id,
pdu: &PduEvent::convert_to_outgoing_federation_event(join_event.clone()),
room_id: room_id.to_owned(),
event_id: event_id.to_owned(),
pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()),
},
)
.await?;
if let Some(signed_raw) = &send_join_response.room_state.event {
let (signed_event_id, signed_value) =
match gen_event_id_canonical_json(signed_raw, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Could not convert event to canonical json.",
));
}
};
if signed_event_id != event_id {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent event with wrong event id",
));
}
if let Ok(signature) = signed_value["signatures"]
.as_object()
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent invalid signatures type",
))
.and_then(|e| {
e.get(remote_server.as_str()).ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Server did not send its signature",
))
})
{
join_event
.get_mut("signatures")
.expect("we created a valid pdu")
.as_object_mut()
.expect("we created a valid pdu")
.insert(remote_server.to_string(), signature.clone());
} else {
warn!("Server {} sent invalid sendjoin event", remote_server);
}
}
services().rooms.short.get_or_create_shortroomid(room_id)?;
let parsed_pdu = PduEvent::from_id_val(event_id, join_event.clone())
let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone())
.map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?;
let mut state = HashMap::new();
@ -613,14 +645,14 @@ async fn join_room_by_id_helper(
services()
.rooms
.event_handler
.fetch_join_signing_keys(&send_join_response, &room_version, &pub_key_map)
.fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map)
.await?;
for result in send_join_response
.room_state
.state
.iter()
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
{
let (event_id, value) = match result {
Ok(t) => t,
@ -645,31 +677,11 @@ async fn join_room_by_id_helper(
}
}
let incoming_shortstatekey = services().rooms.short.get_or_create_shortstatekey(
&parsed_pdu.kind.to_string().into(),
parsed_pdu
.state_key
.as_ref()
.expect("Pdu is a membership state event"),
)?;
state.insert(incoming_shortstatekey, parsed_pdu.event_id.clone());
let create_shortstatekey = services()
.rooms
.short
.get_shortstatekey(&StateEventType::RoomCreate, "")?
.expect("Room exists");
if state.get(&create_shortstatekey).is_none() {
return Err(Error::BadServerResponse("State contained no create event."));
}
for result in send_join_response
.room_state
.auth_chain
.iter()
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
{
let (event_id, value) = match result {
Ok(t) => t,
@ -682,6 +694,34 @@ async fn join_room_by_id_helper(
.add_pdu_outlier(&event_id, &value)?;
}
if !state_res::event_auth::auth_check(
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
&parsed_join_pdu,
None::<PduEvent>, // TODO: third party invite
|k, s| {
services()
.rooms
.timeline
.get_pdu(
state.get(
&services()
.rooms
.short
.get_or_create_shortstatekey(&k.to_string().into(), s)
.ok()?,
)?,
)
.ok()?
},
)
.map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed"))?
{
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Auth check failed",
));
}
let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state(
room_id,
state
@ -705,12 +745,12 @@ async fn join_room_by_id_helper(
// We append to state before appending the pdu, so we don't have a moment in time with the
// pdu without it's state. This is okay because append_pdu can't fail.
let statehash_after_join = services().rooms.state.append_to_state(&parsed_pdu)?;
let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?;
services().rooms.timeline.append_pdu(
&parsed_pdu,
&parsed_join_pdu,
join_event,
vec![(*parsed_pdu.event_id).to_owned()],
vec![(*parsed_join_pdu.event_id).to_owned()],
&state_lock,
)?;
@ -721,6 +761,95 @@ async fn join_room_by_id_helper(
.state
.set_room_state(room_id, statehash_after_join, &state_lock)?;
} else {
let join_rules_event = services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomJoinRules,
"",
)?;
let power_levels_event = services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomPowerLevels,
"",
)?;
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
.as_ref()
.map(|join_rules_event| {
serde_json::from_str(join_rules_event.content.get()).map_err(|e| {
warn!("Invalid join rules event: {}", e);
Error::bad_database("Invalid join rules event in db.")
})
})
.transpose()?;
let power_levels_event_content: Option<RoomPowerLevelsEventContent> = power_levels_event
.as_ref()
.map(|power_levels_event| {
serde_json::from_str(power_levels_event.content.get()).map_err(|e| {
warn!("Invalid power levels event: {}", e);
Error::bad_database("Invalid power levels event in db.")
})
})
.transpose()?;
let restriction_rooms = match join_rules_event_content {
Some(RoomJoinRulesEventContent {
join_rule: JoinRule::Restricted(restricted),
})
| Some(RoomJoinRulesEventContent {
join_rule: JoinRule::KnockRestricted(restricted),
}) => restricted
.allow
.into_iter()
.filter_map(|a| match a {
AllowRule::RoomMembership(r) => Some(r.room_id),
_ => None,
})
.collect(),
_ => Vec::new(),
};
let authorized_user = restriction_rooms
.iter()
.find_map(|restriction_room_id| {
if !services()
.rooms
.state_cache
.is_joined(sender_user, restriction_room_id)
.ok()?
{
return None;
}
let authorized_user = power_levels_event_content
.as_ref()
.and_then(|c| {
c.users
.iter()
.filter(|(uid, i)| {
uid.server_name() == services().globals.server_name()
&& **i > ruma::int!(0)
&& services()
.rooms
.state_cache
.is_joined(uid, restriction_room_id)
.unwrap_or(false)
})
.max_by_key(|(_, i)| *i)
.map(|(u, _)| u.to_owned())
})
.or_else(|| {
// TODO: Check here if user is actually allowed to invite. Currently the auth
// check will just fail in this case.
services()
.rooms
.state_cache
.room_members(restriction_room_id)
.filter_map(|r| r.ok())
.find(|uid| uid.server_name() == services().globals.server_name())
});
Some(authorized_user)
})
.flatten();
let event = RoomMemberEventContent {
membership: MembershipState::Join,
displayname: services().users.displayname(sender_user)?,
@ -728,11 +857,12 @@ async fn join_room_by_id_helper(
is_direct: None,
third_party_invite: None,
blurhash: services().users.blurhash(sender_user)?,
reason: None,
join_authorized_via_users_server: None,
reason: reason.clone(),
join_authorized_via_users_server: authorized_user,
};
services().rooms.timeline.build_and_append_pdu(
// Try normal join first
let error = match services().rooms.timeline.build_and_append_pdu(
PduBuilder {
event_type: RoomEventType::RoomMember,
content: to_raw_value(&event).expect("event is valid, we just created it"),
@ -743,14 +873,193 @@ async fn join_room_by_id_helper(
sender_user,
room_id,
&state_lock,
)?;
) {
Ok(_event_id) => return Ok(join_room_by_id::v3::Response::new(room_id.to_owned())),
Err(e) => e,
};
if !restriction_rooms.is_empty() {
// We couldn't do the join locally, maybe federation can help to satisfy the restricted
// join requirements
let (make_join_response, remote_server) =
make_join_request(sender_user, room_id, servers).await?;
let room_version_id = match make_join_response.room_version {
Some(room_version_id)
if services()
.globals
.supported_room_versions()
.contains(&room_version_id) =>
{
room_version_id
}
_ => return Err(Error::BadServerResponse("Room version is not supported")),
};
let mut join_event_stub: CanonicalJsonObject =
serde_json::from_str(make_join_response.event.get()).map_err(|_| {
Error::BadServerResponse("Invalid make_join event json received from server.")
})?;
let join_authorized_via_users_server = join_event_stub
.get("content")
.map(|s| {
s.as_object()?
.get("join_authorised_via_users_server")?
.as_str()
})
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok());
// TODO: Is origin needed?
join_event_stub.insert(
"origin".to_owned(),
CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()),
);
join_event_stub.insert(
"origin_server_ts".to_owned(),
CanonicalJsonValue::Integer(
utils::millis_since_unix_epoch()
.try_into()
.expect("Timestamp is valid js_int value"),
),
);
join_event_stub.insert(
"content".to_owned(),
to_canonical_value(RoomMemberEventContent {
membership: MembershipState::Join,
displayname: services().users.displayname(sender_user)?,
avatar_url: services().users.avatar_url(sender_user)?,
is_direct: None,
third_party_invite: None,
blurhash: services().users.blurhash(sender_user)?,
reason,
join_authorized_via_users_server,
})
.expect("event is valid, we just created it"),
);
// We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms
join_event_stub.remove("event_id");
// In order to create a compatible ref hash (EventID) the `hashes` field needs to be present
ruma::signatures::hash_and_sign_event(
services().globals.server_name().as_str(),
services().globals.keypair(),
&mut join_event_stub,
&room_version_id,
)
.expect("event is valid, we just created it");
// Generate event id
let event_id = format!(
"${}",
ruma::signatures::reference_hash(&join_event_stub, &room_version_id)
.expect("ruma can calculate reference hashes")
);
let event_id = <&EventId>::try_from(event_id.as_str())
.expect("ruma's reference hashes are valid event ids");
// Add event_id back
join_event_stub.insert(
"event_id".to_owned(),
CanonicalJsonValue::String(event_id.as_str().to_owned()),
);
// It has enough fields to be called a proper event now
let join_event = join_event_stub;
let send_join_response = services()
.sending
.send_federation_request(
&remote_server,
federation::membership::create_join_event::v2::Request {
room_id: room_id.to_owned(),
event_id: event_id.to_owned(),
pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()),
},
)
.await?;
if let Some(signed_raw) = send_join_response.room_state.event {
let (signed_event_id, signed_value) =
match gen_event_id_canonical_json(&signed_raw, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Could not convert event to canonical json.",
));
}
};
if signed_event_id != event_id {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Server sent event with wrong event id",
));
}
drop(state_lock);
let pub_key_map = RwLock::new(BTreeMap::new());
services()
.rooms
.event_handler
.handle_incoming_pdu(
&remote_server,
&signed_event_id,
room_id,
signed_value,
true,
&pub_key_map,
)
.await?;
} else {
return Err(error);
}
} else {
return Err(error);
}
}
drop(state_lock);
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
}
async fn make_join_request(
sender_user: &UserId,
room_id: &RoomId,
servers: &[OwnedServerName],
) -> Result<(
federation::membership::prepare_join_event::v1::Response,
OwnedServerName,
)> {
let mut make_join_response_and_server = Err(Error::BadServerResponse(
"No server available to assist in joining.",
));
for remote_server in servers {
if remote_server == services().globals.server_name() {
continue;
}
let make_join_response = services()
.sending
.send_federation_request(
remote_server,
federation::membership::prepare_join_event::v1::Request {
room_id: room_id.to_owned(),
user_id: sender_user.to_owned(),
ver: services().globals.supported_room_versions(),
},
)
.await;
make_join_response_and_server = make_join_response.map(|r| (r, remote_server.clone()));
if make_join_response_and_server.is_ok() {
break;
}
}
make_join_response_and_server
}
fn validate_and_add_event_id(
pdu: &RawJsonValue,
room_version: &RoomVersionId,
@ -823,10 +1132,11 @@ pub(crate) async fn invite_helper<'a>(
sender_user: &UserId,
user_id: &UserId,
room_id: &RoomId,
reason: Option<String>,
is_direct: bool,
) -> Result<()> {
if user_id.server_name() != services().globals.server_name() {
let (pdu_json, invite_room_state) = {
let (pdu, pdu_json, invite_room_state) = {
let mutex_state = Arc::clone(
services()
.globals
@ -845,7 +1155,7 @@ pub(crate) async fn invite_helper<'a>(
membership: MembershipState::Invite,
third_party_invite: None,
blurhash: None,
reason: None,
reason,
join_authorized_via_users_server: None,
})
.expect("member event is valid value");
@ -867,31 +1177,21 @@ pub(crate) async fn invite_helper<'a>(
drop(state_lock);
(pdu_json, invite_room_state)
(pdu, pdu_json, invite_room_state)
};
// Generate event id
let expected_event_id = format!(
"${}",
ruma::signatures::reference_hash(
&pdu_json,
&services().rooms.state.get_room_version(room_id)?
)
.expect("ruma can calculate reference hashes")
);
let expected_event_id = <&EventId>::try_from(expected_event_id.as_str())
.expect("ruma's reference hashes are valid event ids");
let room_version_id = services().rooms.state.get_room_version(room_id)?;
let response = services()
.sending
.send_federation_request(
user_id.server_name(),
create_invite::v2::Request {
room_id,
event_id: expected_event_id,
room_version: &services().rooms.state.get_room_version(room_id)?,
event: &PduEvent::convert_to_outgoing_federation_event(pdu_json.clone()),
invite_room_state: &invite_room_state,
room_id: room_id.to_owned(),
event_id: (*pdu.event_id).to_owned(),
room_version: room_version_id.clone(),
event: PduEvent::convert_to_outgoing_federation_event(pdu_json.clone()),
invite_room_state,
},
)
.await?;
@ -899,7 +1199,8 @@ pub(crate) async fn invite_helper<'a>(
let pub_key_map = RwLock::new(BTreeMap::new());
// We do not add the event_id field to the pdu here because of signature and hashes checks
let (event_id, value) = match gen_event_id_canonical_json(&response.event) {
let (event_id, value) = match gen_event_id_canonical_json(&response.event, &room_version_id)
{
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
@ -910,7 +1211,7 @@ pub(crate) async fn invite_helper<'a>(
}
};
if expected_event_id != event_id {
if *pdu.event_id != *event_id {
warn!("Server {} changed invite event, that's not allowed in the spec: ours: {:?}, theirs: {:?}", user_id.server_name(), pdu_json, value);
}
@ -978,7 +1279,7 @@ pub(crate) async fn invite_helper<'a>(
is_direct: Some(is_direct),
third_party_invite: None,
blurhash: services().users.blurhash(user_id)?,
reason: None,
reason,
join_authorized_via_users_server: None,
})
.expect("event is valid, we just created it"),
@ -1017,13 +1318,13 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
Err(_) => continue,
};
let _ = leave_room(user_id, &room_id).await;
let _ = leave_room(user_id, &room_id, None).await;
}
Ok(())
}
pub async fn leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
// Ask a remote server if we don't have this room
if !services().rooms.metadata.exists(room_id)?
&& room_id.server_name() != services().globals.server_name()
@ -1063,21 +1364,35 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
);
let state_lock = mutex_state.lock().await;
let mut event: RoomMemberEventContent = serde_json::from_str(
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
.ok_or(Error::BadRequest(
ErrorKind::BadState,
"Cannot leave a room you are not a member of.",
))?
.content
.get(),
)
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
let member_event = services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomMember,
user_id.as_str(),
)?;
// Fix for broken rooms
let member_event = match member_event {
None => {
error!("Trying to leave a room you are not a member of.");
services().rooms.state_cache.update_membership(
room_id,
user_id,
MembershipState::Leave,
user_id,
None,
true,
)?;
return Ok(());
}
Some(e) => e,
};
let mut event: RoomMemberEventContent = serde_json::from_str(member_event.content.get())
.map_err(|_| Error::bad_database("Invalid member event in database."))?;
event.membership = MembershipState::Leave;
event.reason = reason;
services().rooms.timeline.build_and_append_pdu(
PduBuilder {
@ -1124,7 +1439,10 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
.sending
.send_federation_request(
&remote_server,
federation::membership::prepare_leave_event::v1::Request { room_id, user_id },
federation::membership::prepare_leave_event::v1::Request {
room_id: room_id.to_owned(),
user_id: user_id.to_owned(),
},
)
.await;
@ -1201,9 +1519,9 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
.send_federation_request(
&remote_server,
federation::membership::create_leave_event::v2::Request {
room_id,
event_id: &event_id,
pdu: &PduEvent::convert_to_outgoing_federation_event(leave_event.clone()),
room_id: room_id.to_owned(),
event_id,
pdu: PduEvent::convert_to_outgoing_federation_event(leave_event.clone()),
},
)
.await?;

View file

@ -19,7 +19,7 @@ use std::{
/// - The only requirement for the content is that it has to be valid json
/// - Tries to send the event into the room, auth rules will determine if it is allowed
pub async fn send_message_event_route(
body: Ruma<send_message_event::v3::IncomingRequest>,
body: Ruma<send_message_event::v3::Request>,
) -> Result<send_message_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_deref();
@ -105,7 +105,7 @@ pub async fn send_message_event_route(
/// - Only works if the user is joined (TODO: always allow, but only show events where the user was
/// joined, depending on history_visibility)
pub async fn get_message_events_route(
body: Ruma<get_message_events::v3::IncomingRequest>,
body: Ruma<get_message_events::v3::Request>,
) -> Result<get_message_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");

View file

@ -63,6 +63,6 @@ pub use user_directory::*;
pub use voip::*;
pub const DEVICE_ID_LENGTH: usize = 10;
pub const TOKEN_LENGTH: usize = 256;
pub const SESSION_ID_LENGTH: usize = 256;
pub const TOKEN_LENGTH: usize = 32;
pub const SESSION_ID_LENGTH: usize = 32;
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 15;

View file

@ -6,7 +6,7 @@ use std::time::Duration;
///
/// Sets the presence state of the sender user.
pub async fn set_presence_route(
body: Ruma<set_presence::v3::IncomingRequest>,
body: Ruma<set_presence::v3::Request>,
) -> Result<set_presence::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -43,7 +43,7 @@ pub async fn set_presence_route(
///
/// - Only works if you share a room with the user
pub async fn get_presence_route(
body: Ruma<get_presence::v3::IncomingRequest>,
body: Ruma<get_presence::v3::Request>,
) -> Result<get_presence::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -20,7 +20,7 @@ use std::sync::Arc;
///
/// - Also makes sure other users receive the update using presence EDUs
pub async fn set_displayname_route(
body: Ruma<set_display_name::v3::IncomingRequest>,
body: Ruma<set_display_name::v3::Request>,
) -> Result<set_display_name::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -121,7 +121,7 @@ pub async fn set_displayname_route(
///
/// - If user is on another server: Fetches displayname over federation
pub async fn get_displayname_route(
body: Ruma<get_display_name::v3::IncomingRequest>,
body: Ruma<get_display_name::v3::Request>,
) -> Result<get_display_name::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
let response = services()
@ -129,8 +129,8 @@ pub async fn get_displayname_route(
.send_federation_request(
body.user_id.server_name(),
federation::query::get_profile_information::v1::Request {
user_id: &body.user_id,
field: Some(&ProfileField::DisplayName),
user_id: body.user_id.clone(),
field: Some(ProfileField::DisplayName),
},
)
.await?;
@ -151,7 +151,7 @@ pub async fn get_displayname_route(
///
/// - Also makes sure other users receive the update using presence EDUs
pub async fn set_avatar_url_route(
body: Ruma<set_avatar_url::v3::IncomingRequest>,
body: Ruma<set_avatar_url::v3::Request>,
) -> Result<set_avatar_url::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -256,7 +256,7 @@ pub async fn set_avatar_url_route(
///
/// - If user is on another server: Fetches avatar_url and blurhash over federation
pub async fn get_avatar_url_route(
body: Ruma<get_avatar_url::v3::IncomingRequest>,
body: Ruma<get_avatar_url::v3::Request>,
) -> Result<get_avatar_url::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
let response = services()
@ -264,8 +264,8 @@ pub async fn get_avatar_url_route(
.send_federation_request(
body.user_id.server_name(),
federation::query::get_profile_information::v1::Request {
user_id: &body.user_id,
field: Some(&ProfileField::AvatarUrl),
user_id: body.user_id.clone(),
field: Some(ProfileField::AvatarUrl),
},
)
.await?;
@ -288,7 +288,7 @@ pub async fn get_avatar_url_route(
///
/// - If user is on another server: Fetches profile over federation
pub async fn get_profile_route(
body: Ruma<get_profile::v3::IncomingRequest>,
body: Ruma<get_profile::v3::Request>,
) -> Result<get_profile::v3::Response> {
if body.user_id.server_name() != services().globals.server_name() {
let response = services()
@ -296,7 +296,7 @@ pub async fn get_profile_route(
.send_federation_request(
body.user_id.server_name(),
federation::query::get_profile_information::v1::Request {
user_id: &body.user_id,
user_id: body.user_id.clone(),
field: None,
},
)

View file

@ -5,11 +5,11 @@ use ruma::{
push::{
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled,
get_pushrules_all, set_pusher, set_pushrule, set_pushrule_actions,
set_pushrule_enabled, RuleKind,
set_pushrule_enabled, RuleKind, RuleScope,
},
},
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
push::{ConditionalPushRuleInit, PatternedPushRuleInit, SimplePushRuleInit},
push::{ConditionalPushRuleInit, NewPushRule, PatternedPushRuleInit, SimplePushRuleInit},
};
/// # `GET /_matrix/client/r0/pushrules`
@ -45,7 +45,7 @@ pub async fn get_pushrules_all_route(
///
/// Retrieves a single specified push rule for this user.
pub async fn get_pushrule_route(
body: Ruma<get_pushrule::v3::IncomingRequest>,
body: Ruma<get_pushrule::v3::Request>,
) -> Result<get_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -104,12 +104,12 @@ pub async fn get_pushrule_route(
///
/// Creates a single specified push rule for this user.
pub async fn set_pushrule_route(
body: Ruma<set_pushrule::v3::IncomingRequest>,
body: Ruma<set_pushrule::v3::Request>,
) -> Result<set_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let body = body.body;
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -132,66 +132,65 @@ pub async fn set_pushrule_route(
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
let global = &mut account_data.content.global;
match body.kind {
RuleKind::Override => {
match body.rule {
NewPushRule::Override(rule) => {
global.override_.replace(
ConditionalPushRuleInit {
actions: body.actions,
actions: rule.actions,
default: false,
enabled: true,
rule_id: body.rule_id,
conditions: body.conditions,
rule_id: rule.rule_id,
conditions: rule.conditions,
}
.into(),
);
}
RuleKind::Underride => {
NewPushRule::Underride(rule) => {
global.underride.replace(
ConditionalPushRuleInit {
actions: body.actions,
actions: rule.actions,
default: false,
enabled: true,
rule_id: body.rule_id,
conditions: body.conditions,
rule_id: rule.rule_id,
conditions: rule.conditions,
}
.into(),
);
}
RuleKind::Sender => {
NewPushRule::Sender(rule) => {
global.sender.replace(
SimplePushRuleInit {
actions: body.actions,
actions: rule.actions,
default: false,
enabled: true,
rule_id: body.rule_id,
rule_id: rule.rule_id,
}
.into(),
);
}
RuleKind::Room => {
NewPushRule::Room(rule) => {
global.room.replace(
SimplePushRuleInit {
actions: body.actions,
actions: rule.actions,
default: false,
enabled: true,
rule_id: body.rule_id,
rule_id: rule.rule_id,
}
.into(),
);
}
RuleKind::Content => {
NewPushRule::Content(rule) => {
global.content.replace(
PatternedPushRuleInit {
actions: body.actions,
actions: rule.actions,
default: false,
enabled: true,
rule_id: body.rule_id,
pattern: body.pattern.unwrap_or_default(),
rule_id: rule.rule_id,
pattern: rule.pattern,
}
.into(),
);
}
_ => {}
}
services().account_data.update(
@ -208,11 +207,11 @@ pub async fn set_pushrule_route(
///
/// Gets the actions of a single specified push rule for this user.
pub async fn get_pushrule_actions_route(
body: Ruma<get_pushrule_actions::v3::IncomingRequest>,
body: Ruma<get_pushrule_actions::v3::Request>,
) -> Result<get_pushrule_actions::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -269,11 +268,11 @@ pub async fn get_pushrule_actions_route(
///
/// Sets the actions of a single specified push rule for this user.
pub async fn set_pushrule_actions_route(
body: Ruma<set_pushrule_actions::v3::IncomingRequest>,
body: Ruma<set_pushrule_actions::v3::Request>,
) -> Result<set_pushrule_actions::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -344,11 +343,11 @@ pub async fn set_pushrule_actions_route(
///
/// Gets the enabled status of a single specified push rule for this user.
pub async fn get_pushrule_enabled_route(
body: Ruma<get_pushrule_enabled::v3::IncomingRequest>,
body: Ruma<get_pushrule_enabled::v3::Request>,
) -> Result<get_pushrule_enabled::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -407,11 +406,11 @@ pub async fn get_pushrule_enabled_route(
///
/// Sets the enabled status of a single specified push rule for this user.
pub async fn set_pushrule_enabled_route(
body: Ruma<set_pushrule_enabled::v3::IncomingRequest>,
body: Ruma<set_pushrule_enabled::v3::Request>,
) -> Result<set_pushrule_enabled::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -487,11 +486,11 @@ pub async fn set_pushrule_enabled_route(
///
/// Deletes a single specified push rule for this user.
pub async fn delete_pushrule_route(
body: Ruma<delete_pushrule::v3::IncomingRequest>,
body: Ruma<delete_pushrule::v3::Request>,
) -> Result<delete_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if body.scope != "global" {
if body.scope != RuleScope::Global {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Scopes other than 'global' are not supported.",
@ -575,9 +574,10 @@ pub async fn set_pushers_route(
body: Ruma<set_pusher::v3::Request>,
) -> Result<set_pusher::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let pusher = body.pusher.clone();
services().pusher.set_pusher(sender_user, pusher)?;
services()
.pusher
.set_pusher(sender_user, body.action.clone())?;
Ok(set_pusher::v3::Response::default())
}

View file

@ -16,7 +16,7 @@ use std::collections::BTreeMap;
/// - Updates fully-read account data event to `fully_read`
/// - If `read_receipt` is set: Update private marker and public read receipt EDU
pub async fn set_read_marker_route(
body: Ruma<set_read_marker::v3::IncomingRequest>,
body: Ruma<set_read_marker::v3::Request>,
) -> Result<set_read_marker::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -89,7 +89,7 @@ pub async fn set_read_marker_route(
///
/// Sets private read marker and public read receipt EDU.
pub async fn create_receipt_route(
body: Ruma<create_receipt::v3::IncomingRequest>,
body: Ruma<create_receipt::v3::Request>,
) -> Result<create_receipt::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -14,7 +14,7 @@ use serde_json::value::to_raw_value;
///
/// - TODO: Handle txn id
pub async fn redact_event_route(
body: Ruma<redact_event::v3::IncomingRequest>,
body: Ruma<redact_event::v3::Request>,
) -> Result<redact_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let body = body.body;

View file

@ -10,7 +10,7 @@ use ruma::{
/// Reports an inappropriate event to homeserver admins
///
pub async fn report_event_route(
body: Ruma<report_content::v3::IncomingRequest>,
body: Ruma<report_content::v3::Request>,
) -> Result<report_content::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -46,7 +46,7 @@ use tracing::{info, warn};
/// - Send events implied by `name` and `topic`
/// - Send invite events
pub async fn create_room_route(
body: Ruma<create_room::v3::IncomingRequest>,
body: Ruma<create_room::v3::Request>,
) -> Result<create_room::v3::Response> {
use create_room::v3::RoomPreset;
@ -398,7 +398,7 @@ pub async fn create_room_route(
// 8. Events implied by invite (and TODO: invite_3pid)
drop(state_lock);
for user_id in &body.invite {
let _ = invite_helper(sender_user, user_id, &room_id, body.is_direct).await;
let _ = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await;
}
// Homeserver specific stuff
@ -421,7 +421,7 @@ pub async fn create_room_route(
///
/// - You have to currently be joined to the room (TODO: Respect history visibility)
pub async fn get_room_event_route(
body: Ruma<get_room_event::v3::IncomingRequest>,
body: Ruma<get_room_event::v3::Request>,
) -> Result<get_room_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -452,7 +452,7 @@ pub async fn get_room_event_route(
///
/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable
pub async fn get_room_aliases_route(
body: Ruma<aliases::v3::IncomingRequest>,
body: Ruma<aliases::v3::Request>,
) -> Result<aliases::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -488,7 +488,7 @@ pub async fn get_room_aliases_route(
/// - Moves local aliases
/// - Modifies old room power levels to prevent users from speaking
pub async fn upgrade_room_route(
body: Ruma<upgrade_room::v3::IncomingRequest>,
body: Ruma<upgrade_room::v3::Request>,
) -> Result<upgrade_room::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -15,7 +15,7 @@ use std::collections::BTreeMap;
///
/// - Only works if the user is currently joined to the room (TODO: Respect history visibility)
pub async fn search_events_route(
body: Ruma<search_events::v3::IncomingRequest>,
body: Ruma<search_events::v3::Request>,
) -> Result<search_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -103,7 +103,7 @@ pub async fn search_events_route(
.take(limit)
.collect();
let next_batch = if results.len() < limit as usize {
let next_batch = if results.len() < limit {
None
} else {
Some((skip + limit).to_string())

View file

@ -4,7 +4,7 @@ use ruma::{
api::client::{
error::ErrorKind,
session::{get_login_types, login, logout, logout_all},
uiaa::IncomingUserIdentifier,
uiaa::UserIdentifier,
},
UserId,
};
@ -22,7 +22,7 @@ struct Claims {
/// Get the supported login types of this server. One of these should be used as the `type` field
/// when logging in.
pub async fn get_login_types_route(
_body: Ruma<get_login_types::v3::IncomingRequest>,
_body: Ruma<get_login_types::v3::Request>,
) -> Result<get_login_types::v3::Response> {
Ok(get_login_types::v3::Response::new(vec![
get_login_types::v3::LoginType::Password(Default::default()),
@ -40,15 +40,15 @@ pub async fn get_login_types_route(
///
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
/// supported login types.
pub async fn login_route(body: Ruma<login::v3::IncomingRequest>) -> Result<login::v3::Response> {
pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
// Validate login method
// TODO: Other login methods
let user_id = match &body.login_info {
login::v3::IncomingLoginInfo::Password(login::v3::IncomingPassword {
login::v3::LoginInfo::Password(login::v3::Password {
identifier,
password,
}) => {
let username = if let IncomingUserIdentifier::UserIdOrLocalpart(user_id) = identifier {
let username = if let UserIdentifier::UserIdOrLocalpart(user_id) = identifier {
user_id.to_lowercase()
} else {
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
@ -84,7 +84,7 @@ pub async fn login_route(body: Ruma<login::v3::IncomingRequest>) -> Result<login
user_id
}
login::v3::IncomingLoginInfo::Token(login::v3::IncomingToken { token }) => {
login::v3::LoginInfo::Token(login::v3::Token { token }) => {
if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() {
let token = jsonwebtoken::decode::<Claims>(
token,

View file

@ -25,7 +25,7 @@ use ruma::{
/// - Tries to send the event into the room, auth rules will determine if it is allowed
/// - If event is new canonical_alias: Rejects if alias is incorrect
pub async fn send_state_event_for_key_route(
body: Ruma<send_state_event::v3::IncomingRequest>,
body: Ruma<send_state_event::v3::Request>,
) -> Result<send_state_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -50,7 +50,7 @@ pub async fn send_state_event_for_key_route(
/// - Tries to send the event into the room, auth rules will determine if it is allowed
/// - If event is new canonical_alias: Rejects if alias is incorrect
pub async fn send_state_event_for_empty_key_route(
body: Ruma<send_state_event::v3::IncomingRequest>,
body: Ruma<send_state_event::v3::Request>,
) -> Result<RumaResponse<send_state_event::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -81,7 +81,7 @@ pub async fn send_state_event_for_empty_key_route(
///
/// - If not joined: Only works if current room history visibility is world readable
pub async fn get_state_events_route(
body: Ruma<get_state_events::v3::IncomingRequest>,
body: Ruma<get_state_events::v3::Request>,
) -> Result<get_state_events::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -133,7 +133,7 @@ pub async fn get_state_events_route(
///
/// - If not joined: Only works if current room history visibility is world readable
pub async fn get_state_events_for_key_route(
body: Ruma<get_state_events_for_key::v3::IncomingRequest>,
body: Ruma<get_state_events_for_key::v3::Request>,
) -> Result<get_state_events_for_key::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -188,7 +188,7 @@ pub async fn get_state_events_for_key_route(
///
/// - If not joined: Only works if current room history visibility is world readable
pub async fn get_state_events_for_empty_key_route(
body: Ruma<get_state_events_for_key::v3::IncomingRequest>,
body: Ruma<get_state_events_for_key::v3::Request>,
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -1,7 +1,7 @@
use crate::{services, Error, Result, Ruma, RumaResponse};
use ruma::{
api::client::{
filter::{IncomingFilterDefinition, LazyLoadOptions},
filter::{FilterDefinition, LazyLoadOptions},
sync::sync_events::{self, DeviceLists, UnreadNotificationsCount},
uiaa::UiaaResponse,
},
@ -55,7 +55,7 @@ use tracing::error;
/// - Sync is handled in an async task, multiple requests from the same device with the same
/// `since` will be cached
pub async fn sync_events_route(
body: Ruma<sync_events::v3::IncomingRequest>,
body: Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
let sender_user = body.sender_user.expect("user is authenticated");
let sender_device = body.sender_device.expect("user is authenticated");
@ -124,7 +124,7 @@ pub async fn sync_events_route(
async fn sync_helper_wrapper(
sender_user: OwnedUserId,
sender_device: OwnedDeviceId,
body: sync_events::v3::IncomingRequest,
body: sync_events::v3::Request,
tx: Sender<Option<Result<sync_events::v3::Response>>>,
) {
let since = body.since.clone();
@ -157,12 +157,12 @@ async fn sync_helper_wrapper(
async fn sync_helper(
sender_user: OwnedUserId,
sender_device: OwnedDeviceId,
body: sync_events::v3::IncomingRequest,
body: sync_events::v3::Request,
// bool = caching allowed
) -> Result<(sync_events::v3::Response, bool), Error> {
use sync_events::v3::{
Ephemeral, GlobalAccountData, IncomingFilter, InviteState, InvitedRoom, JoinedRoom,
LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice,
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom,
Presence, RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice,
};
// TODO: match body.set_presence {
@ -176,9 +176,9 @@ async fn sync_helper(
// Load filter
let filter = match body.filter {
None => IncomingFilterDefinition::default(),
Some(IncomingFilter::FilterDefinition(filter)) => filter,
Some(IncomingFilter::FilterId(filter_id)) => services()
None => FilterDefinition::default(),
Some(Filter::FilterDefinition(filter)) => filter,
Some(Filter::FilterId(filter_id)) => services()
.users
.get_filter(&sender_user, &filter_id)?
.unwrap_or_default(),
@ -282,9 +282,8 @@ async fn sync_helper(
let send_notification_counts = !timeline_pdus.is_empty()
|| services()
.rooms
.edus
.read_receipt
.last_privateread_update(&sender_user, &room_id)?
.user
.last_notification_read(&sender_user, &room_id)?
> since;
let mut timeline_users = HashSet::new();
@ -389,13 +388,35 @@ async fn sync_helper(
))
};
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
.and_then(|shortstatehash| {
services()
.rooms
.state_accessor
.state_get(
shortstatehash,
&StateEventType::RoomMember,
sender_user.as_str(),
)
.transpose()
})
.transpose()?
.and_then(|pdu| {
serde_json::from_str(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid PDU in database."))
.ok()
});
let joined_since_last_sync =
since_sender_member.map_or(true, |member| member.membership != MembershipState::Join);
let (
heroes,
joined_member_count,
invited_member_count,
joined_since_last_sync,
state_events,
) = if since_shortstatehash.is_none() {
) = if since_shortstatehash.is_none() || joined_since_last_sync {
// Probably since = 0, we will do an initial sync
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?;
@ -488,23 +509,6 @@ async fn sync_helper(
// Incremental /sync
let since_shortstatehash = since_shortstatehash.unwrap();
let since_sender_member: Option<RoomMemberEventContent> = services()
.rooms
.state_accessor
.state_get(
since_shortstatehash,
&StateEventType::RoomMember,
sender_user.as_str(),
)?
.and_then(|pdu| {
serde_json::from_str(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid PDU in database."))
.ok()
});
let joined_since_last_sync = since_sender_member
.map_or(true, |member| member.membership != MembershipState::Join);
let mut state_events = Vec::new();
let mut lazy_loaded = HashSet::new();
@ -869,7 +873,7 @@ async fn sync_helper(
let since_state_ids = match since_shortstatehash {
Some(s) => services().rooms.state_accessor.state_full_ids(s).await?,
None => BTreeMap::new(),
None => HashMap::new(),
};
let left_event_id = match services().rooms.state_accessor.room_state_get_id(
@ -905,7 +909,7 @@ async fn sync_helper(
let leave_shortstatekey = services()
.rooms
.short
.get_or_create_shortstatekey(&StateEventType::RoomMember, &sender_user.as_str())?;
.get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?;
left_state_ids.insert(leave_shortstatekey, left_event_id);

View file

@ -14,7 +14,7 @@ use std::collections::BTreeMap;
///
/// - Inserts the tag into the tag event of the room account data.
pub async fn update_tag_route(
body: Ruma<create_tag::v3::IncomingRequest>,
body: Ruma<create_tag::v3::Request>,
) -> Result<create_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -58,7 +58,7 @@ pub async fn update_tag_route(
///
/// - Removes the tag from the tag event of the room account data.
pub async fn delete_tag_route(
body: Ruma<delete_tag::v3::IncomingRequest>,
body: Ruma<delete_tag::v3::Request>,
) -> Result<delete_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -98,9 +98,7 @@ pub async fn delete_tag_route(
/// Returns tags on the room.
///
/// - Gets the tag event of the room account data.
pub async fn get_tags_route(
body: Ruma<get_tags::v3::IncomingRequest>,
) -> Result<get_tags::v3::Response> {
pub async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tags::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services().account_data.get(

View file

@ -7,7 +7,7 @@ use std::collections::BTreeMap;
///
/// TODO: Fetches all metadata about protocols supported by the homeserver.
pub async fn get_protocols_route(
_body: Ruma<get_protocols::v3::IncomingRequest>,
_body: Ruma<get_protocols::v3::Request>,
) -> Result<get_protocols::v3::Response> {
// TODO
Ok(get_protocols::v3::Response {

View file

@ -14,7 +14,7 @@ use ruma::{
///
/// Send a to-device event to a set of client devices.
pub async fn send_event_to_device_route(
body: Ruma<send_event_to_device::v3::IncomingRequest>,
body: Ruma<send_event_to_device::v3::Request>,
) -> Result<send_event_to_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_deref();

View file

@ -5,7 +5,7 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
///
/// Sets the typing state of the sender user.
pub async fn create_typing_event_route(
body: Ruma<create_typing_event::v3::IncomingRequest>,
body: Ruma<create_typing_event::v3::Request>,
) -> Result<create_typing_event::v3::Response> {
use create_typing_event::v3::Typing;

View file

@ -15,7 +15,7 @@ use crate::{Result, Ruma};
/// Note: Unstable features are used while developing new features. Clients should avoid using
/// unstable features in their stable releases
pub async fn get_supported_versions_route(
_body: Ruma<get_supported_versions::IncomingRequest>,
_body: Ruma<get_supported_versions::Request>,
) -> Result<get_supported_versions::Response> {
let resp = get_supported_versions::Response {
versions: vec![

View file

@ -14,7 +14,7 @@ use ruma::{
/// - Hides any local users that aren't in any public rooms (i.e. those that have the join rule set to public)
/// and don't share a room with the sender
pub async fn search_users_route(
body: Ruma<search_users::v3::IncomingRequest>,
body: Ruma<search_users::v3::Request>,
) -> Result<search_users::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let limit = u64::from(body.limit) as usize;

View file

@ -10,7 +10,7 @@ type HmacSha1 = Hmac<Sha1>;
///
/// TODO: Returns information about the recommended turn server.
pub async fn turn_server_route(
body: Ruma<get_turn_server_info::v3::IncomingRequest>,
body: Ruma<get_turn_server_info::v3::Request>,
) -> Result<get_turn_server_info::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -311,8 +311,7 @@ impl Credentials for XMatrix {
fn decode(value: &http::HeaderValue) -> Option<Self> {
debug_assert!(
value.as_bytes().starts_with(b"X-Matrix "),
"HeaderValue to decode should start with \"X-Matrix ..\", received = {:?}",
value,
"HeaderValue to decode should start with \"X-Matrix ..\", received = {value:?}",
);
let parameters = str::from_utf8(&value.as_bytes()["X-Matrix ".len()..])

View file

@ -31,7 +31,7 @@ use ruma::{
EndpointError, IncomingResponse, MatrixVersion, OutgoingRequest, OutgoingResponse,
SendAccessToken,
},
directory::{IncomingFilter, IncomingRoomNetwork},
directory::{Filter, RoomNetwork},
events::{
receipt::{ReceiptEvent, ReceiptEventContent, ReceiptType},
room::{
@ -42,8 +42,8 @@ use ruma::{
},
serde::{Base64, JsonObject, Raw},
to_device::DeviceIdOrAllDevices,
CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, RoomId, ServerName,
CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId,
OwnedRoomId, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, RoomId, ServerName,
};
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use std::{
@ -55,7 +55,7 @@ use std::{
time::{Duration, Instant, SystemTime},
};
use tracing::{error, info, warn};
use tracing::{debug, error, info, warn};
/// Wraps either an literal IP address plus port, or a hostname plus complement
/// (colon-plus-port if it was specified).
@ -84,8 +84,8 @@ pub enum FedDest {
impl FedDest {
fn into_https_string(self) -> String {
match self {
Self::Literal(addr) => format!("https://{}", addr),
Self::Named(host, port) => format!("https://{}{}", host, port),
Self::Literal(addr) => format!("https://{addr}"),
Self::Named(host, port) => format!("https://{host}{port}"),
}
}
@ -294,13 +294,7 @@ where
} else {
Err(Error::FederationError(
destination.to_owned(),
RumaError::try_from_http_response(http_response).map_err(|e| {
warn!(
"Invalid {} response from {} on: {} {}",
status, &destination, url, e
);
Error::BadServerResponse("Server returned bad error response.")
})?,
RumaError::from_http_response(http_response),
))
}
}
@ -391,7 +385,7 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
}
if let Some(port) = force_port {
FedDest::Named(delegated_hostname, format!(":{}", port))
FedDest::Named(delegated_hostname, format!(":{port}"))
} else {
add_port_to_hostname(&delegated_hostname)
}
@ -433,7 +427,7 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
}
if let Some(port) = force_port {
FedDest::Named(hostname.clone(), format!(":{}", port))
FedDest::Named(hostname.clone(), format!(":{port}"))
} else {
add_port_to_hostname(&hostname)
}
@ -466,7 +460,7 @@ async fn query_srv_record(hostname: &'_ str) -> Option<FedDest> {
if let Ok(Some(host_port)) = services()
.globals
.dns_resolver()
.srv_lookup(format!("_matrix._tcp.{}", hostname))
.srv_lookup(format!("_matrix._tcp.{hostname}"))
.await
.map(|srv| {
srv.iter().next().map(|result| {
@ -488,10 +482,7 @@ async fn request_well_known(destination: &str) -> Option<String> {
&services()
.globals
.default_client()
.get(&format!(
"https://{}/.well-known/matrix/server",
destination
))
.get(&format!("https://{destination}/.well-known/matrix/server"))
.send()
.await
.ok()?
@ -586,7 +577,7 @@ pub async fn get_server_keys_deprecated_route() -> impl IntoResponse {
///
/// Lists the public rooms on this server.
pub async fn get_public_rooms_filtered_route(
body: Ruma<get_public_rooms_filtered::v1::IncomingRequest>,
body: Ruma<get_public_rooms_filtered::v1::Request>,
) -> Result<get_public_rooms_filtered::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -613,7 +604,7 @@ pub async fn get_public_rooms_filtered_route(
///
/// Lists the public rooms on this server.
pub async fn get_public_rooms_route(
body: Ruma<get_public_rooms::v1::IncomingRequest>,
body: Ruma<get_public_rooms::v1::Request>,
) -> Result<get_public_rooms::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -623,8 +614,8 @@ pub async fn get_public_rooms_route(
None,
body.limit,
body.since.as_deref(),
&IncomingFilter::default(),
&IncomingRoomNetwork::Matrix,
&Filter::default(),
&RoomNetwork::Matrix,
)
.await?;
@ -640,7 +631,7 @@ pub async fn get_public_rooms_route(
///
/// Push EDUs and PDUs to this server.
pub async fn send_transaction_message_route(
body: Ruma<send_transaction_message::v1::IncomingRequest>,
body: Ruma<send_transaction_message::v1::Request>,
) -> Result<send_transaction_message::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -664,16 +655,11 @@ pub async fn send_transaction_message_route(
// let mut auth_cache = EventMap::new();
for pdu in &body.pdus {
// We do not add the event_id field to the pdu here because of signature and hashes checks
let (event_id, value) = match gen_event_id_canonical_json(pdu) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
continue;
}
};
let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
warn!("Error parsing incoming event {:?}: {:?}", pdu, e);
Error::BadServerResponse("Invalid PDU in server response")
})?;
// 0. Check the server is in the room
let room_id: OwnedRoomId = match value
.get("room_id")
.and_then(|id| RoomId::parse(id.as_str()?).ok())
@ -681,14 +667,26 @@ pub async fn send_transaction_message_route(
Some(id) => id,
None => {
// Event is invalid
resolved_map.insert(
event_id,
Err(Error::bad_database("Event needs a valid RoomId.")),
);
continue;
}
};
let room_version_id = match services().rooms.state.get_room_version(&room_id) {
Ok(v) => v,
Err(_) => {
continue;
}
};
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
continue;
}
};
// We do not add the event_id field to the pdu here because of signature and hashes checks
services()
.rooms
.event_handler
@ -724,7 +722,7 @@ pub async fn send_transaction_message_route(
drop(mutex_lock);
let elapsed = start_time.elapsed();
warn!(
debug!(
"Handling transaction of event {} took {}m{}s",
event_id,
elapsed.as_secs() / 60,
@ -909,7 +907,7 @@ pub async fn send_transaction_message_route(
///
/// - Only works if a user of this server is currently invited or joined the room
pub async fn get_event_route(
body: Ruma<get_event::v1::IncomingRequest>,
body: Ruma<get_event::v1::Request>,
) -> Result<get_event::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -956,7 +954,7 @@ pub async fn get_event_route(
///
/// Retrieves events that the sender is missing.
pub async fn get_missing_events_route(
body: Ruma<get_missing_events::v1::IncomingRequest>,
body: Ruma<get_missing_events::v1::Request>,
) -> Result<get_missing_events::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1035,7 +1033,7 @@ pub async fn get_missing_events_route(
///
/// - This does not include the event itself
pub async fn get_event_authorization_route(
body: Ruma<get_event_authorization::v1::IncomingRequest>,
body: Ruma<get_event_authorization::v1::Request>,
) -> Result<get_event_authorization::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1094,7 +1092,7 @@ pub async fn get_event_authorization_route(
///
/// Retrieves the current state of the room.
pub async fn get_room_state_route(
body: Ruma<get_room_state::v1::IncomingRequest>,
body: Ruma<get_room_state::v1::Request>,
) -> Result<get_room_state::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1174,7 +1172,7 @@ pub async fn get_room_state_route(
///
/// Retrieves the current state of the room.
pub async fn get_room_state_ids_route(
body: Ruma<get_room_state_ids::v1::IncomingRequest>,
body: Ruma<get_room_state_ids::v1::Request>,
) -> Result<get_room_state_ids::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1235,7 +1233,7 @@ pub async fn get_room_state_ids_route(
///
/// Creates a join template.
pub async fn create_join_event_template_route(
body: Ruma<prepare_join_event::v1::IncomingRequest>,
body: Ruma<prepare_join_event::v1::Request>,
) -> Result<prepare_join_event::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1407,7 +1405,8 @@ async fn create_join_event(
// let mut auth_cache = EventMap::new();
// We do not add the event_id field to the pdu here because of signature and hashes checks
let (event_id, value) = match gen_event_id_canonical_json(pdu) {
let room_version_id = services().rooms.state.get_room_version(room_id)?;
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
Ok(t) => t,
Err(_) => {
// Event could not be converted to canonical json
@ -1478,6 +1477,7 @@ async fn create_join_event(
.filter_map(|(_, id)| services().rooms.timeline.get_pdu_json(id).ok().flatten())
.map(PduEvent::convert_to_outgoing_federation_event)
.collect(),
event: None, // TODO: handle restricted joins
})
}
@ -1485,7 +1485,7 @@ async fn create_join_event(
///
/// Submits a signed join event.
pub async fn create_join_event_v1_route(
body: Ruma<create_join_event::v1::IncomingRequest>,
body: Ruma<create_join_event::v1::Request>,
) -> Result<create_join_event::v1::Response> {
let sender_servername = body
.sender_servername
@ -1501,7 +1501,7 @@ pub async fn create_join_event_v1_route(
///
/// Submits a signed join event.
pub async fn create_join_event_v2_route(
body: Ruma<create_join_event::v2::IncomingRequest>,
body: Ruma<create_join_event::v2::Request>,
) -> Result<create_join_event::v2::Response> {
let sender_servername = body
.sender_servername
@ -1517,7 +1517,7 @@ pub async fn create_join_event_v2_route(
///
/// Invites a remote user to a room.
pub async fn create_invite_route(
body: Ruma<create_invite::v2::IncomingRequest>,
body: Ruma<create_invite::v2::Request>,
) -> Result<create_invite::v2::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1609,8 +1609,12 @@ pub async fn create_invite_route(
invite_state.push(pdu.to_stripped_state_event());
// If the room already exists, the remote server will notify us about the join via /send
if !services().rooms.metadata.exists(&pdu.room_id)? {
// If we are active in the room, the remote server will notify us about the join via /send
if !services()
.rooms
.state_cache
.server_in_room(services().globals.server_name(), &body.room_id)?
{
services().rooms.state_cache.update_membership(
&body.room_id,
&invited_user,
@ -1630,7 +1634,7 @@ pub async fn create_invite_route(
///
/// Gets information on all devices of the user.
pub async fn get_devices_route(
body: Ruma<get_devices::v1::IncomingRequest>,
body: Ruma<get_devices::v1::Request>,
) -> Result<get_devices::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1677,7 +1681,7 @@ pub async fn get_devices_route(
///
/// Resolve a room alias to a room id.
pub async fn get_room_information_route(
body: Ruma<get_room_information::v1::IncomingRequest>,
body: Ruma<get_room_information::v1::Request>,
) -> Result<get_room_information::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));
@ -1702,7 +1706,7 @@ pub async fn get_room_information_route(
///
/// Gets information on a profile.
pub async fn get_profile_information_route(
body: Ruma<get_profile_information::v1::IncomingRequest>,
body: Ruma<get_profile_information::v1::Request>,
) -> Result<get_profile_information::v1::Response> {
if !services().globals.allow_federation() {
return Err(Error::bad_config("Federation is disabled."));