apply new rustfmt.toml changes, fix some clippy lints

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-12-15 00:05:47 -05:00
parent 0317cc8cc5
commit 77e0b76408
No known key found for this signature in database
296 changed files with 7147 additions and 4300 deletions

View file

@ -2,16 +2,19 @@ use std::fmt::Write;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{debug_info, error, info, is_equal_to, utils, utils::ReadyExt, warn, Error, PduBuilder, Result};
use conduwuit::{
debug_info, error, info, is_equal_to, utils, utils::ReadyExt, warn, Error, PduBuilder, Result,
};
use futures::{FutureExt, StreamExt};
use register::RegistrationKind;
use ruma::{
api::client::{
account::{
change_password, check_registration_token_validity, deactivate, get_3pids, get_username_availability,
change_password, check_registration_token_validity, deactivate, get_3pids,
get_username_availability,
register::{self, LoginType},
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},
@ -45,7 +48,8 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
/// invalid when trying to register
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
pub(crate) async fn get_register_available_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
@ -66,7 +70,8 @@ pub(crate) async fn get_register_available_route(
let user_id = UserId::parse_with_server_name(body_username, services.globals.server_name())
.ok()
.filter(|user_id| {
(!user_id.is_historical() || is_matrix_appservice_irc) && services.globals.user_is_local(user_id)
(!user_id.is_historical() || is_matrix_appservice_irc)
&& services.globals.user_is_local(user_id)
})
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
@ -86,9 +91,7 @@ pub(crate) async fn get_register_available_route(
// TODO add check for appservice namespaces
// If no if check is true we have an username that's available to be used.
Ok(get_username_availability::v3::Response {
available: true,
})
Ok(get_username_availability::v3::Response { available: true })
}
/// # `POST /_matrix/client/v3/register`
@ -111,12 +114,14 @@ pub(crate) async fn get_register_available_route(
#[allow(clippy::doc_markdown)]
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn register_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<register::v3::Request>,
) -> Result<register::v3::Response> {
if !services.globals.allow_registration() && body.appservice_info.is_none() {
info!(
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
\"{}\"",
"Registration disabled and request not from known appservice, rejecting \
registration attempt for username \"{}\"",
body.username.as_deref().unwrap_or("")
);
return Err(Error::BadRequest(ErrorKind::forbidden(), "Registration has been disabled."));
@ -126,11 +131,12 @@ pub(crate) async fn register_route(
if is_guest
&& (!services.globals.allow_guest_registration()
|| (services.globals.allow_registration() && services.globals.registration_token.is_some()))
|| (services.globals.allow_registration()
&& services.globals.registration_token.is_some()))
{
info!(
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
attempt, initial device name: \"{}\"",
"Guest registration disabled / registration enabled with token configured, \
rejecting guest registration attempt, initial device name: \"{}\"",
body.initial_device_display_name.as_deref().unwrap_or("")
);
return Err(Error::BadRequest(
@ -143,21 +149,25 @@ pub(crate) async fn register_route(
// generic user error.
if is_guest && services.users.count().await < 2 {
warn!(
"Guest account attempted to register before a real admin user has been registered, rejecting \
registration. Guest's initial device name: \"{}\"",
"Guest account attempted to register before a real admin user has been registered, \
rejecting registration. Guest's initial device name: \"{}\"",
body.initial_device_display_name.as_deref().unwrap_or("")
);
return Err(Error::BadRequest(ErrorKind::forbidden(), "Registration temporarily disabled."));
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Registration temporarily disabled.",
));
}
let user_id = match (&body.username, is_guest) {
(Some(username), false) => {
| (Some(username), false) => {
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
let is_matrix_appservice_irc = body.appservice_info.as_ref().is_some_and(|appservice| {
appservice.registration.id == "irc"
|| appservice.registration.id.contains("matrix-appservice-irc")
|| appservice.registration.id.contains("matrix_appservice_irc")
});
let is_matrix_appservice_irc =
body.appservice_info.as_ref().is_some_and(|appservice| {
appservice.registration.id == "irc"
|| appservice.registration.id.contains("matrix-appservice-irc")
|| appservice.registration.id.contains("matrix_appservice_irc")
});
// don't force the username lowercase if it's from matrix-appservice-irc
let body_username = if is_matrix_appservice_irc {
@ -166,15 +176,23 @@ pub(crate) async fn register_route(
username.to_lowercase()
};
let proposed_user_id = UserId::parse_with_server_name(body_username, services.globals.server_name())
.ok()
.filter(|user_id| {
(!user_id.is_historical() || is_matrix_appservice_irc) && services.globals.user_is_local(user_id)
})
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
let proposed_user_id =
UserId::parse_with_server_name(body_username, services.globals.server_name())
.ok()
.filter(|user_id| {
(!user_id.is_historical() || is_matrix_appservice_irc)
&& services.globals.user_is_local(user_id)
})
.ok_or(Error::BadRequest(
ErrorKind::InvalidUsername,
"Username is invalid.",
))?;
if services.users.exists(&proposed_user_id).await {
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
return Err(Error::BadRequest(
ErrorKind::UserInUse,
"Desired user ID is already taken.",
));
}
if services
@ -187,7 +205,7 @@ pub(crate) async fn register_route(
proposed_user_id
},
_ => loop {
| _ => loop {
let proposed_user_id = UserId::parse_with_server_name(
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
services.globals.server_name(),
@ -228,9 +246,7 @@ pub(crate) async fn register_route(
} else {
// No registration token necessary, but clients must still go through the flow
uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Dummy],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
completed: Vec::new(),
params: Box::default(),
session: None,
@ -244,7 +260,8 @@ pub(crate) async fn register_route(
let (worked, uiaainfo) = services
.uiaa
.try_auth(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
&UserId::parse_with_server_name("", services.globals.server_name())
.expect("we know this is valid"),
"".into(),
auth,
&uiaainfo,
@ -257,7 +274,8 @@ pub(crate) async fn register_route(
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services.uiaa.create(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
&UserId::parse_with_server_name("", services.globals.server_name())
.expect("we know this is valid"),
"".into(),
&uiaainfo,
&json,
@ -268,11 +286,7 @@ pub(crate) async fn register_route(
}
}
let password = if is_guest {
None
} else {
body.password.as_deref()
};
let password = if is_guest { None } else { body.password.as_deref() };
// Create user
services.users.create(&user_id, password)?;
@ -282,7 +296,9 @@ pub(crate) async fn register_route(
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !services.globals.new_user_displayname_suffix().is_empty() && body.appservice_info.is_none() {
if !services.globals.new_user_displayname_suffix().is_empty()
&& body.appservice_info.is_none()
{
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
@ -319,12 +335,8 @@ pub(crate) async fn register_route(
}
// Generate new device id if the user didn't specify one
let device_id = if is_guest {
None
} else {
body.device_id.clone()
}
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
let device_id = if is_guest { None } else { body.device_id.clone() }
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
// Generate new token for the device
let token = utils::random_string(TOKEN_LENGTH);
@ -349,15 +361,16 @@ pub(crate) async fn register_route(
if body.appservice_info.is_none() && !is_guest {
if !device_display_name.is_empty() {
info!(
"New user \"{user_id}\" registered on this server with device display name: \"{device_display_name}\""
"New user \"{user_id}\" registered on this server with device display name: \
\"{device_display_name}\""
);
if services.globals.config.admin_room_notices {
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"New user \"{user_id}\" registered on this server from IP {client} and device display name \
\"{device_display_name}\""
"New user \"{user_id}\" registered on this server from IP {client} and \
device display name \"{device_display_name}\""
)))
.await
.ok();
@ -386,8 +399,8 @@ pub(crate) async fn register_route(
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with device display name \"{device_display_name}\" registered on \
this server from IP {client}"
"Guest user \"{user_id}\" with device display name \
\"{device_display_name}\" registered on this server from IP {client}"
)))
.await
.ok();
@ -398,8 +411,8 @@ pub(crate) async fn register_route(
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
{client}",
"Guest user \"{user_id}\" with no device display name registered on \
this server from IP {client}",
)))
.await
.ok();
@ -430,7 +443,10 @@ pub(crate) async fn register_route(
{
for room in &services.globals.config.auto_join_rooms {
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
error!("Failed to resolve room alias to room ID when attempting to auto join {room}, skipping");
error!(
"Failed to resolve room alias to room ID when attempting to auto join \
{room}, skipping"
);
continue;
};
@ -440,7 +456,9 @@ pub(crate) async fn register_route(
.server_in_room(services.globals.server_name(), &room_id)
.await
{
warn!("Skipping room {room} to automatically join as we have never joined before.");
warn!(
"Skipping room {room} to automatically join as we have never joined before."
);
continue;
}
@ -494,7 +512,8 @@ pub(crate) async fn register_route(
/// - Triggers device list updates
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
pub(crate) async fn change_password_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
// Authentication for this endpoint was made optional, but we need
@ -506,9 +525,7 @@ pub(crate) async fn change_password_route(
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Password],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
completed: Vec::new(),
params: Box::default(),
session: None,
@ -572,7 +589,8 @@ pub(crate) async fn change_password_route(
///
/// Note: Also works for Application Services
pub(crate) async fn whoami_route(
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
State(services): State<crate::State>,
body: Ruma<whoami::v3::Request>,
) -> Result<whoami::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let device_id = body.sender_device.clone();
@ -580,7 +598,8 @@ pub(crate) async fn whoami_route(
Ok(whoami::v3::Response {
user_id: sender_user.clone(),
device_id,
is_guest: services.users.is_deactivated(sender_user).await? && body.appservice_info.is_none(),
is_guest: services.users.is_deactivated(sender_user).await?
&& body.appservice_info.is_none(),
})
}
@ -597,7 +616,8 @@ pub(crate) async fn whoami_route(
/// - Removes ability to log in again
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
pub(crate) async fn deactivate_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<deactivate::v3::Request>,
) -> Result<deactivate::v3::Response> {
// Authentication for this endpoint was made optional, but we need
@ -609,9 +629,7 @@ pub(crate) async fn deactivate_route(
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Password],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
completed: Vec::new(),
params: Box::default(),
session: None,
@ -675,7 +693,9 @@ pub(crate) async fn deactivate_route(
/// Get a list of third party identifiers associated with this account.
///
/// - Currently always returns empty list
pub(crate) async fn third_party_route(body: Ruma<get_3pids::v3::Request>) -> Result<get_3pids::v3::Response> {
pub(crate) async fn third_party_route(
body: Ruma<get_3pids::v3::Request>,
) -> Result<get_3pids::v3::Response> {
let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
Ok(get_3pids::v3::Response::new(Vec::new()))
@ -720,7 +740,8 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
/// Currently does not have any ratelimiting, and this isn't very practical as
/// there is only one registration token allowed.
pub(crate) async fn check_registration_token_validity(
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
State(services): State<crate::State>,
body: Ruma<check_registration_token_validity::v1::Request>,
) -> Result<check_registration_token_validity::v1::Response> {
let Some(reg_token) = services.globals.registration_token.clone() else {
return Err(Error::BadRequest(
@ -729,9 +750,7 @@ pub(crate) async fn check_registration_token_validity(
));
};
Ok(check_registration_token_validity::v1::Response {
valid: reg_token == body.token,
})
Ok(check_registration_token_validity::v1::Response { valid: reg_token == body.token })
}
/// Runs through all the deactivation steps:
@ -742,7 +761,9 @@ pub(crate) async fn check_registration_token_validity(
/// - Removing all profile data
/// - Leaving all rooms (and forgets all of them)
pub async fn full_user_deactivate(
services: &Services, user_id: &UserId, all_joined_rooms: &[OwnedRoomId],
services: &Services,
user_id: &UserId,
all_joined_rooms: &[OwnedRoomId],
) -> Result<()> {
services.users.deactivate_account(user_id).await.ok();
super::update_displayname(services, user_id, None, all_joined_rooms).await;
@ -751,7 +772,9 @@ pub async fn full_user_deactivate(
services
.users
.all_profile_keys(user_id)
.ready_for_each(|(profile_key, _)| services.users.set_profile_key(user_id, &profile_key, None))
.ready_for_each(|(profile_key, _)| {
services.users.set_profile_key(user_id, &profile_key, None);
})
.await;
for room_id in all_joined_rooms {
@ -760,20 +783,26 @@ pub async fn full_user_deactivate(
let room_power_levels = services
.rooms
.state_accessor
.room_state_get_content::<RoomPowerLevelsEventContent>(room_id, &StateEventType::RoomPowerLevels, "")
.room_state_get_content::<RoomPowerLevelsEventContent>(
room_id,
&StateEventType::RoomPowerLevels,
"",
)
.await
.ok();
let user_can_demote_self = room_power_levels
.as_ref()
.is_some_and(|power_levels_content| {
RoomPowerLevels::from(power_levels_content.clone()).user_can_change_user_power_level(user_id, user_id)
}) || services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")
.await
.is_ok_and(|event| event.sender == user_id);
let user_can_demote_self =
room_power_levels
.as_ref()
.is_some_and(|power_levels_content| {
RoomPowerLevels::from(power_levels_content.clone())
.user_can_change_user_power_level(user_id, user_id)
}) || services
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")
.await
.is_ok_and(|event| event.sender == user_id);
if user_can_demote_self {
let mut power_levels_content = room_power_levels.unwrap_or_default();

View file

@ -2,11 +2,12 @@ use axum::extract::State;
use conduwuit::{err, Err};
use ruma::{
api::client::config::{
get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data,
get_global_account_data, get_room_account_data, set_global_account_data,
set_room_account_data,
},
events::{
AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent, GlobalAccountDataEventType,
RoomAccountDataEventType,
AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent,
GlobalAccountDataEventType, RoomAccountDataEventType,
},
serde::Raw,
RoomId, UserId,
@ -20,7 +21,8 @@ use crate::{service::Services, Result, Ruma};
///
/// Sets some account data for the sender user.
pub(crate) async fn set_global_account_data_route(
State(services): State<crate::State>, body: Ruma<set_global_account_data::v3::Request>,
State(services): State<crate::State>,
body: Ruma<set_global_account_data::v3::Request>,
) -> Result<set_global_account_data::v3::Response> {
let sender_user = body.sender_user();
@ -28,7 +30,14 @@ pub(crate) async fn set_global_account_data_route(
return Err!(Request(Forbidden("You cannot set account data for other users.")));
}
set_account_data(&services, None, &body.user_id, &body.event_type.to_string(), body.data.json()).await?;
set_account_data(
&services,
None,
&body.user_id,
&body.event_type.to_string(),
body.data.json(),
)
.await?;
Ok(set_global_account_data::v3::Response {})
}
@ -37,7 +46,8 @@ pub(crate) async fn set_global_account_data_route(
///
/// Sets some room account data for the sender user.
pub(crate) async fn set_room_account_data_route(
State(services): State<crate::State>, body: Ruma<set_room_account_data::v3::Request>,
State(services): State<crate::State>,
body: Ruma<set_room_account_data::v3::Request>,
) -> Result<set_room_account_data::v3::Response> {
let sender_user = body.sender_user();
@ -61,7 +71,8 @@ pub(crate) async fn set_room_account_data_route(
///
/// Gets some account data for the sender user.
pub(crate) async fn get_global_account_data_route(
State(services): State<crate::State>, body: Ruma<get_global_account_data::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_global_account_data::v3::Request>,
) -> Result<get_global_account_data::v3::Response> {
let sender_user = body.sender_user();
@ -75,16 +86,15 @@ pub(crate) async fn get_global_account_data_route(
.await
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
Ok(get_global_account_data::v3::Response {
account_data: account_data.content,
})
Ok(get_global_account_data::v3::Response { account_data: account_data.content })
}
/// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
///
/// Gets some room account data for the sender user.
pub(crate) async fn get_room_account_data_route(
State(services): State<crate::State>, body: Ruma<get_room_account_data::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_room_account_data::v3::Request>,
) -> Result<get_room_account_data::v3::Response> {
let sender_user = body.sender_user();
@ -98,17 +108,20 @@ pub(crate) async fn get_room_account_data_route(
.await
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
Ok(get_room_account_data::v3::Response {
account_data: account_data.content,
})
Ok(get_room_account_data::v3::Response { account_data: account_data.content })
}
async fn set_account_data(
services: &Services, room_id: Option<&RoomId>, sender_user: &UserId, event_type_s: &str, data: &RawJsonValue,
services: &Services,
room_id: Option<&RoomId>,
sender_user: &UserId,
event_type_s: &str,
data: &RawJsonValue,
) -> Result {
if event_type_s == RoomAccountDataEventType::FullyRead.to_cow_str() {
return Err!(Request(BadJson(
"This endpoint cannot be used for marking a room as fully read (setting m.fully_read)"
"This endpoint cannot be used for marking a room as fully read (setting \
m.fully_read)"
)));
}
@ -118,8 +131,8 @@ async fn set_account_data(
)));
}
let data: serde_json::Value =
serde_json::from_str(data.get()).map_err(|e| err!(Request(BadJson(warn!("Invalid JSON provided: {e}")))))?;
let data: serde_json::Value = serde_json::from_str(data.get())
.map_err(|e| err!(Request(BadJson(warn!("Invalid JSON provided: {e}")))))?;
services
.account_data

View file

@ -14,7 +14,8 @@ use crate::Ruma;
///
/// Creates a new room alias on this server.
pub(crate) async fn create_alias_route(
State(services): State<crate::State>, body: Ruma<create_alias::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_alias::v3::Request>,
) -> Result<create_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -58,7 +59,8 @@ pub(crate) async fn create_alias_route(
///
/// - TODO: Update canonical alias event
pub(crate) async fn delete_alias_route(
State(services): State<crate::State>, body: Ruma<delete_alias::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_alias::v3::Request>,
) -> Result<delete_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -83,11 +85,13 @@ pub(crate) async fn delete_alias_route(
///
/// Resolve an alias locally or over federation.
pub(crate) async fn get_alias_route(
State(services): State<crate::State>, body: Ruma<get_alias::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_alias::v3::Request>,
) -> Result<get_alias::v3::Response> {
let room_alias = body.body.room_alias;
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias, None).await else {
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias, None).await
else {
return Err!(Request(NotFound("Room with alias not found.")));
};
@ -98,7 +102,10 @@ pub(crate) async fn get_alias_route(
}
async fn room_available_servers(
services: &Services, room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: Vec<OwnedServerName>,
services: &Services,
room_id: &RoomId,
room_alias: &RoomAliasId,
pre_servers: Vec<OwnedServerName>,
) -> Vec<OwnedServerName> {
// find active servers in room state cache to suggest
let mut servers: Vec<OwnedServerName> = services

View file

@ -9,12 +9,12 @@ use crate::Ruma;
/// Ask the homeserver to ping the application service to ensure the connection
/// works.
pub(crate) async fn appservice_ping(
State(services): State<crate::State>, body: Ruma<request_ping::v1::Request>,
State(services): State<crate::State>,
body: Ruma<request_ping::v1::Request>,
) -> Result<request_ping::v1::Response> {
let appservice_info = body
.appservice_info
.as_ref()
.ok_or_else(|| err!(Request(Forbidden("This endpoint can only be called by appservices."))))?;
let appservice_info = body.appservice_info.as_ref().ok_or_else(|| {
err!(Request(Forbidden("This endpoint can only be called by appservices.")))
})?;
if body.appservice_id != appservice_info.registration.id {
return Err!(Request(Forbidden(
@ -41,7 +41,5 @@ pub(crate) async fn appservice_ping(
.await?
.expect("We already validated if an appservice URL exists above");
Ok(request_ping::v1::Response {
duration: timer.elapsed(),
})
Ok(request_ping::v1::Response { duration: timer.elapsed() })
}

View file

@ -2,10 +2,11 @@ use axum::extract::State;
use conduwuit::{err, Err};
use ruma::{
api::client::backup::{
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
get_latest_backup_info, update_backup_version,
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session,
create_backup_version, delete_backup_keys, delete_backup_keys_for_room,
delete_backup_keys_for_session, delete_backup_version, get_backup_info, get_backup_keys,
get_backup_keys_for_room, get_backup_keys_for_session, get_latest_backup_info,
update_backup_version,
},
UInt,
};
@ -16,15 +17,14 @@ use crate::{Result, Ruma};
///
/// Creates a new backup.
pub(crate) async fn create_backup_version_route(
State(services): State<crate::State>, body: Ruma<create_backup_version::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_backup_version::v3::Request>,
) -> Result<create_backup_version::v3::Response> {
let version = services
.key_backups
.create_backup(body.sender_user(), &body.algorithm)?;
Ok(create_backup_version::v3::Response {
version,
})
Ok(create_backup_version::v3::Response { version })
}
/// # `PUT /_matrix/client/r0/room_keys/version/{version}`
@ -32,7 +32,8 @@ pub(crate) async fn create_backup_version_route(
/// Update information about an existing backup. Only `auth_data` can be
/// modified.
pub(crate) async fn update_backup_version_route(
State(services): State<crate::State>, body: Ruma<update_backup_version::v3::Request>,
State(services): State<crate::State>,
body: Ruma<update_backup_version::v3::Request>,
) -> Result<update_backup_version::v3::Response> {
services
.key_backups
@ -46,7 +47,8 @@ pub(crate) async fn update_backup_version_route(
///
/// Get information about the latest backup version.
pub(crate) async fn get_latest_backup_info_route(
State(services): State<crate::State>, body: Ruma<get_latest_backup_info::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_latest_backup_info::v3::Request>,
) -> Result<get_latest_backup_info::v3::Response> {
let (version, algorithm) = services
.key_backups
@ -75,13 +77,16 @@ pub(crate) async fn get_latest_backup_info_route(
///
/// Get information about an existing backup.
pub(crate) async fn get_backup_info_route(
State(services): State<crate::State>, body: Ruma<get_backup_info::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_backup_info::v3::Request>,
) -> Result<get_backup_info::v3::Response> {
let algorithm = services
.key_backups
.get_backup(body.sender_user(), &body.version)
.await
.map_err(|_| err!(Request(NotFound("Key backup does not exist at version {:?}", body.version))))?;
.map_err(|_| {
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
})?;
Ok(get_backup_info::v3::Response {
algorithm,
@ -105,7 +110,8 @@ pub(crate) async fn get_backup_info_route(
/// - Deletes both information about the backup, as well as all key data related
/// to the backup
pub(crate) async fn delete_backup_version_route(
State(services): State<crate::State>, body: Ruma<delete_backup_version::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_backup_version::v3::Request>,
) -> Result<delete_backup_version::v3::Response> {
services
.key_backups
@ -124,7 +130,8 @@ pub(crate) 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(crate) async fn add_backup_keys_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<add_backup_keys::v3::Request>,
) -> Result<add_backup_keys::v3::Response> {
if services
.key_backups
@ -168,7 +175,8 @@ pub(crate) 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(crate) async fn add_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<add_backup_keys_for_room::v3::Request>,
) -> Result<add_backup_keys_for_room::v3::Response> {
if services
.key_backups
@ -210,7 +218,8 @@ pub(crate) 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(crate) async fn add_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_session::v3::Request>,
State(services): State<crate::State>,
body: Ruma<add_backup_keys_for_session::v3::Request>,
) -> Result<add_backup_keys_for_session::v3::Response> {
if services
.key_backups
@ -251,56 +260,56 @@ pub(crate) async fn add_backup_keys_for_session_route(
///
/// Retrieves all keys from the backup.
pub(crate) async fn get_backup_keys_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_backup_keys::v3::Request>,
) -> Result<get_backup_keys::v3::Response> {
let rooms = services
.key_backups
.get_all(body.sender_user(), &body.version)
.await;
Ok(get_backup_keys::v3::Response {
rooms,
})
Ok(get_backup_keys::v3::Response { rooms })
}
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
///
/// Retrieves all keys from the backup for a given room.
pub(crate) async fn get_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_backup_keys_for_room::v3::Request>,
) -> Result<get_backup_keys_for_room::v3::Response> {
let sessions = services
.key_backups
.get_room(body.sender_user(), &body.version, &body.room_id)
.await;
Ok(get_backup_keys_for_room::v3::Response {
sessions,
})
Ok(get_backup_keys_for_room::v3::Response { sessions })
}
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
///
/// Retrieves a key from the backup.
pub(crate) async fn get_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_session::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_backup_keys_for_session::v3::Request>,
) -> Result<get_backup_keys_for_session::v3::Response> {
let key_data = services
.key_backups
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
.await
.map_err(|_| err!(Request(NotFound(debug_error!("Backup key not found for this user's session.")))))?;
.map_err(|_| {
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
})?;
Ok(get_backup_keys_for_session::v3::Response {
key_data,
})
Ok(get_backup_keys_for_session::v3::Response { key_data })
}
/// # `DELETE /_matrix/client/r0/room_keys/keys`
///
/// Delete the keys from the backup.
pub(crate) async fn delete_backup_keys_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_backup_keys::v3::Request>,
) -> Result<delete_backup_keys::v3::Response> {
services
.key_backups
@ -324,7 +333,8 @@ pub(crate) async fn delete_backup_keys_route(
///
/// Delete the keys from the backup for a given room.
pub(crate) async fn delete_backup_keys_for_room_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_backup_keys_for_room::v3::Request>,
) -> Result<delete_backup_keys_for_room::v3::Response> {
services
.key_backups
@ -348,7 +358,8 @@ pub(crate) async fn delete_backup_keys_for_room_route(
///
/// Delete a key from the backup.
pub(crate) async fn delete_backup_keys_for_session_route(
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_session::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_backup_keys_for_session::v3::Request>,
) -> Result<delete_backup_keys_for_session::v3::Response> {
services
.key_backups

View file

@ -3,8 +3,8 @@ use std::collections::BTreeMap;
use axum::extract::State;
use ruma::{
api::client::discovery::get_capabilities::{
self, Capabilities, GetLoginTokenCapability, RoomVersionStability, RoomVersionsCapability,
ThirdPartyIdChangesCapability,
self, Capabilities, GetLoginTokenCapability, RoomVersionStability,
RoomVersionsCapability, ThirdPartyIdChangesCapability,
},
RoomVersionId,
};
@ -17,9 +17,11 @@ use crate::{Result, Ruma};
/// Get information on the supported feature set and other relevent capabilities
/// of this server.
pub(crate) async fn get_capabilities_route(
State(services): State<crate::State>, _body: Ruma<get_capabilities::v3::Request>,
State(services): State<crate::State>,
_body: Ruma<get_capabilities::v3::Request>,
) -> Result<get_capabilities::v3::Response> {
let available: BTreeMap<RoomVersionId, RoomVersionStability> = services.server.available_room_versions().collect();
let available: BTreeMap<RoomVersionId, RoomVersionStability> =
services.server.available_room_versions().collect();
let mut capabilities = Capabilities::default();
capabilities.room_versions = RoomVersionsCapability {
@ -28,21 +30,15 @@ pub(crate) async fn get_capabilities_route(
};
// we do not implement 3PID stuff
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability {
enabled: false,
};
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability { enabled: false };
// we dont support generating tokens yet
capabilities.get_login_token = GetLoginTokenCapability {
enabled: false,
};
capabilities.get_login_token = GetLoginTokenCapability { enabled: false };
// MSC4133 capability
capabilities
.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))
.expect("this is valid JSON we created");
Ok(get_capabilities::v3::Response {
capabilities,
})
Ok(get_capabilities::v3::Response { capabilities })
}

View file

@ -32,7 +32,8 @@ const LIMIT_DEFAULT: usize = 10;
/// - Only works if the user is joined (TODO: always allow, but only show events
/// if the user was joined, depending on history_visibility)
pub(crate) async fn get_context_route(
State(services): State<crate::State>, body: Ruma<get_context::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_context::v3::Request>,
) -> Result<get_context::v3::Response> {
let filter = &body.filter;
let sender = body.sender();
@ -50,9 +51,8 @@ pub(crate) async fn get_context_route(
// members for "inline" profiles on the timeline to work properly
let lazy_load_enabled = matches!(filter.lazy_load_options, LazyLoadOptions::Enabled { .. });
let lazy_load_redundant = if let LazyLoadOptions::Enabled {
include_redundant_members,
} = filter.lazy_load_options
let lazy_load_redundant = if let LazyLoadOptions::Enabled { include_redundant_members } =
filter.lazy_load_options
{
include_redundant_members
} else {
@ -91,10 +91,11 @@ pub(crate) async fn get_context_route(
return Err!(Request(Forbidden("You don't have permission to view this event.")));
}
let events_before = services
.rooms
.timeline
.pdus_rev(Some(sender_user), room_id, Some(base_token));
let events_before =
services
.rooms
.timeline
.pdus_rev(Some(sender_user), room_id, Some(base_token));
let events_after = services
.rooms
@ -166,7 +167,9 @@ pub(crate) async fn get_context_route(
.filter(|&user_id: &&UserId| lazy.contains(user_id))
.map(|_| event_id)
})
.broad_filter_map(|event_id: &OwnedEventId| services.rooms.timeline.get_pdu(event_id).ok())
.broad_filter_map(|event_id: &OwnedEventId| {
services.rooms.timeline.get_pdu(event_id).ok()
})
.map(|pdu| pdu.to_state_event())
.collect()
.await;

View file

@ -18,7 +18,8 @@ use crate::{utils, Error, Result, Ruma};
///
/// Get metadata on all devices of the sender user.
pub(crate) async fn get_devices_route(
State(services): State<crate::State>, body: Ruma<get_devices::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_devices::v3::Request>,
) -> Result<get_devices::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -28,16 +29,15 @@ pub(crate) async fn get_devices_route(
.collect()
.await;
Ok(get_devices::v3::Response {
devices,
})
Ok(get_devices::v3::Response { devices })
}
/// # `GET /_matrix/client/r0/devices/{deviceId}`
///
/// Get metadata on a single device of the sender user.
pub(crate) async fn get_device_route(
State(services): State<crate::State>, body: Ruma<get_device::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_device::v3::Request>,
) -> Result<get_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -47,9 +47,7 @@ pub(crate) async fn get_device_route(
.await
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
Ok(get_device::v3::Response {
device,
})
Ok(get_device::v3::Response { device })
}
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
@ -57,7 +55,8 @@ pub(crate) async fn get_device_route(
/// Updates the metadata on a given device of the sender user.
#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
pub(crate) async fn update_device_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<update_device::v3::Request>,
) -> Result<update_device::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -93,16 +92,15 @@ pub(crate) async fn update_device_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub(crate) async fn delete_device_route(
State(services): State<crate::State>, body: Ruma<delete_device::v3::Request>,
State(services): State<crate::State>,
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");
// UIAA
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Password],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
completed: Vec::new(),
params: Box::default(),
session: None,
@ -151,16 +149,15 @@ pub(crate) async fn delete_device_route(
/// - Forgets to-device events
/// - Triggers device list updates
pub(crate) async fn delete_devices_route(
State(services): State<crate::State>, body: Ruma<delete_devices::v3::Request>,
State(services): State<crate::State>,
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");
// UIAA
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Password],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
completed: Vec::new(),
params: Box::default(),
session: None,

View file

@ -5,7 +5,10 @@ use futures::{StreamExt, TryFutureExt};
use ruma::{
api::{
client::{
directory::{get_public_rooms, get_public_rooms_filtered, get_room_visibility, set_room_visibility},
directory::{
get_public_rooms, get_public_rooms_filtered, get_room_visibility,
set_room_visibility,
},
error::ErrorKind,
room,
},
@ -32,7 +35,8 @@ use crate::Ruma;
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
pub(crate) async fn get_public_rooms_filtered_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_public_rooms_filtered::v3::Request>,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(server) = &body.server {
@ -57,7 +61,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
.await
.map_err(|e| {
warn!(?body.server, "Failed to return /publicRooms: {e}");
Error::BadRequest(ErrorKind::Unknown, "Failed to return the requested server's public room list.")
Error::BadRequest(
ErrorKind::Unknown,
"Failed to return the requested server's public room list.",
)
})?;
Ok(response)
@ -70,7 +77,8 @@ pub(crate) async fn get_public_rooms_filtered_route(
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
pub(crate) async fn get_public_rooms_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_public_rooms::v3::Request>,
) -> Result<get_public_rooms::v3::Response> {
if let Some(server) = &body.server {
@ -95,7 +103,10 @@ pub(crate) async fn get_public_rooms_route(
.await
.map_err(|e| {
warn!(?body.server, "Failed to return /publicRooms: {e}");
Error::BadRequest(ErrorKind::Unknown, "Failed to return the requested server's public room list.")
Error::BadRequest(
ErrorKind::Unknown,
"Failed to return the requested server's public room list.",
)
})?;
Ok(get_public_rooms::v3::Response {
@ -111,7 +122,8 @@ pub(crate) async fn get_public_rooms_route(
/// Sets the visibility of a given room in the room directory.
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
pub(crate) async fn set_room_visibility_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
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");
@ -139,14 +151,14 @@ pub(crate) async fn set_room_visibility_route(
}
match &body.visibility {
room::Visibility::Public => {
| room::Visibility::Public => {
if services.globals.config.lockdown_public_room_directory
&& !services.users.is_admin(sender_user).await
&& body.appservice_info.is_none()
{
info!(
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
\"lockdown_public_room_directory\" is enabled",
"Non-admin user {sender_user} tried to publish {0} to the room directory \
while \"lockdown_public_room_directory\" is enabled",
body.room_id
);
@ -154,8 +166,8 @@ pub(crate) async fn set_room_visibility_route(
services
.admin
.send_text(&format!(
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
\"lockdown_public_room_directory\" is enabled",
"Non-admin user {sender_user} tried to publish {0} to the room \
directory while \"lockdown_public_room_directory\" is enabled",
body.room_id
))
.await;
@ -172,13 +184,16 @@ pub(crate) async fn set_room_visibility_route(
if services.globals.config.admin_room_notices {
services
.admin
.send_text(&format!("{sender_user} made {} public to the room directory", body.room_id))
.send_text(&format!(
"{sender_user} made {} public to the room directory",
body.room_id
))
.await;
}
info!("{sender_user} made {0} public to the room directory", body.room_id);
},
room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id),
_ => {
| room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id),
| _ => {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Room visibility type is not supported.",
@ -193,7 +208,8 @@ pub(crate) async fn set_room_visibility_route(
///
/// Gets the visibility of a given room in the room directory.
pub(crate) async fn get_room_visibility_route(
State(services): State<crate::State>, body: Ruma<get_room_visibility::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_room_visibility::v3::Request>,
) -> Result<get_room_visibility::v3::Response> {
if !services.rooms.metadata.exists(&body.room_id).await {
// Return 404 if the room doesn't exist
@ -210,10 +226,16 @@ pub(crate) async fn get_room_visibility_route(
}
pub(crate) async fn get_public_rooms_filtered_helper(
services: &Services, server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter,
services: &Services,
server: Option<&ServerName>,
limit: Option<UInt>,
since: Option<&str>,
filter: &Filter,
_network: &RoomNetwork,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(other_server) = server.filter(|server_name| !services.globals.server_is_ours(server_name)) {
if let Some(other_server) =
server.filter(|server_name| !services.globals.server_is_ours(server_name))
{
let response = services
.sending
.send_federation_request(
@ -245,9 +267,10 @@ pub(crate) async fn get_public_rooms_filtered_helper(
if let Some(s) = &since {
let mut characters = s.chars();
let backwards = match characters.next() {
Some('n') => false,
Some('p') => true,
_ => return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token")),
| Some('n') => false,
| Some('p') => true,
| _ =>
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token")),
};
num_since = characters
@ -337,7 +360,11 @@ pub(crate) async fn get_public_rooms_filtered_helper(
/// Check whether the user can publish to the room directory via power levels of
/// room history visibility event or room creator
async fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
async fn user_can_publish_room(
services: &Services,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
if let Ok(event) = services
.rooms
.state_accessor
@ -347,7 +374,8 @@ async fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &
serde_json::from_str(event.content.get())
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
.map(|content: RoomPowerLevelsEventContent| {
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
RoomPowerLevels::from(content)
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
})
} else if let Ok(event) = services
.rooms
@ -406,10 +434,10 @@ async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> Public
.state_accessor
.room_state_get_content(&room_id, &StateEventType::RoomJoinRules, "")
.map_ok(|c: RoomJoinRulesEventContent| match c.join_rule {
JoinRule::Public => PublicRoomJoinRule::Public,
JoinRule::Knock => "knock".into(),
JoinRule::KnockRestricted(_) => "knock_restricted".into(),
_ => "invite".into(),
| JoinRule::Public => PublicRoomJoinRule::Public,
| JoinRule::Knock => "knock".into(),
| JoinRule::KnockRestricted(_) => "knock_restricted".into(),
| _ => "invite".into(),
})
.await
.unwrap_or_default(),

View file

@ -10,7 +10,8 @@ use crate::{Result, Ruma};
///
/// - A user can only access their own filters
pub(crate) async fn get_filter_route(
State(services): State<crate::State>, body: Ruma<get_filter::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_filter::v3::Request>,
) -> Result<get_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -26,7 +27,8 @@ pub(crate) async fn get_filter_route(
///
/// Creates a new filter to be used by other endpoints.
pub(crate) async fn create_filter_route(
State(services): State<crate::State>, body: Ruma<create_filter::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_filter::v3::Request>,
) -> Result<create_filter::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -7,7 +7,10 @@ use ruma::{
api::{
client::{
error::ErrorKind,
keys::{claim_keys, get_key_changes, get_keys, upload_keys, upload_signatures, upload_signing_keys},
keys::{
claim_keys, get_key_changes, get_keys, upload_keys, upload_signatures,
upload_signing_keys,
},
uiaa::{AuthFlow, AuthType, UiaaInfo},
},
federation,
@ -31,7 +34,8 @@ use crate::{
/// - If there are no device keys yet: Adds device keys (TODO: merge with
/// existing keys?)
pub(crate) async fn upload_keys_route(
State(services): State<crate::State>, body: Ruma<upload_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<upload_keys::v3::Request>,
) -> Result<upload_keys::v3::Response> {
let (sender_user, sender_device) = body.sender();
@ -75,7 +79,8 @@ pub(crate) async fn upload_keys_route(
/// - The master and self-signing keys contain signatures that the user is
/// allowed to see
pub(crate) async fn get_keys_route(
State(services): State<crate::State>, body: Ruma<get_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_keys::v3::Request>,
) -> Result<get_keys::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -93,7 +98,8 @@ pub(crate) async fn get_keys_route(
///
/// Claims one-time keys
pub(crate) async fn claim_keys_route(
State(services): State<crate::State>, body: Ruma<claim_keys::v3::Request>,
State(services): State<crate::State>,
body: Ruma<claim_keys::v3::Request>,
) -> Result<claim_keys::v3::Response> {
claim_keys_helper(&services, &body.one_time_keys).await
}
@ -104,16 +110,15 @@ pub(crate) async fn claim_keys_route(
///
/// - Requires UIAA to verify password
pub(crate) async fn upload_signing_keys_route(
State(services): State<crate::State>, body: Ruma<upload_signing_keys::v3::Request>,
State(services): State<crate::State>,
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");
// UIAA
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec![AuthType::Password],
}],
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
completed: Vec::new(),
params: Box::default(),
session: None,
@ -161,7 +166,8 @@ pub(crate) async fn upload_signing_keys_route(
///
/// Uploads end-to-end key signatures from the sender user.
pub(crate) async fn upload_signatures_route(
State(services): State<crate::State>, body: Ruma<upload_signatures::v3::Request>,
State(services): State<crate::State>,
body: Ruma<upload_signatures::v3::Request>,
) -> Result<upload_signatures::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -174,7 +180,10 @@ pub(crate) async fn upload_signatures_route(
.get("signatures")
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Missing signatures field."))?
.get(sender_user.to_string())
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid user in signatures field."))?
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid user in signatures field.",
))?
.as_object()
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature."))?
.clone()
@ -185,7 +194,10 @@ pub(crate) async fn upload_signatures_route(
signature
.1
.as_str()
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid signature value.",
))?
.to_owned(),
);
@ -209,7 +221,8 @@ pub(crate) async fn upload_signatures_route(
///
/// - TODO: left users
pub(crate) async fn get_key_changes_route(
State(services): State<crate::State>, body: Ruma<get_key_changes::v3::Request>,
State(services): State<crate::State>,
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");
@ -255,8 +268,11 @@ pub(crate) async fn get_key_changes_route(
}
pub(crate) async fn get_keys_helper<F>(
services: &Services, sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
allowed_signatures: F, include_display_names: bool,
services: &Services,
sender_user: Option<&UserId>,
device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
allowed_signatures: F,
include_display_names: bool,
) -> Result<get_keys::v3::Response>
where
F: Fn(&UserId) -> bool + Send + Sync,
@ -289,7 +305,9 @@ where
.users
.get_device_metadata(user_id, device_id)
.await
.map_err(|_| err!(Database("all_device_keys contained nonexistent device.")))?;
.map_err(|_| {
err!(Database("all_device_keys contained nonexistent device."))
})?;
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
.map_err(|_| err!(Database("invalid device keys in database")))?;
@ -307,7 +325,11 @@ where
.users
.get_device_metadata(user_id, device_id)
.await
.map_err(|_| err!(Request(InvalidParam("Tried to get keys for nonexistent device."))))?;
.map_err(|_| {
err!(Request(InvalidParam(
"Tried to get keys for nonexistent device."
)))
})?;
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
.map_err(|_| err!(Database("invalid device keys in database")))?;
@ -350,9 +372,8 @@ where
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
}
let request = federation::keys::get_keys::v1::Request {
device_keys: device_keys_input_fed,
};
let request =
federation::keys::get_keys::v1::Request { device_keys: device_keys_input_fed };
let response = services
.sending
@ -382,8 +403,8 @@ where
.users
.add_cross_signing_keys(
&user, &raw, &None, &None,
false, /* Dont notify. A notification would trigger another key request resulting in an
* endless loop */
false, /* Dont notify. A notification would trigger another key request
* resulting in an endless loop */
)
.await?;
master_keys.insert(user.clone(), raw);
@ -406,7 +427,8 @@ where
}
fn add_unsigned_device_display_name(
keys: &mut Raw<ruma::encryption::DeviceKeys>, metadata: ruma::api::client::device::Device,
keys: &mut Raw<ruma::encryption::DeviceKeys>,
metadata: ruma::api::client::device::Device,
include_display_names: bool,
) -> serde_json::Result<()> {
if let Some(display_name) = metadata.display_name {
@ -431,7 +453,8 @@ fn add_unsigned_device_display_name(
}
pub(crate) async fn claim_keys_helper(
services: &Services, one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, OneTimeKeyAlgorithm>>,
services: &Services,
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, OneTimeKeyAlgorithm>>,
) -> Result<claim_keys::v3::Response> {
let mut one_time_keys = BTreeMap::new();
@ -473,12 +496,9 @@ pub(crate) async fn claim_keys_helper(
server,
services
.sending
.send_federation_request(
server,
federation::keys::claim_keys::v1::Request {
one_time_keys: one_time_keys_input_fed,
},
)
.send_federation_request(server, federation::keys::claim_keys::v1::Request {
one_time_keys: one_time_keys_input_fed,
})
.await,
)
})
@ -486,17 +506,14 @@ pub(crate) async fn claim_keys_helper(
while let Some((server, response)) = futures.next().await {
match response {
Ok(keys) => {
| Ok(keys) => {
one_time_keys.extend(keys.one_time_keys);
},
Err(_e) => {
| Err(_e) => {
failures.insert(server.to_string(), json!({}));
},
}
}
Ok(claim_keys::v3::Response {
failures,
one_time_keys,
})
Ok(claim_keys::v3::Response { failures, one_time_keys })
}

View file

@ -15,7 +15,8 @@ use reqwest::Url;
use ruma::{
api::client::{
authenticated_media::{
get_content, get_content_as_filename, get_content_thumbnail, get_media_config, get_media_preview,
get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
get_media_preview,
},
media::create_content,
},
@ -26,7 +27,8 @@ use crate::Ruma;
/// # `GET /_matrix/client/v1/media/config`
pub(crate) async fn get_media_config_route(
State(services): State<crate::State>, _body: Ruma<get_media_config::v1::Request>,
State(services): State<crate::State>,
_body: Ruma<get_media_config::v1::Request>,
) -> Result<get_media_config::v1::Response> {
Ok(get_media_config::v1::Response {
upload_size: ruma_from_usize(services.globals.config.max_request_size),
@ -46,7 +48,8 @@ pub(crate) async fn get_media_config_route(
fields(%client),
)]
pub(crate) async fn create_content_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let user = body.sender_user.as_ref().expect("user is authenticated");
@ -79,7 +82,8 @@ pub(crate) async fn create_content_route(
fields(%client),
)]
pub(crate) async fn get_content_thumbnail_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v1::Request>,
) -> Result<get_content_thumbnail::v1::Response> {
let user = body.sender_user.as_ref().expect("user is authenticated");
@ -115,7 +119,8 @@ pub(crate) async fn get_content_thumbnail_route(
fields(%client),
)]
pub(crate) async fn get_content_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v1::Request>,
) -> Result<get_content::v1::Response> {
let user = body.sender_user.as_ref().expect("user is authenticated");
@ -150,7 +155,8 @@ pub(crate) async fn get_content_route(
fields(%client),
)]
pub(crate) async fn get_content_as_filename_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v1::Request>,
) -> Result<get_content_as_filename::v1::Response> {
let user = body.sender_user.as_ref().expect("user is authenticated");
@ -185,7 +191,8 @@ pub(crate) async fn get_content_as_filename_route(
fields(%client),
)]
pub(crate) async fn get_media_preview_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_media_preview::v1::Request>,
) -> Result<get_media_preview::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -223,7 +230,11 @@ pub(crate) async fn get_media_preview_route(
}
async fn fetch_thumbnail(
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, dim: &Dim,
services: &Services,
mxc: &Mxc<'_>,
user: &UserId,
timeout_ms: Duration,
dim: &Dim,
) -> Result<FileMeta> {
let FileMeta {
content,
@ -245,7 +256,11 @@ async fn fetch_thumbnail(
}
async fn fetch_file(
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, filename: Option<&str>,
services: &Services,
mxc: &Mxc<'_>,
user: &UserId,
timeout_ms: Duration,
filename: Option<&str>,
) -> Result<FileMeta> {
let FileMeta {
content,
@ -267,7 +282,11 @@ async fn fetch_file(
}
async fn fetch_thumbnail_meta(
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, dim: &Dim,
services: &Services,
mxc: &Mxc<'_>,
user: &UserId,
timeout_ms: Duration,
dim: &Dim,
) -> Result<FileMeta> {
if let Some(filemeta) = services.media.get_thumbnail(mxc, dim).await? {
return Ok(filemeta);
@ -283,7 +302,12 @@ async fn fetch_thumbnail_meta(
.await
}
async fn fetch_file_meta(services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration) -> Result<FileMeta> {
async fn fetch_file_meta(
services: &Services,
mxc: &Mxc<'_>,
user: &UserId,
timeout_ms: Duration,
) -> Result<FileMeta> {
if let Some(filemeta) = services.media.get(mxc).await? {
return Ok(filemeta);
}

View file

@ -11,8 +11,8 @@ use conduwuit_service::media::{Dim, FileMeta, CACHE_CONTROL_IMMUTABLE, CORP_CROS
use reqwest::Url;
use ruma::{
api::client::media::{
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
get_media_preview,
create_content, get_content, get_content_as_filename, get_content_thumbnail,
get_media_config, get_media_preview,
},
Mxc,
};
@ -23,7 +23,8 @@ use crate::{client::create_content_route, Ruma, RumaResponse};
///
/// Returns max upload size.
pub(crate) async fn get_media_config_legacy_route(
State(services): State<crate::State>, _body: Ruma<get_media_config::v3::Request>,
State(services): State<crate::State>,
_body: Ruma<get_media_config::v3::Request>,
) -> Result<get_media_config::v3::Response> {
Ok(get_media_config::v3::Response {
upload_size: ruma_from_usize(services.globals.config.max_request_size),
@ -38,7 +39,8 @@ pub(crate) async fn get_media_config_legacy_route(
///
/// Returns max upload size.
pub(crate) async fn get_media_config_legacy_legacy_route(
State(services): State<crate::State>, body: Ruma<get_media_config::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_media_config::v3::Request>,
) -> Result<RumaResponse<get_media_config::v3::Response>> {
get_media_config_legacy_route(State(services), body)
.await
@ -50,7 +52,8 @@ pub(crate) async fn get_media_config_legacy_legacy_route(
/// Returns URL preview.
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy")]
pub(crate) async fn get_media_preview_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_media_preview::v3::Request>,
) -> Result<get_media_preview::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -91,7 +94,8 @@ pub(crate) async fn get_media_preview_legacy_route(
///
/// Returns URL preview.
pub(crate) async fn get_media_preview_legacy_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_media_preview::v3::Request>,
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
get_media_preview_legacy_route(State(services), InsecureClientIp(client), body)
@ -110,7 +114,8 @@ pub(crate) async fn get_media_preview_legacy_legacy_route(
/// - Some metadata will be saved in the database
/// - Media will be saved in the media/ directory
pub(crate) async fn create_content_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<create_content::v3::Request>,
) -> Result<RumaResponse<create_content::v3::Response>> {
create_content_route(State(services), InsecureClientIp(client), body)
@ -128,7 +133,8 @@ pub(crate) async fn create_content_legacy_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
pub(crate) async fn get_content_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v3::Request>,
) -> Result<get_content::v3::Response> {
let mxc = Mxc {
@ -142,7 +148,8 @@ pub(crate) async fn get_content_legacy_route(
content_disposition,
}) = services.media.get(&mxc).await?
{
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
let content_disposition =
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
Ok(get_content::v3::Response {
file: content.expect("entire file contents"),
@ -156,10 +163,15 @@ pub(crate) async fn get_content_legacy_route(
.media
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
.await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
.map_err(|e| {
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
})?;
let content_disposition =
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
let content_disposition = make_content_disposition(
response.content_disposition.as_ref(),
response.content_type.as_deref(),
None,
);
Ok(get_content::v3::Response {
file: response.file,
@ -187,7 +199,8 @@ pub(crate) async fn get_content_legacy_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
pub(crate) async fn get_content_legacy_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content::v3::Request>,
) -> Result<RumaResponse<get_content::v3::Response>> {
get_content_legacy_route(State(services), InsecureClientIp(client), body)
@ -205,7 +218,8 @@ pub(crate) async fn get_content_legacy_legacy_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
pub(crate) async fn get_content_as_filename_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<get_content_as_filename::v3::Response> {
let mxc = Mxc {
@ -219,8 +233,11 @@ pub(crate) async fn get_content_as_filename_legacy_route(
content_disposition,
}) = services.media.get(&mxc).await?
{
let content_disposition =
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), Some(&body.filename));
let content_disposition = make_content_disposition(
content_disposition.as_ref(),
content_type.as_deref(),
Some(&body.filename),
);
Ok(get_content_as_filename::v3::Response {
file: content.expect("entire file contents"),
@ -234,10 +251,15 @@ pub(crate) async fn get_content_as_filename_legacy_route(
.media
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
.await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
.map_err(|e| {
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
})?;
let content_disposition =
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
let content_disposition = make_content_disposition(
response.content_disposition.as_ref(),
response.content_type.as_deref(),
None,
);
Ok(get_content_as_filename::v3::Response {
content_disposition: Some(content_disposition),
@ -264,7 +286,8 @@ pub(crate) async fn get_content_as_filename_legacy_route(
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
get_content_as_filename_legacy_route(State(services), InsecureClientIp(client), body)
@ -282,7 +305,8 @@ pub(crate) async fn get_content_as_filename_legacy_legacy_route(
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy")]
pub(crate) async fn get_content_thumbnail_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<get_content_thumbnail::v3::Response> {
let mxc = Mxc {
@ -297,7 +321,8 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
content_disposition,
}) = services.media.get_thumbnail(&mxc, &dim).await?
{
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
let content_disposition =
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
Ok(get_content_thumbnail::v3::Response {
file: content.expect("entire file contents"),
@ -311,10 +336,15 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
.media
.fetch_remote_thumbnail_legacy(&body)
.await
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
.map_err(|e| {
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
})?;
let content_disposition =
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
let content_disposition = make_content_disposition(
response.content_disposition.as_ref(),
response.content_type.as_deref(),
None,
);
Ok(get_content_thumbnail::v3::Response {
file: response.file,
@ -341,7 +371,8 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
get_content_thumbnail_legacy_route(State(services), InsecureClientIp(client), body)

View file

@ -20,7 +20,8 @@ use ruma::{
client::{
error::ErrorKind,
membership::{
ban_user, forget_room, get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias,
ban_user, forget_room, get_member_events, invite_user, join_room_by_id,
join_room_by_id_or_alias,
joined_members::{self, v3::RoomMember},
joined_rooms, kick_user, leave_room, unban_user, ThirdPartySigned,
},
@ -36,8 +37,8 @@ use ruma::{
},
StateEventType,
},
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
RoomVersionId, ServerName, UserId,
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName,
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
};
use service::{
appservice::RegistrationInfo,
@ -54,7 +55,10 @@ use crate::{client::full_user_deactivate, Ruma};
/// enabled
#[tracing::instrument(skip(services))]
async fn banned_room_check(
services: &Services, user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>,
services: &Services,
user_id: &UserId,
room_id: Option<&RoomId>,
server_name: Option<&ServerName>,
client_ip: IpAddr,
) -> Result {
if services.users.is_admin(user_id).await {
@ -70,19 +74,21 @@ async fn banned_room_check(
.contains(&room_id.server_name().unwrap().to_owned())
{
warn!(
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
room or banned room server name: {room_id}"
"User {user_id} who is not an admin attempted to send an invite for or \
attempted to join a banned room or banned room server name: {room_id}"
);
if services.globals.config.auto_deactivate_banned_room_attempts {
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
warn!(
"Automatically deactivating user {user_id} due to attempted banned room join"
);
if services.globals.config.admin_room_notices {
services
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join from IP \
{client_ip}"
"Automatically deactivating user {user_id} due to attempted banned \
room join from IP {client_ip}"
)))
.await
.ok();
@ -109,19 +115,21 @@ async fn banned_room_check(
.contains(&server_name.to_owned())
{
warn!(
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} that \
is globally forbidden. Rejecting.",
"User {user_id} who is not an admin tried joining a room which has the server \
name {server_name} that is globally forbidden. Rejecting.",
);
if services.globals.config.auto_deactivate_banned_room_attempts {
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
warn!(
"Automatically deactivating user {user_id} due to attempted banned room join"
);
if services.globals.config.admin_room_notices {
services
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Automatically deactivating user {user_id} due to attempted banned room join from IP \
{client_ip}"
"Automatically deactivating user {user_id} due to attempted banned \
room join from IP {client_ip}"
)))
.await
.ok();
@ -155,12 +163,20 @@ async fn banned_room_check(
/// federation
#[tracing::instrument(skip_all, fields(%client), name = "join")]
pub(crate) async fn join_room_by_id_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.sender_user();
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name(), client).await?;
banned_room_check(
&services,
sender_user,
Some(&body.room_id),
body.room_id.server_name(),
client,
)
.await?;
// There is no body.server_name for /roomId/join
let mut servers: Vec<_> = services
@ -216,7 +232,8 @@ pub(crate) async fn join_room_by_id_route(
/// via room alias server name and room ID server name
#[tracing::instrument(skip_all, fields(%client), name = "join")]
pub(crate) async fn join_room_by_id_or_alias_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
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");
@ -224,8 +241,15 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let body = body.body;
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
Ok(room_id) => {
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name(), client).await?;
| Ok(room_id) => {
banned_room_check(
&services,
sender_user,
Some(&room_id),
room_id.server_name(),
client,
)
.await?;
let mut servers = body.via.clone();
servers.extend(
@ -261,14 +285,21 @@ pub(crate) async fn join_room_by_id_or_alias_route(
(servers, room_id)
},
Err(room_alias) => {
| Err(room_alias) => {
let (room_id, mut servers) = services
.rooms
.alias
.resolve_alias(&room_alias, Some(body.via.clone()))
.await?;
banned_room_check(&services, sender_user, Some(&room_id), Some(room_alias.server_name()), client).await?;
banned_room_check(
&services,
sender_user,
Some(&room_id),
Some(room_alias.server_name()),
client,
)
.await?;
let addl_via_servers = services
.rooms
@ -314,9 +345,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
.boxed()
.await?;
Ok(join_room_by_id_or_alias::v3::Response {
room_id: join_room_response.room_id,
})
Ok(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id })
}
/// # `POST /_matrix/client/v3/rooms/{roomId}/leave`
@ -325,7 +354,8 @@ pub(crate) async fn join_room_by_id_or_alias_route(
///
/// - This should always work if the user is currently joined.
pub(crate) async fn leave_room_route(
State(services): State<crate::State>, body: Ruma<leave_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<leave_room::v3::Request>,
) -> Result<leave_room::v3::Response> {
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone()).await?;
@ -337,7 +367,8 @@ pub(crate) async fn leave_room_route(
/// Tries to send an invite event into the room.
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
pub(crate) async fn invite_user_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<invite_user::v3::Request>,
) -> Result<invite_user::v3::Response> {
let sender_user = body.sender_user();
@ -350,12 +381,16 @@ pub(crate) async fn invite_user_route(
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
}
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name(), client).await?;
banned_room_check(
&services,
sender_user,
Some(&body.room_id),
body.room_id.server_name(),
client,
)
.await?;
if let invite_user::v3::InvitationRecipient::UserId {
user_id,
} = &body.recipient
{
if let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient {
let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id);
let recipient_ignored_by_sender = services.users.user_is_ignored(user_id, sender_user);
@ -363,7 +398,9 @@ pub(crate) async fn invite_user_route(
join!(sender_ignored_recipient, recipient_ignored_by_sender);
if sender_ignored_recipient {
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
return Err!(Request(Forbidden(
"You cannot invite users you have ignored to rooms."
)));
}
if recipient_ignored_by_sender {
@ -386,7 +423,8 @@ pub(crate) async fn invite_user_route(
///
/// Tries to send a kick event into the room.
pub(crate) async fn kick_user_route(
State(services): State<crate::State>, body: Ruma<kick_user::v3::Request>,
State(services): State<crate::State>,
body: Ruma<kick_user::v3::Request>,
) -> Result<kick_user::v3::Response> {
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
@ -405,17 +443,14 @@ pub(crate) async fn kick_user_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
body.user_id.to_string(),
&RoomMemberEventContent {
membership: MembershipState::Leave,
reason: body.reason.clone(),
is_direct: None,
join_authorized_via_users_server: None,
third_party_invite: None,
..event
},
),
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
membership: MembershipState::Leave,
reason: body.reason.clone(),
is_direct: None,
join_authorized_via_users_server: None,
third_party_invite: None,
..event
}),
body.sender_user(),
&body.room_id,
&state_lock,
@ -431,7 +466,8 @@ pub(crate) async fn kick_user_route(
///
/// Tries to send a ban event into the room.
pub(crate) async fn ban_user_route(
State(services): State<crate::State>, body: Ruma<ban_user::v3::Request>,
State(services): State<crate::State>,
body: Ruma<ban_user::v3::Request>,
) -> Result<ban_user::v3::Response> {
let sender_user = body.sender_user();
@ -452,19 +488,16 @@ pub(crate) async fn ban_user_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
body.user_id.to_string(),
&RoomMemberEventContent {
membership: MembershipState::Ban,
reason: body.reason.clone(),
displayname: None, // display name may be offensive
avatar_url: None, // avatar may be offensive
is_direct: None,
join_authorized_via_users_server: None,
third_party_invite: None,
..current_member_content
},
),
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
membership: MembershipState::Ban,
reason: body.reason.clone(),
displayname: None, // display name may be offensive
avatar_url: None, // avatar may be offensive
is_direct: None,
join_authorized_via_users_server: None,
third_party_invite: None,
..current_member_content
}),
sender_user,
&body.room_id,
&state_lock,
@ -480,7 +513,8 @@ pub(crate) async fn ban_user_route(
///
/// Tries to send an unban event into the room.
pub(crate) async fn unban_user_route(
State(services): State<crate::State>, body: Ruma<unban_user::v3::Request>,
State(services): State<crate::State>,
body: Ruma<unban_user::v3::Request>,
) -> Result<unban_user::v3::Response> {
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
@ -502,17 +536,14 @@ pub(crate) async fn unban_user_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
body.user_id.to_string(),
&RoomMemberEventContent {
membership: MembershipState::Leave,
reason: body.reason.clone(),
join_authorized_via_users_server: None,
third_party_invite: None,
is_direct: None,
..current_member_content
},
),
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
membership: MembershipState::Leave,
reason: body.reason.clone(),
join_authorized_via_users_server: None,
third_party_invite: None,
is_direct: None,
..current_member_content
}),
body.sender_user(),
&body.room_id,
&state_lock,
@ -534,7 +565,8 @@ pub(crate) 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(crate) async fn forget_room_route(
State(services): State<crate::State>, body: Ruma<forget_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<forget_room::v3::Request>,
) -> Result<forget_room::v3::Response> {
let sender_user = body.sender_user();
@ -559,7 +591,8 @@ pub(crate) async fn forget_room_route(
///
/// Lists all rooms the user has joined.
pub(crate) async fn joined_rooms_route(
State(services): State<crate::State>, body: Ruma<joined_rooms::v3::Request>,
State(services): State<crate::State>,
body: Ruma<joined_rooms::v3::Request>,
) -> Result<joined_rooms::v3::Response> {
Ok(joined_rooms::v3::Response {
joined_rooms: services
@ -579,7 +612,8 @@ pub(crate) async fn joined_rooms_route(
///
/// - Only works if the user is currently joined
pub(crate) async fn get_member_events_route(
State(services): State<crate::State>, body: Ruma<get_member_events::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_member_events::v3::Request>,
) -> Result<get_member_events::v3::Response> {
let sender_user = body.sender_user();
@ -612,7 +646,8 @@ pub(crate) async fn get_member_events_route(
/// - The sender user must be in the room
/// - TODO: An appservice just needs a puppet joined
pub(crate) async fn joined_members_route(
State(services): State<crate::State>, body: Ruma<joined_members::v3::Request>,
State(services): State<crate::State>,
body: Ruma<joined_members::v3::Request>,
) -> Result<joined_members::v3::Response> {
let sender_user = body.sender_user();
@ -631,25 +666,25 @@ pub(crate) async fn joined_members_route(
.room_members(&body.room_id)
.map(ToOwned::to_owned)
.then(|user| async move {
(
user.clone(),
RoomMember {
display_name: services.users.displayname(&user).await.ok(),
avatar_url: services.users.avatar_url(&user).await.ok(),
},
)
(user.clone(), RoomMember {
display_name: services.users.displayname(&user).await.ok(),
avatar_url: services.users.avatar_url(&user).await.ok(),
})
})
.collect()
.await;
Ok(joined_members::v3::Response {
joined,
})
Ok(joined_members::v3::Response { joined })
}
pub async fn join_room_by_id_helper(
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
third_party_signed: Option<&ThirdPartySigned>, appservice_info: &Option<RegistrationInfo>,
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: &[OwnedServerName],
third_party_signed: Option<&ThirdPartySigned>,
appservice_info: &Option<RegistrationInfo>,
) -> Result<join_room_by_id::v3::Response> {
let state_lock = services.rooms.state.mutex.lock(room_id).await;
@ -671,9 +706,7 @@ pub async fn join_room_by_id_helper(
.await
{
debug_warn!("{sender_user} is already joined in {room_id}");
return Ok(join_room_by_id::v3::Response {
room_id: room_id.into(),
});
return Ok(join_room_by_id::v3::Response { room_id: room_id.into() });
}
if let Ok(membership) = services
@ -694,18 +727,35 @@ pub async fn join_room_by_id_helper(
.server_in_room(services.globals.server_name(), room_id)
.await;
let local_join =
server_in_room || servers.is_empty() || (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
let local_join = server_in_room
|| servers.is_empty()
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
if local_join {
join_room_by_id_helper_local(services, sender_user, room_id, reason, servers, third_party_signed, state_lock)
.boxed()
.await?;
join_room_by_id_helper_local(
services,
sender_user,
room_id,
reason,
servers,
third_party_signed,
state_lock,
)
.boxed()
.await?;
} else {
// Ask a remote server if we are not participating in this room
join_room_by_id_helper_remote(services, sender_user, room_id, reason, servers, third_party_signed, state_lock)
.boxed()
.await?;
join_room_by_id_helper_remote(
services,
sender_user,
room_id,
reason,
servers,
third_party_signed,
state_lock,
)
.boxed()
.await?;
}
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
@ -713,12 +763,18 @@ pub async fn join_room_by_id_helper(
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote")]
async fn join_room_by_id_helper_remote(
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: &[OwnedServerName],
_third_party_signed: Option<&ThirdPartySigned>,
state_lock: RoomMutexGuard,
) -> Result {
info!("Joining {room_id} over federation.");
let (make_join_response, remote_server) = make_join_request(services, sender_user, room_id, servers).await?;
let (make_join_response, remote_server) =
make_join_request(services, sender_user, room_id, servers).await?;
info!("make_join finished");
@ -783,8 +839,8 @@ async fn join_room_by_id_helper_remote(
// We keep the "event_id" in the pdu only in v1 or
// v2 rooms
match room_version_id {
RoomVersionId::V1 | RoomVersionId::V2 => {},
_ => {
| RoomVersionId::V1 | RoomVersionId::V2 => {},
| _ => {
join_event_stub.remove("event_id");
},
};
@ -799,7 +855,8 @@ async fn join_room_by_id_helper_remote(
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
// Add event_id back
join_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
join_event_stub
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
// It has enough fields to be called a proper event now
let mut join_event = join_event_stub;
@ -825,12 +882,16 @@ async fn join_room_by_id_helper_remote(
if join_authorized_via_users_server.is_some() {
if let Some(signed_raw) = &send_join_response.room_state.event {
debug_info!(
"There is a signed event with join_authorized_via_users_server. This room is probably using \
restricted joins. Adding signature to our event"
"There is a signed event with join_authorized_via_users_server. This room is \
probably using restricted joins. Adding signature to our event"
);
let (signed_event_id, signed_value) = gen_event_id_canonical_json(signed_raw, &room_version_id)
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
let (signed_event_id, signed_value) =
gen_event_id_canonical_json(signed_raw, &room_version_id).map_err(|e| {
err!(Request(BadJson(warn!(
"Could not convert event to canonical JSON: {e}"
))))
})?;
if signed_event_id != event_id {
return Err!(Request(BadJson(
@ -840,15 +901,20 @@ async fn join_room_by_id_helper_remote(
match signed_value["signatures"]
.as_object()
.ok_or_else(|| err!(BadServerResponse(warn!("Server {remote_server} sent invalid signatures type"))))
.ok_or_else(|| {
err!(BadServerResponse(warn!(
"Server {remote_server} sent invalid signatures type"
)))
})
.and_then(|e| {
e.get(remote_server.as_str()).ok_or_else(|| {
err!(BadServerResponse(warn!(
"Server {remote_server} did not send its signature for a restricted room"
"Server {remote_server} did not send its signature for a restricted \
room"
)))
})
}) {
Ok(signature) => {
| Ok(signature) => {
join_event
.get_mut("signatures")
.expect("we created a valid pdu")
@ -856,10 +922,10 @@ async fn join_room_by_id_helper_remote(
.expect("we created a valid pdu")
.insert(remote_server.to_string(), signature.clone());
},
Err(e) => {
| Err(e) => {
warn!(
"Server {remote_server} sent invalid signature in send_join signatures for event \
{signed_value:?}: {e:?}",
"Server {remote_server} sent invalid signature in send_join signatures \
for event {signed_value:?}: {e:?}",
);
},
}
@ -900,8 +966,8 @@ async fn join_room_by_id_helper_remote(
.ready_filter_map(Result::ok)
.fold(HashMap::new(), |mut state, (event_id, value)| async move {
let pdu = match PduEvent::from_id_val(&event_id, value.clone()) {
Ok(pdu) => pdu,
Err(e) => {
| Ok(pdu) => pdu,
| Err(e) => {
debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}");
return state;
},
@ -937,7 +1003,9 @@ async fn join_room_by_id_helper_remote(
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
})
.ready_filter_map(Result::ok)
.ready_for_each(|(event_id, value)| services.rooms.outlier.add_pdu_outlier(&event_id, &value))
.ready_for_each(|(event_id, value)| {
services.rooms.outlier.add_pdu_outlier(&event_id, &value);
})
.await;
drop(cork);
@ -1031,29 +1099,38 @@ async fn join_room_by_id_helper_remote(
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_local")]
async fn join_room_by_id_helper_local(
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: &[OwnedServerName],
_third_party_signed: Option<&ThirdPartySigned>,
state_lock: RoomMutexGuard,
) -> Result {
debug_info!("We can join locally");
let join_rules_event_content = services
.rooms
.state_accessor
.room_state_get_content::<RoomJoinRulesEventContent>(room_id, &StateEventType::RoomJoinRules, "")
.room_state_get_content::<RoomJoinRulesEventContent>(
room_id,
&StateEventType::RoomJoinRules,
"",
)
.await;
let restriction_rooms = match join_rules_event_content {
Ok(RoomJoinRulesEventContent {
| Ok(RoomJoinRulesEventContent {
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
}) => restricted
.allow
.into_iter()
.filter_map(|a| match a {
AllowRule::RoomMembership(r) => Some(r.room_id),
_ => None,
| AllowRule::RoomMembership(r) => Some(r.room_id),
| _ => None,
})
.collect(),
_ => Vec::new(),
| _ => Vec::new(),
};
let join_authorized_via_users_server: Option<OwnedUserId> = {
@ -1073,10 +1150,12 @@ async fn join_room_by_id_helper_local(
.state_cache
.local_users_in_room(room_id)
.filter(|user| {
services
.rooms
.state_accessor
.user_can_invite(room_id, user, sender_user, &state_lock)
services.rooms.state_accessor.user_can_invite(
room_id,
user,
sender_user,
&state_lock,
)
})
.boxed()
.next()
@ -1112,13 +1191,18 @@ async fn join_room_by_id_helper_local(
};
if restriction_rooms.is_empty()
&& (servers.is_empty() || servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
&& (servers.is_empty()
|| servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
{
return Err(error);
}
warn!("We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements");
let Ok((make_join_response, remote_server)) = make_join_request(services, sender_user, room_id, servers).await
warn!(
"We couldn't do the join locally, maybe federation can help to satisfy the restricted \
join requirements"
);
let Ok((make_join_response, remote_server)) =
make_join_request(services, sender_user, room_id, servers).await
else {
return Err(error);
};
@ -1133,8 +1217,10 @@ async fn join_room_by_id_helper_local(
));
}
let mut join_event_stub: CanonicalJsonObject = serde_json::from_str(make_join_response.event.get())
.map_err(|e| err!(BadServerResponse("Invalid make_join event json received from server: {e:?}")))?;
let mut join_event_stub: CanonicalJsonObject =
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
err!(BadServerResponse("Invalid make_join event json received from server: {e:?}"))
})?;
let join_authorized_via_users_server = join_event_stub
.get("content")
@ -1173,8 +1259,8 @@ async fn join_room_by_id_helper_local(
// We keep the "event_id" in the pdu only in v1 or
// v2 rooms
match room_version_id {
RoomVersionId::V1 | RoomVersionId::V2 => {},
_ => {
| RoomVersionId::V1 | RoomVersionId::V2 => {},
| _ => {
join_event_stub.remove("event_id");
},
};
@ -1189,7 +1275,8 @@ async fn join_room_by_id_helper_local(
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
// Add event_id back
join_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
join_event_stub
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
// It has enough fields to be called a proper event now
let join_event = join_event_stub;
@ -1211,8 +1298,10 @@ async fn join_room_by_id_helper_local(
.await?;
if let Some(signed_raw) = send_join_response.room_state.event {
let (signed_event_id, signed_value) = gen_event_id_canonical_json(&signed_raw, &room_version_id)
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
let (signed_event_id, signed_value) =
gen_event_id_canonical_json(&signed_raw, &room_version_id).map_err(|e| {
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
})?;
if signed_event_id != event_id {
return Err!(Request(BadJson(
@ -1234,9 +1323,13 @@ async fn join_room_by_id_helper_local(
}
async fn make_join_request(
services: &Services, sender_user: &UserId, room_id: &RoomId, servers: &[OwnedServerName],
services: &Services,
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!(BadServerResponse("No server available to assist in joining."));
let mut make_join_response_and_server =
Err!(BadServerResponse("No server available to assist in joining."));
let mut make_join_counter: usize = 0;
let mut incompatible_room_version_count: usize = 0;
@ -1266,23 +1359,28 @@ async fn make_join_request(
e.kind(),
ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
) {
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
incompatible_room_version_count =
incompatible_room_version_count.saturating_add(1);
}
if incompatible_room_version_count > 15 {
info!(
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or M_UNSUPPORTED_ROOM_VERSION, \
assuming that conduwuit does not support the room version {room_id}: {e}"
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or \
M_UNSUPPORTED_ROOM_VERSION, assuming that conduwuit does not support the \
room version {room_id}: {e}"
);
make_join_response_and_server = Err!(BadServerResponse("Room version is not supported by Conduwuit"));
make_join_response_and_server =
Err!(BadServerResponse("Room version is not supported by Conduwuit"));
return make_join_response_and_server;
}
if make_join_counter > 40 {
warn!(
"40 servers failed to provide valid make_join response, assuming no server can assist in joining."
"40 servers failed to provide valid make_join response, assuming no server \
can assist in joining."
);
make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining."));
make_join_response_and_server =
Err!(BadServerResponse("No server available to assist in joining."));
return make_join_response_and_server;
}
}
@ -1298,11 +1396,18 @@ async fn make_join_request(
}
pub(crate) async fn invite_helper(
services: &Services, sender_user: &UserId, user_id: &UserId, room_id: &RoomId, reason: Option<String>,
services: &Services,
sender_user: &UserId,
user_id: &UserId,
room_id: &RoomId,
reason: Option<String>,
is_direct: bool,
) -> Result {
if !services.users.is_admin(sender_user).await && services.globals.block_non_admin_invites() {
info!("User {sender_user} is not an admin and attempted to send an invite to room {room_id}");
info!(
"User {sender_user} is not an admin and attempted to send an invite to room \
{room_id}"
);
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
}
@ -1339,31 +1444,30 @@ pub(crate) async fn invite_helper(
let response = services
.sending
.send_federation_request(
user_id.server_name(),
create_invite::v2::Request {
room_id: room_id.to_owned(),
event_id: (*pdu.event_id).to_owned(),
room_version: room_version_id.clone(),
event: services
.sending
.convert_to_outgoing_federation_event(pdu_json.clone())
.await,
invite_room_state,
via: services
.rooms
.state_cache
.servers_route_via(room_id)
.await
.ok(),
},
)
.send_federation_request(user_id.server_name(), create_invite::v2::Request {
room_id: room_id.to_owned(),
event_id: (*pdu.event_id).to_owned(),
room_version: room_version_id.clone(),
event: services
.sending
.convert_to_outgoing_federation_event(pdu_json.clone())
.await,
invite_room_state,
via: services
.rooms
.state_cache
.servers_route_via(room_id)
.await
.ok(),
})
.await?;
// We do not add the event_id field to the pdu here because of signature and
// hashes checks
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
.map_err(|e| {
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
})?;
if pdu.event_id != event_id {
return Err!(Request(BadJson(
@ -1379,14 +1483,18 @@ pub(crate) async fn invite_helper(
)
.expect("CanonicalJson is valid json value"),
)
.map_err(|e| err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}")))))?;
.map_err(|e| {
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
})?;
let pdu_id = services
.rooms
.event_handler
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
.await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept incoming PDU as timeline event."))))?;
.ok_or_else(|| {
err!(Request(InvalidParam("Could not accept incoming PDU as timeline event.")))
})?;
return services.sending.send_pdu_room(room_id, &pdu_id).await;
}
@ -1456,7 +1564,12 @@ pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
}
}
pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
pub async fn leave_room(
services: &Services,
user_id: &UserId,
room_id: &RoomId,
reason: Option<String>,
) -> Result<()> {
//use conduwuit::utils::stream::OptionStream;
use futures::TryFutureExt;
@ -1500,7 +1613,11 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
let Ok(event) = services
.rooms
.state_accessor
.room_state_get_content::<RoomMemberEventContent>(room_id, &StateEventType::RoomMember, user_id.as_str())
.room_state_get_content::<RoomMemberEventContent>(
room_id,
&StateEventType::RoomMember,
user_id.as_str(),
)
.await
else {
// Fix for broken rooms
@ -1527,14 +1644,11 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
user_id.to_string(),
&RoomMemberEventContent {
membership: MembershipState::Leave,
reason,
..event
},
),
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
membership: MembershipState::Leave,
reason,
..event
}),
user_id,
room_id,
&state_lock,
@ -1545,8 +1659,13 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
Ok(())
}
async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<()> {
let mut make_leave_response_and_server = Err!(BadServerResponse("No server available to assist in leaving."));
async fn remote_leave_room(
services: &Services,
user_id: &UserId,
room_id: &RoomId,
) -> Result<()> {
let mut make_leave_response_and_server =
Err!(BadServerResponse("No server available to assist in leaving."));
let invite_state = services
.rooms
@ -1608,8 +1727,12 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
));
}
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(make_leave_response.event.get())
.map_err(|e| err!(BadServerResponse("Invalid make_leave event json received from server: {e:?}")))?;
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
make_leave_response.event.get(),
)
.map_err(|e| {
err!(BadServerResponse("Invalid make_leave event json received from server: {e:?}"))
})?;
// TODO: Is origin needed?
leave_event_stub.insert(
@ -1627,8 +1750,8 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
// room v3 and above removed the "event_id" field from remote PDU format
match room_version_id {
RoomVersionId::V1 | RoomVersionId::V2 => {},
_ => {
| RoomVersionId::V1 | RoomVersionId::V2 => {},
| _ => {
leave_event_stub.remove("event_id");
},
};
@ -1643,7 +1766,8 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
let event_id = pdu::gen_event_id(&leave_event_stub, &room_version_id)?;
// Add event_id back
leave_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
leave_event_stub
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
// It has enough fields to be called a proper event now
let leave_event = leave_event_stub;

View file

@ -56,7 +56,8 @@ const LIMIT_DEFAULT: usize = 10;
/// - Only works if the user is joined (TODO: always allow, but only show events
/// where the user was joined, depending on `history_visibility`)
pub(crate) async fn get_message_events_route(
State(services): State<crate::State>, body: Ruma<get_message_events::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_message_events::v3::Request>,
) -> Result<get_message_events::v3::Response> {
let sender = body.sender();
let (sender_user, sender_device) = sender;
@ -69,8 +70,8 @@ pub(crate) async fn get_message_events_route(
.map(str::parse)
.transpose()?
.unwrap_or_else(|| match body.dir {
Direction::Forward => PduCount::min(),
Direction::Backward => PduCount::max(),
| Direction::Forward => PduCount::min(),
| Direction::Backward => PduCount::max(),
});
let to: Option<PduCount> = body.to.as_deref().map(str::parse).flat_ok();
@ -81,10 +82,12 @@ pub(crate) async fn get_message_events_route(
.unwrap_or(LIMIT_DEFAULT)
.min(LIMIT_MAX);
services
.rooms
.lazy_loading
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, from);
services.rooms.lazy_loading.lazy_load_confirm_delivery(
sender_user,
sender_device,
room_id,
from,
);
if matches!(body.dir, Direction::Backward) {
services
@ -98,14 +101,14 @@ pub(crate) async fn get_message_events_route(
}
let it = match body.dir {
Direction::Forward => services
| Direction::Forward => services
.rooms
.timeline
.pdus(Some(sender_user), room_id, Some(from))
.await?
.boxed(),
Direction::Backward => services
| Direction::Backward => services
.rooms
.timeline
.pdus_rev(Some(sender_user), room_id, Some(from))
@ -141,10 +144,13 @@ pub(crate) async fn get_message_events_route(
if !cfg!(feature = "element_hacks") {
if let Some(next_token) = next_token {
services
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy, next_token);
services.rooms.lazy_loading.lazy_load_mark_sent(
sender_user,
sender_device,
room_id,
lazy,
next_token,
);
}
}
@ -162,7 +168,11 @@ pub(crate) async fn get_message_events_route(
})
}
async fn get_member_event(services: &Services, room_id: &RoomId, user_id: &UserId) -> Option<Raw<AnyStateEvent>> {
async fn get_member_event(
services: &Services,
room_id: &RoomId,
user_id: &UserId,
) -> Option<Raw<AnyStateEvent>> {
services
.rooms
.state_accessor
@ -173,7 +183,11 @@ async fn get_member_event(services: &Services, room_id: &RoomId, user_id: &UserI
}
pub(crate) async fn update_lazy(
services: &Services, room_id: &RoomId, sender: (&UserId, &DeviceId), mut lazy: LazySet, item: &PdusIterItem,
services: &Services,
room_id: &RoomId,
sender: (&UserId, &DeviceId),
mut lazy: LazySet,
item: &PdusIterItem,
force: bool,
) -> LazySet {
let (_, event) = &item;
@ -204,7 +218,11 @@ pub(crate) async fn update_lazy(
lazy
}
pub(crate) async fn ignored_filter(services: &Services, item: PdusIterItem, user_id: &UserId) -> Option<PdusIterItem> {
pub(crate) async fn ignored_filter(
services: &Services,
item: PdusIterItem,
user_id: &UserId,
) -> Option<PdusIterItem> {
let (_, pdu) = &item;
// exclude Synapse's dummy events from bloating up response bodies. clients
@ -223,7 +241,9 @@ pub(crate) async fn ignored_filter(services: &Services, item: PdusIterItem, user
}
pub(crate) async fn visibility_filter(
services: &Services, item: PdusIterItem, user_id: &UserId,
services: &Services,
item: PdusIterItem,
user_id: &UserId,
) -> Option<PdusIterItem> {
let (_, pdu) = &item;

View file

@ -16,7 +16,8 @@ use crate::{Error, Result, Ruma};
///
/// - The token generated is only valid for the OpenID API
pub(crate) async fn create_openid_token_route(
State(services): State<crate::State>, body: Ruma<account::request_openid_token::v3::Request>,
State(services): State<crate::State>,
body: Ruma<account::request_openid_token::v3::Request>,
) -> Result<account::request_openid_token::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -12,10 +12,14 @@ use crate::{Error, Result, Ruma};
///
/// Sets the presence state of the sender user.
pub(crate) async fn set_presence_route(
State(services): State<crate::State>, body: Ruma<set_presence::v3::Request>,
State(services): State<crate::State>,
body: Ruma<set_presence::v3::Request>,
) -> Result<set_presence::v3::Response> {
if !services.globals.allow_local_presence() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Presence is disabled on this server",
));
}
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -40,10 +44,14 @@ pub(crate) async fn set_presence_route(
///
/// - Only works if you share a room with the user
pub(crate) async fn get_presence_route(
State(services): State<crate::State>, body: Ruma<get_presence::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_presence::v3::Request>,
) -> Result<get_presence::v3::Response> {
if !services.globals.allow_local_presence() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Presence is disabled on this server",
));
}
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -11,7 +11,9 @@ use ruma::{
api::{
client::{
error::ErrorKind,
profile::{get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name},
profile::{
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
},
},
federation,
},
@ -29,7 +31,8 @@ use crate::Ruma;
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn set_displayname_route(
State(services): State<crate::State>, body: Ruma<set_display_name::v3::Request>,
State(services): State<crate::State>,
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");
@ -45,7 +48,8 @@ pub(crate) async fn set_displayname_route(
.collect()
.await;
update_displayname(&services, &body.user_id, body.displayname.clone(), &all_joined_rooms).await;
update_displayname(&services, &body.user_id, body.displayname.clone(), &all_joined_rooms)
.await;
if services.globals.allow_local_presence() {
// Presence update
@ -65,7 +69,8 @@ pub(crate) async fn set_displayname_route(
/// - If user is on another server and we do not have a local copy already fetch
/// displayname over federation
pub(crate) async fn get_displayname_route(
State(services): State<crate::State>, body: Ruma<get_display_name::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_display_name::v3::Request>,
) -> Result<get_display_name::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
// Create and update our local copy of the user
@ -94,9 +99,7 @@ pub(crate) async fn get_displayname_route(
.users
.set_blurhash(&body.user_id, response.blurhash.clone());
return Ok(get_display_name::v3::Response {
displayname: response.displayname,
});
return Ok(get_display_name::v3::Response { displayname: response.displayname });
}
}
@ -117,7 +120,8 @@ pub(crate) async fn get_displayname_route(
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn set_avatar_url_route(
State(services): State<crate::State>, body: Ruma<set_avatar_url::v3::Request>,
State(services): State<crate::State>,
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");
@ -161,7 +165,8 @@ pub(crate) async fn set_avatar_url_route(
/// - If user is on another server and we do not have a local copy already fetch
/// `avatar_url` and blurhash over federation
pub(crate) async fn get_avatar_url_route(
State(services): State<crate::State>, body: Ruma<get_avatar_url::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_avatar_url::v3::Request>,
) -> Result<get_avatar_url::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
// Create and update our local copy of the user
@ -218,7 +223,8 @@ pub(crate) async fn get_avatar_url_route(
/// - If user is on another server and we do not have a local copy already,
/// fetch profile over federation.
pub(crate) async fn get_profile_route(
State(services): State<crate::State>, body: Ruma<get_profile::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_profile::v3::Request>,
) -> Result<get_profile::v3::Response> {
if !services.globals.user_is_local(&body.user_id) {
// Create and update our local copy of the user
@ -254,9 +260,11 @@ pub(crate) async fn get_profile_route(
.set_timezone(&body.user_id, response.tz.clone());
for (profile_key, profile_key_value) in &response.custom_profile_fields {
services
.users
.set_profile_key(&body.user_id, profile_key, Some(profile_key_value.clone()));
services.users.set_profile_key(
&body.user_id,
profile_key,
Some(profile_key_value.clone()),
);
}
return Ok(get_profile::v3::Response {
@ -295,7 +303,10 @@ pub(crate) async fn get_profile_route(
}
pub async fn update_displayname(
services: &Services, user_id: &UserId, displayname: Option<String>, all_joined_rooms: &[OwnedRoomId],
services: &Services,
user_id: &UserId,
displayname: Option<String>,
all_joined_rooms: &[OwnedRoomId],
) {
let (current_avatar_url, current_blurhash, current_displayname) = join3(
services.users.avatar_url(user_id),
@ -322,19 +333,16 @@ pub async fn update_displayname(
.iter()
.try_stream()
.and_then(|room_id: &OwnedRoomId| async move {
let pdu = PduBuilder::state(
user_id.to_string(),
&RoomMemberEventContent {
displayname: displayname.clone(),
membership: MembershipState::Join,
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
},
);
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
displayname: displayname.clone(),
membership: MembershipState::Join,
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
});
Ok((pdu, room_id))
})
@ -346,7 +354,10 @@ pub async fn update_displayname(
}
pub async fn update_avatar_url(
services: &Services, user_id: &UserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>,
services: &Services,
user_id: &UserId,
avatar_url: Option<OwnedMxcUri>,
blurhash: Option<String>,
all_joined_rooms: &[OwnedRoomId],
) {
let (current_avatar_url, current_blurhash, current_displayname) = join3(
@ -375,19 +386,16 @@ pub async fn update_avatar_url(
.iter()
.try_stream()
.and_then(|room_id: &OwnedRoomId| async move {
let pdu = PduBuilder::state(
user_id.to_string(),
&RoomMemberEventContent {
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
membership: MembershipState::Join,
displayname: displayname.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
},
);
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
membership: MembershipState::Join,
displayname: displayname.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
});
Ok((pdu, room_id))
})
@ -399,7 +407,9 @@ pub async fn update_avatar_url(
}
pub async fn update_all_rooms(
services: &Services, all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: &UserId,
services: &Services,
all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>,
user_id: &UserId,
) {
for (pdu_builder, room_id) in all_joined_rooms {
let state_lock = services.rooms.state.mutex.lock(room_id).await;

View file

@ -4,15 +4,19 @@ use ruma::{
api::client::{
error::ErrorKind,
push::{
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled, get_pushrules_all,
get_pushrules_global_scope, set_pusher, set_pushrule, set_pushrule_actions, set_pushrule_enabled,
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions,
get_pushrule_enabled, get_pushrules_all, get_pushrules_global_scope, set_pusher,
set_pushrule, set_pushrule_actions, set_pushrule_enabled,
},
},
events::{
push_rules::{PushRulesEvent, PushRulesEventContent},
GlobalAccountDataEventType,
},
push::{InsertPushRuleError, PredefinedContentRuleId, PredefinedOverrideRuleId, RemovePushRuleError, Ruleset},
push::{
InsertPushRuleError, PredefinedContentRuleId, PredefinedOverrideRuleId,
RemovePushRuleError, Ruleset,
},
CanonicalJsonObject, CanonicalJsonValue,
};
use service::Services;
@ -23,7 +27,8 @@ use crate::{Error, Result, Ruma};
///
/// Retrieves the push rules event for this user.
pub(crate) async fn get_pushrules_all_route(
State(services): State<crate::State>, body: Ruma<get_pushrules_all::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_pushrules_all::v3::Request>,
) -> Result<get_pushrules_all::v3::Response> {
let sender_user = body.sender_user();
@ -40,8 +45,10 @@ pub(crate) async fn get_pushrules_all_route(
return recreate_push_rules_and_return(&services, sender_user).await;
};
let account_data_content = serde_json::from_value::<PushRulesEventContent>(content_value.into())
.map_err(|e| err!(Database(warn!("Invalid push rules account data event in database: {e}"))))?;
let account_data_content =
serde_json::from_value::<PushRulesEventContent>(content_value.into()).map_err(|e| {
err!(Database(warn!("Invalid push rules account data event in database: {e}")))
})?;
let mut global_ruleset = account_data_content.global;
@ -79,9 +86,7 @@ pub(crate) async fn get_pushrules_all_route(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(PushRulesEvent {
content: PushRulesEventContent {
global: global_ruleset.clone(),
},
content: PushRulesEventContent { global: global_ruleset.clone() },
})
.expect("to json always works"),
)
@ -89,9 +94,7 @@ pub(crate) async fn get_pushrules_all_route(
}
};
Ok(get_pushrules_all::v3::Response {
global: global_ruleset,
})
Ok(get_pushrules_all::v3::Response { global: global_ruleset })
}
/// # `GET /_matrix/client/r0/pushrules/global/`
@ -100,7 +103,8 @@ pub(crate) async fn get_pushrules_all_route(
///
/// This appears to be the exact same as `GET /_matrix/client/r0/pushrules/`.
pub(crate) async fn get_pushrules_global_route(
State(services): State<crate::State>, body: Ruma<get_pushrules_global_scope::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_pushrules_global_scope::v3::Request>,
) -> Result<get_pushrules_global_scope::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -134,8 +138,10 @@ pub(crate) async fn get_pushrules_global_route(
});
};
let account_data_content = serde_json::from_value::<PushRulesEventContent>(content_value.into())
.map_err(|e| err!(Database(warn!("Invalid push rules account data event in database: {e}"))))?;
let account_data_content =
serde_json::from_value::<PushRulesEventContent>(content_value.into()).map_err(|e| {
err!(Database(warn!("Invalid push rules account data event in database: {e}")))
})?;
let mut global_ruleset = account_data_content.global;
@ -173,9 +179,7 @@ pub(crate) async fn get_pushrules_global_route(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(PushRulesEvent {
content: PushRulesEventContent {
global: global_ruleset.clone(),
},
content: PushRulesEventContent { global: global_ruleset.clone() },
})
.expect("to json always works"),
)
@ -183,16 +187,15 @@ pub(crate) async fn get_pushrules_global_route(
}
};
Ok(get_pushrules_global_scope::v3::Response {
global: global_ruleset,
})
Ok(get_pushrules_global_scope::v3::Response { global: global_ruleset })
}
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
///
/// Retrieves a single specified push rule for this user.
pub(crate) async fn get_pushrule_route(
State(services): State<crate::State>, body: Ruma<get_pushrule::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_pushrule::v3::Request>,
) -> Result<get_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -218,9 +221,7 @@ pub(crate) async fn get_pushrule_route(
.map(Into::into);
if let Some(rule) = rule {
Ok(get_pushrule::v3::Response {
rule,
})
Ok(get_pushrule::v3::Response { rule })
} else {
Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."))
}
@ -230,7 +231,8 @@ pub(crate) async fn get_pushrule_route(
///
/// Creates a single specified push rule for this user.
pub(crate) async fn set_pushrule_route(
State(services): State<crate::State>, body: Ruma<set_pushrule::v3::Request>,
State(services): State<crate::State>,
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;
@ -241,32 +243,33 @@ pub(crate) async fn set_pushrule_route(
.await
.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
if let Err(error) =
account_data
.content
.global
.insert(body.rule.clone(), body.after.as_deref(), body.before.as_deref())
{
if let Err(error) = account_data.content.global.insert(
body.rule.clone(),
body.after.as_deref(),
body.before.as_deref(),
) {
let err = match error {
InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
| InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
ErrorKind::InvalidParam,
"Rule IDs starting with a dot are reserved for server-default rules.",
),
InsertPushRuleError::InvalidRuleId => {
Error::BadRequest(ErrorKind::InvalidParam, "Rule ID containing invalid characters.")
},
InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
| InsertPushRuleError::InvalidRuleId => Error::BadRequest(
ErrorKind::InvalidParam,
"Rule ID containing invalid characters.",
),
| InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
ErrorKind::InvalidParam,
"Can't place a push rule relatively to a server-default rule.",
),
InsertPushRuleError::UnknownRuleId => {
Error::BadRequest(ErrorKind::NotFound, "The before or after rule could not be found.")
},
InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
| InsertPushRuleError::UnknownRuleId => Error::BadRequest(
ErrorKind::NotFound,
"The before or after rule could not be found.",
),
| InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
ErrorKind::InvalidParam,
"The before rule has a higher priority than the after rule.",
),
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
};
return Err(err);
@ -289,7 +292,8 @@ pub(crate) async fn set_pushrule_route(
///
/// Gets the actions of a single specified push rule for this user.
pub(crate) async fn get_pushrule_actions_route(
State(services): State<crate::State>, body: Ruma<get_pushrule_actions::v3::Request>,
State(services): State<crate::State>,
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");
@ -315,16 +319,15 @@ pub(crate) async fn get_pushrule_actions_route(
.map(|rule| rule.actions().to_owned())
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
Ok(get_pushrule_actions::v3::Response {
actions,
})
Ok(get_pushrule_actions::v3::Response { actions })
}
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/actions`
///
/// Sets the actions of a single specified push rule for this user.
pub(crate) async fn set_pushrule_actions_route(
State(services): State<crate::State>, body: Ruma<set_pushrule_actions::v3::Request>,
State(services): State<crate::State>,
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");
@ -360,7 +363,8 @@ pub(crate) async fn set_pushrule_actions_route(
///
/// Gets the enabled status of a single specified push rule for this user.
pub(crate) async fn get_pushrule_enabled_route(
State(services): State<crate::State>, body: Ruma<get_pushrule_enabled::v3::Request>,
State(services): State<crate::State>,
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");
@ -370,9 +374,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|| body.rule_id.as_str() == PredefinedOverrideRuleId::ContainsDisplayName.as_str()
|| body.rule_id.as_str() == PredefinedOverrideRuleId::RoomNotif.as_str()
{
return Ok(get_pushrule_enabled::v3::Response {
enabled: false,
});
return Ok(get_pushrule_enabled::v3::Response { enabled: false });
}
let event: PushRulesEvent = services
@ -388,16 +390,15 @@ pub(crate) async fn get_pushrule_enabled_route(
.map(ruma::push::AnyPushRuleRef::enabled)
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
Ok(get_pushrule_enabled::v3::Response {
enabled,
})
Ok(get_pushrule_enabled::v3::Response { enabled })
}
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/enabled`
///
/// Sets the enabled status of a single specified push rule for this user.
pub(crate) async fn set_pushrule_enabled_route(
State(services): State<crate::State>, body: Ruma<set_pushrule_enabled::v3::Request>,
State(services): State<crate::State>,
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");
@ -433,7 +434,8 @@ pub(crate) async fn set_pushrule_enabled_route(
///
/// Deletes a single specified push rule for this user.
pub(crate) async fn delete_pushrule_route(
State(services): State<crate::State>, body: Ruma<delete_pushrule::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_pushrule::v3::Request>,
) -> Result<delete_pushrule::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -449,11 +451,13 @@ pub(crate) async fn delete_pushrule_route(
.remove(body.kind.clone(), &body.rule_id)
{
let err = match error {
RemovePushRuleError::ServerDefault => {
Error::BadRequest(ErrorKind::InvalidParam, "Cannot delete a server-default pushrule.")
},
RemovePushRuleError::NotFound => Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
| RemovePushRuleError::ServerDefault => Error::BadRequest(
ErrorKind::InvalidParam,
"Cannot delete a server-default pushrule.",
),
| RemovePushRuleError::NotFound =>
Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
};
return Err(err);
@ -476,7 +480,8 @@ pub(crate) async fn delete_pushrule_route(
///
/// Gets all currently active pushers for the sender user.
pub(crate) async fn get_pushers_route(
State(services): State<crate::State>, body: Ruma<get_pushers::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_pushers::v3::Request>,
) -> Result<get_pushers::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -491,7 +496,8 @@ pub(crate) async fn get_pushers_route(
///
/// - TODO: Handle `append`
pub(crate) async fn set_pushers_route(
State(services): State<crate::State>, body: Ruma<set_pusher::v3::Request>,
State(services): State<crate::State>,
body: Ruma<set_pusher::v3::Request>,
) -> Result<set_pusher::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -506,7 +512,8 @@ pub(crate) async fn set_pushers_route(
/// user somehow has bad push rules, these must always exist per spec.
/// so recreate it and return server default silently
async fn recreate_push_rules_and_return(
services: &Services, sender_user: &ruma::UserId,
services: &Services,
sender_user: &ruma::UserId,
) -> Result<get_pushrules_all::v3::Response> {
services
.account_data

View file

@ -21,15 +21,14 @@ use crate::{Result, Ruma};
/// - If `read_receipt` is set: Update private marker and public read receipt
/// EDU
pub(crate) async fn set_read_marker_route(
State(services): State<crate::State>, body: Ruma<set_read_marker::v3::Request>,
State(services): State<crate::State>,
body: Ruma<set_read_marker::v3::Request>,
) -> Result<set_read_marker::v3::Response> {
let sender_user = body.sender_user();
if let Some(event) = &body.fully_read {
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
content: ruma::events::fully_read::FullyReadEventContent {
event_id: event.clone(),
},
content: ruma::events::fully_read::FullyReadEventContent { event_id: event.clone() },
};
services
@ -55,13 +54,10 @@ pub(crate) async fn set_read_marker_route(
event.to_owned(),
BTreeMap::from_iter([(
ReceiptType::Read,
BTreeMap::from_iter([(
sender_user.to_owned(),
ruma::events::receipt::Receipt {
ts: Some(MilliSecondsSinceUnixEpoch::now()),
thread: ReceiptThread::Unthreaded,
},
)]),
BTreeMap::from_iter([(sender_user.to_owned(), ruma::events::receipt::Receipt {
ts: Some(MilliSecondsSinceUnixEpoch::now()),
thread: ReceiptThread::Unthreaded,
})]),
)]),
)]);
@ -88,7 +84,9 @@ pub(crate) async fn set_read_marker_route(
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
let PduCount::Normal(count) = count else {
return Err!(Request(InvalidParam("Event is a backfilled PDU and cannot be marked as read.")));
return Err!(Request(InvalidParam(
"Event is a backfilled PDU and cannot be marked as read."
)));
};
services
@ -104,7 +102,8 @@ pub(crate) async fn set_read_marker_route(
///
/// Sets private read marker and public read receipt EDU.
pub(crate) async fn create_receipt_route(
State(services): State<crate::State>, body: Ruma<create_receipt::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_receipt::v3::Request>,
) -> Result<create_receipt::v3::Response> {
let sender_user = body.sender_user();
@ -119,7 +118,7 @@ pub(crate) async fn create_receipt_route(
}
match body.receipt_type {
create_receipt::v3::ReceiptType::FullyRead => {
| create_receipt::v3::ReceiptType::FullyRead => {
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
content: ruma::events::fully_read::FullyReadEventContent {
event_id: body.event_id.clone(),
@ -135,7 +134,7 @@ pub(crate) async fn create_receipt_route(
)
.await?;
},
create_receipt::v3::ReceiptType::Read => {
| create_receipt::v3::ReceiptType::Read => {
let receipt_content = BTreeMap::from_iter([(
body.event_id.clone(),
BTreeMap::from_iter([(
@ -163,7 +162,7 @@ pub(crate) async fn create_receipt_route(
)
.await;
},
create_receipt::v3::ReceiptType::ReadPrivate => {
| create_receipt::v3::ReceiptType::ReadPrivate => {
let count = services
.rooms
.timeline
@ -172,7 +171,9 @@ pub(crate) async fn create_receipt_route(
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
let PduCount::Normal(count) = count else {
return Err!(Request(InvalidParam("Event is a backfilled PDU and cannot be marked as read.")));
return Err!(Request(InvalidParam(
"Event is a backfilled PDU and cannot be marked as read."
)));
};
services
@ -180,12 +181,11 @@ pub(crate) async fn create_receipt_route(
.read_receipt
.private_read_set(&body.room_id, sender_user, count);
},
_ => {
| _ =>
return Err!(Request(InvalidParam(warn!(
"Received unknown read receipt type: {}",
&body.receipt_type
))))
},
)))),
}
Ok(create_receipt::v3::Response {})

View file

@ -1,5 +1,7 @@
use axum::extract::State;
use ruma::{api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent};
use ruma::{
api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent,
};
use crate::{service::pdu::PduBuilder, Result, Ruma};
@ -9,7 +11,8 @@ use crate::{service::pdu::PduBuilder, Result, Ruma};
///
/// - TODO: Handle txn id
pub(crate) async fn redact_event_route(
State(services): State<crate::State>, body: Ruma<redact_event::v3::Request>,
State(services): State<crate::State>,
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;
@ -35,7 +38,5 @@ pub(crate) async fn redact_event_route(
drop(state_lock);
Ok(redact_event::v3::Response {
event_id: event_id.into(),
})
Ok(redact_event::v3::Response { event_id: event_id.into() })
}

View file

@ -8,7 +8,8 @@ use futures::StreamExt;
use ruma::{
api::{
client::relations::{
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
get_relating_events, get_relating_events_with_rel_type,
get_relating_events_with_rel_type_and_event_type,
},
Direction,
},
@ -21,7 +22,8 @@ use crate::Ruma;
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
State(services): State<crate::State>,
body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
paginate_relations_with_filter(
&services,
@ -47,7 +49,8 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
pub(crate) async fn get_relating_events_with_rel_type_route(
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type::v1::Request>,
State(services): State<crate::State>,
body: Ruma<get_relating_events_with_rel_type::v1::Request>,
) -> Result<get_relating_events_with_rel_type::v1::Response> {
paginate_relations_with_filter(
&services,
@ -73,7 +76,8 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
pub(crate) async fn get_relating_events_route(
State(services): State<crate::State>, body: Ruma<get_relating_events::v1::Request>,
State(services): State<crate::State>,
body: Ruma<get_relating_events::v1::Request>,
) -> Result<get_relating_events::v1::Response> {
paginate_relations_with_filter(
&services,
@ -93,16 +97,24 @@ pub(crate) async fn get_relating_events_route(
#[allow(clippy::too_many_arguments)]
async fn paginate_relations_with_filter(
services: &Services, sender_user: &UserId, room_id: &RoomId, target: &EventId,
filter_event_type: Option<TimelineEventType>, filter_rel_type: Option<RelationType>, from: Option<&str>,
to: Option<&str>, limit: Option<UInt>, recurse: bool, dir: Direction,
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
target: &EventId,
filter_event_type: Option<TimelineEventType>,
filter_rel_type: Option<RelationType>,
from: Option<&str>,
to: Option<&str>,
limit: Option<UInt>,
recurse: bool,
dir: Direction,
) -> Result<get_relating_events::v1::Response> {
let start: PduCount = from
.map(str::parse)
.transpose()?
.unwrap_or_else(|| match dir {
Direction::Forward => PduCount::min(),
Direction::Backward => PduCount::max(),
| Direction::Forward => PduCount::min(),
| Direction::Backward => PduCount::max(),
});
let to: Option<PduCount> = to.map(str::parse).flat_ok();
@ -115,11 +127,7 @@ async fn paginate_relations_with_filter(
.min(100);
// Spec (v1.10) recommends depth of at least 3
let depth: u8 = if recurse {
3
} else {
1
};
let depth: u8 = if recurse { 3 } else { 1 };
let events: Vec<PdusIterItem> = services
.rooms
@ -145,8 +153,8 @@ async fn paginate_relations_with_filter(
.await;
let next_batch = match dir {
Direction::Forward => events.last(),
Direction::Backward => events.first(),
| Direction::Forward => events.last(),
| Direction::Backward => events.first(),
}
.map(at!(0))
.as_ref()
@ -164,7 +172,11 @@ async fn paginate_relations_with_filter(
})
}
async fn visibility_filter(services: &Services, sender_user: &UserId, item: PdusIterItem) -> Option<PdusIterItem> {
async fn visibility_filter(
services: &Services,
sender_user: &UserId,
item: PdusIterItem,
) -> Option<PdusIterItem> {
let (_, pdu) = &item;
services

View file

@ -25,7 +25,8 @@ use crate::{
/// Reports an abusive room to homeserver admins
#[tracing::instrument(skip_all, fields(%client), name = "report_room")]
pub(crate) async fn report_room_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<report_room::v3::Request>,
) -> Result<report_room::v3::Response> {
// user authentication
@ -78,14 +79,16 @@ pub(crate) async fn report_room_route(
/// Reports an inappropriate event to homeserver admins
#[tracing::instrument(skip_all, fields(%client), name = "report_event")]
pub(crate) async fn report_event_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<report_content::v3::Request>,
) -> Result<report_content::v3::Response> {
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
info!(
"Received event report by user {sender_user} for room {} and event ID {}, with reason: \"{}\"",
"Received event report by user {sender_user} for room {} and event ID {}, with reason: \
\"{}\"",
body.room_id,
body.event_id,
body.reason.as_deref().unwrap_or("")
@ -114,8 +117,8 @@ pub(crate) async fn report_event_route(
services
.admin
.send_message(message::RoomMessageEventContent::text_markdown(format!(
"@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: {}\n\nReport Score: \
{}\nReport Reason: {}",
"@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: \
{}\n\nReport Score: {}\nReport Reason: {}",
sender_user.to_owned(),
pdu.event_id,
pdu.room_id,
@ -136,10 +139,18 @@ pub(crate) async fn report_event_route(
/// check if report reasoning is less than or equal to 750 characters
/// check if reporting user is in the reporting room
async fn is_event_report_valid(
services: &Services, event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: Option<&String>,
score: Option<ruma::Int>, pdu: &PduEvent,
services: &Services,
event_id: &EventId,
room_id: &RoomId,
sender_user: &UserId,
reason: Option<&String>,
score: Option<ruma::Int>,
pdu: &PduEvent,
) -> Result<()> {
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
debug_info!(
"Checking if report from user {sender_user} for event {event_id} in room {room_id} is \
valid"
);
if room_id != pdu.room_id {
return Err(Error::BadRequest(
@ -183,6 +194,9 @@ async fn is_event_report_valid(
/// enumerating for potential events existing in our server.
async fn delay_response() {
let time_to_wait = rand::thread_rng().gen_range(2..5);
debug_info!("Got successful /report request, waiting {time_to_wait} seconds before sending successful response.");
debug_info!(
"Got successful /report request, waiting {time_to_wait} seconds before sending \
successful response."
);
sleep(Duration::from_secs(time_to_wait)).await;
}

View file

@ -12,7 +12,8 @@ use crate::Ruma;
/// - 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>,
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");

View file

@ -24,7 +24,8 @@ use ruma::{
},
int,
serde::{JsonObject, Raw},
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomVersionId,
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId,
RoomVersionId,
};
use serde_json::{json, value::to_raw_value};
use service::{appservice::RegistrationInfo, Services};
@ -49,7 +50,8 @@ use crate::{client::invite_helper, Ruma};
/// - Send invite events
#[allow(clippy::large_stack_frames)]
pub(crate) async fn create_room_route(
State(services): State<crate::State>, body: Ruma<create_room::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_room::v3::Request>,
) -> Result<create_room::v3::Response> {
use create_room::v3::RoomPreset;
@ -59,7 +61,10 @@ pub(crate) async fn create_room_route(
&& body.appservice_info.is_none()
&& !services.users.is_admin(sender_user).await
{
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Room creation has been disabled.",
));
}
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
@ -91,8 +96,8 @@ pub(crate) async fn create_room_route(
services
.admin
.send_text(&format!(
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
\"lockdown_public_room_directory\" is enabled",
"Non-admin user {sender_user} tried to publish {0} to the room directory \
while \"lockdown_public_room_directory\" is enabled",
&room_id
))
.await;
@ -115,7 +120,7 @@ pub(crate) async fn create_room_route(
};
let room_version = match body.room_version.clone() {
Some(room_version) => {
| Some(room_version) =>
if services.server.supported_room_version(&room_version) {
room_version
} else {
@ -123,13 +128,12 @@ pub(crate) async fn create_room_route(
ErrorKind::UnsupportedRoomVersion,
"This server does not support that room version.",
));
}
},
None => services.server.config.default_room_version.clone(),
},
| None => services.server.config.default_room_version.clone(),
};
let create_content = match &body.creation_content {
Some(content) => {
| Some(content) => {
use RoomVersionId::*;
let mut content = content
@ -139,7 +143,7 @@ pub(crate) async fn create_room_route(
Error::bad_database("Failed to deserialise content as canonical JSON.")
})?;
match room_version {
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
content.insert(
"creator".into(),
json!(&sender_user).try_into().map_err(|e| {
@ -148,24 +152,25 @@ pub(crate) async fn create_room_route(
})?,
);
},
_ => {
| _ => {
// V11+ removed the "creator" key
},
}
content.insert(
"room_version".into(),
json!(room_version.as_str())
.try_into()
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?,
json!(room_version.as_str()).try_into().map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
})?,
);
content
},
None => {
| None => {
use RoomVersionId::*;
let content = match room_version {
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
_ => RoomCreateEventContent::new_v11(),
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
RoomCreateEventContent::new_v1(sender_user.clone()),
| _ => RoomCreateEventContent::new_v11(),
};
let mut content = serde_json::from_str::<CanonicalJsonObject>(
to_raw_value(&content)
@ -190,7 +195,8 @@ pub(crate) async fn create_room_route(
.build_and_append_pdu(
PduBuilder {
event_type: TimelineEventType::RoomCreate,
content: to_raw_value(&create_content).expect("create event content serialization"),
content: to_raw_value(&create_content)
.expect("create event content serialization"),
state_key: Some(String::new()),
..Default::default()
},
@ -206,16 +212,13 @@ pub(crate) async fn create_room_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
sender_user.to_string(),
&RoomMemberEventContent {
displayname: services.users.displayname(sender_user).await.ok(),
avatar_url: services.users.avatar_url(sender_user).await.ok(),
blurhash: services.users.blurhash(sender_user).await.ok(),
is_direct: Some(body.is_direct),
..RoomMemberEventContent::new(MembershipState::Join)
},
),
PduBuilder::state(sender_user.to_string(), &RoomMemberEventContent {
displayname: services.users.displayname(sender_user).await.ok(),
avatar_url: services.users.avatar_url(sender_user).await.ok(),
blurhash: services.users.blurhash(sender_user).await.ok(),
is_direct: Some(body.is_direct),
..RoomMemberEventContent::new(MembershipState::Join)
}),
sender_user,
&room_id,
&state_lock,
@ -227,8 +230,8 @@ pub(crate) async fn create_room_route(
// Figure out preset. We need it for preset specific events
let preset = body.preset.clone().unwrap_or(match &body.visibility {
room::Visibility::Public => RoomPreset::PublicChat,
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
| room::Visibility::Public => RoomPreset::PublicChat,
| _ => RoomPreset::PrivateChat, // Room visibility should not be custom
});
let mut users = BTreeMap::from_iter([(sender_user.clone(), int!(100))]);
@ -236,7 +239,9 @@ pub(crate) async fn create_room_route(
if preset == RoomPreset::TrustedPrivateChat {
for invite in &body.invite {
if services.users.user_is_ignored(sender_user, invite).await {
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
return Err!(Request(Forbidden(
"You cannot invite users you have ignored to rooms."
)));
} else if services.users.user_is_ignored(invite, sender_user).await {
// silently drop the invite to the recipient if they've been ignored by the
// sender, pretend it worked
@ -247,8 +252,11 @@ pub(crate) async fn create_room_route(
}
}
let power_levels_content =
default_power_levels_content(body.power_level_content_override.as_ref(), &body.visibility, users)?;
let power_levels_content = default_power_levels_content(
body.power_level_content_override.as_ref(),
&body.visibility,
users,
)?;
services
.rooms
@ -256,7 +264,8 @@ pub(crate) async fn create_room_route(
.build_and_append_pdu(
PduBuilder {
event_type: TimelineEventType::RoomPowerLevels,
content: to_raw_value(&power_levels_content).expect("serialized power_levels event content"),
content: to_raw_value(&power_levels_content)
.expect("serialized power_levels event content"),
state_key: Some(String::new()),
..Default::default()
},
@ -273,13 +282,10 @@ pub(crate) async fn create_room_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
String::new(),
&RoomCanonicalAliasEventContent {
alias: Some(room_alias_id.to_owned()),
alt_aliases: vec![],
},
),
PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
alias: Some(room_alias_id.to_owned()),
alt_aliases: vec![],
}),
sender_user,
&room_id,
&state_lock,
@ -298,9 +304,9 @@ pub(crate) async fn create_room_route(
PduBuilder::state(
String::new(),
&RoomJoinRulesEventContent::new(match preset {
RoomPreset::PublicChat => JoinRule::Public,
| RoomPreset::PublicChat => JoinRule::Public,
// according to spec "invite" is the default
_ => JoinRule::Invite,
| _ => JoinRule::Invite,
}),
),
sender_user,
@ -334,8 +340,8 @@ pub(crate) async fn create_room_route(
PduBuilder::state(
String::new(),
&RoomGuestAccessEventContent::new(match preset {
RoomPreset::PublicChat => GuestAccess::Forbidden,
_ => GuestAccess::CanJoin,
| RoomPreset::PublicChat => GuestAccess::Forbidden,
| _ => GuestAccess::CanJoin,
}),
),
sender_user,
@ -367,7 +373,9 @@ pub(crate) async fn create_room_route(
pdu_builder.state_key.get_or_insert_with(String::new);
// Silently skip encryption events if they are not allowed
if pdu_builder.event_type == TimelineEventType::RoomEncryption && !services.globals.allow_encryption() {
if pdu_builder.event_type == TimelineEventType::RoomEncryption
&& !services.globals.allow_encryption()
{
continue;
}
@ -399,12 +407,7 @@ pub(crate) async fn create_room_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
String::new(),
&RoomTopicEventContent {
topic: topic.clone(),
},
),
PduBuilder::state(String::new(), &RoomTopicEventContent { topic: topic.clone() }),
sender_user,
&room_id,
&state_lock,
@ -417,16 +420,19 @@ pub(crate) async fn create_room_route(
drop(state_lock);
for user_id in &body.invite {
if services.users.user_is_ignored(sender_user, user_id).await {
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
return Err!(Request(Forbidden(
"You cannot invite users you have ignored to rooms."
)));
} else if services.users.user_is_ignored(user_id, sender_user).await {
// silently drop the invite to the recipient if they've been ignored by the
// sender, pretend it worked
continue;
}
if let Err(e) = invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct)
.boxed()
.await
if let Err(e) =
invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct)
.boxed()
.await
{
warn!(%e, "Failed to send invite");
}
@ -446,7 +452,10 @@ pub(crate) async fn create_room_route(
if services.globals.config.admin_room_notices {
services
.admin
.send_text(&format!("{sender_user} made {} public to the room directory", &room_id))
.send_text(&format!(
"{sender_user} made {} public to the room directory",
&room_id
))
.await;
}
info!("{sender_user} made {0} public to the room directory", &room_id);
@ -459,21 +468,24 @@ pub(crate) async fn create_room_route(
/// creates the power_levels_content for the PDU builder
fn default_power_levels_content(
power_level_content_override: Option<&Raw<RoomPowerLevelsEventContent>>, visibility: &room::Visibility,
power_level_content_override: Option<&Raw<RoomPowerLevelsEventContent>>,
visibility: &room::Visibility,
users: BTreeMap<OwnedUserId, Int>,
) -> Result<serde_json::Value> {
let mut power_levels_content = serde_json::to_value(RoomPowerLevelsEventContent {
users,
..Default::default()
})
.expect("event is valid, we just created it");
let mut power_levels_content =
serde_json::to_value(RoomPowerLevelsEventContent { users, ..Default::default() })
.expect("event is valid, we just created it");
// secure proper defaults of sensitive/dangerous permissions that moderators
// (power level 50) should not have easy access to
power_levels_content["events"]["m.room.power_levels"] = serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.server_acl"] = serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.tombstone"] = serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.encryption"] = serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.power_levels"] =
serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.server_acl"] =
serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.tombstone"] =
serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.encryption"] =
serde_json::to_value(100).expect("100 is valid Value");
power_levels_content["events"]["m.room.history_visibility"] =
serde_json::to_value(100).expect("100 is valid Value");
@ -481,14 +493,18 @@ fn default_power_levels_content(
// useful in read-only announcement rooms that post a public poll.
power_levels_content["events"]["org.matrix.msc3381.poll.response"] =
serde_json::to_value(0).expect("0 is valid Value");
power_levels_content["events"]["m.poll.response"] = serde_json::to_value(0).expect("0 is valid Value");
power_levels_content["events"]["m.poll.response"] =
serde_json::to_value(0).expect("0 is valid Value");
// synapse does this too. clients do not expose these permissions. it prevents
// default users from calling public rooms, for obvious reasons.
if *visibility == room::Visibility::Public {
power_levels_content["events"]["m.call.invite"] = serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["m.call"] = serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["m.call.member"] = serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["m.call.invite"] =
serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["m.call"] =
serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["m.call.member"] =
serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["org.matrix.msc3401.call"] =
serde_json::to_value(50).expect("50 is valid Value");
power_levels_content["events"]["org.matrix.msc3401.call.member"] =
@ -497,7 +513,9 @@ fn default_power_levels_content(
if let Some(power_level_content_override) = power_level_content_override {
let json: JsonObject = serde_json::from_str(power_level_content_override.json().get())
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override."))?;
.map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
})?;
for (key, value) in json {
power_levels_content[key] = value;
@ -509,14 +527,16 @@ fn default_power_levels_content(
/// if a room is being created with a room alias, run our checks
async fn room_alias_check(
services: &Services, room_alias_name: &str, appservice_info: Option<&RegistrationInfo>,
services: &Services,
room_alias_name: &str,
appservice_info: Option<&RegistrationInfo>,
) -> Result<OwnedRoomAliasId> {
// Basic checks on the room alias validity
if room_alias_name.contains(':') {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Room alias contained `:` which is not allowed. Please note that this expects a localpart, not the full \
room alias.",
"Room alias contained `:` which is not allowed. Please note that this expects a \
localpart, not the full room alias.",
));
} else if room_alias_name.contains(char::is_whitespace) {
return Err(Error::BadRequest(
@ -534,8 +554,11 @@ async fn room_alias_check(
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
}
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services.globals.config.server_name))
.map_err(|e| {
let full_room_alias = RoomAliasId::parse(format!(
"#{}:{}",
room_alias_name, services.globals.config.server_name
))
.map_err(|e| {
info!("Failed to parse room alias {room_alias_name}: {e}");
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
})?;
@ -552,14 +575,20 @@ async fn room_alias_check(
if let Some(info) = appservice_info {
if !info.aliases.is_match(full_room_alias.as_str()) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
return Err(Error::BadRequest(
ErrorKind::Exclusive,
"Room alias is not in namespace.",
));
}
} else if services
.appservice
.is_exclusive_alias(&full_room_alias)
.await
{
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
return Err(Error::BadRequest(
ErrorKind::Exclusive,
"Room alias reserved by appservice.",
));
}
debug_info!("Full room alias: {full_room_alias}");
@ -581,8 +610,8 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
if custom_room_id.contains(':') {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Custom room ID contained `:` which is not allowed. Please note that this expects a localpart, not the \
full room ID.",
"Custom room ID contained `:` which is not allowed. Please note that this expects a \
localpart, not the full room ID.",
));
} else if custom_room_id.contains(char::is_whitespace) {
return Err(Error::BadRequest(
@ -596,7 +625,10 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
debug_info!("Full custom room ID: {full_room_id}");
RoomId::parse(full_room_id).map_err(|e| {
info!("User attempted to create room with custom room ID {custom_room_id} but failed parsing: {e}");
info!(
"User attempted to create room with custom room ID {custom_room_id} but failed \
parsing: {e}"
);
Error::BadRequest(ErrorKind::InvalidParam, "Custom room ID could not be parsed")
})
}

View file

@ -9,7 +9,8 @@ use crate::{client::ignored_filter, Ruma};
///
/// Gets a single event.
pub(crate) async fn get_room_event_route(
State(services): State<crate::State>, ref body: Ruma<get_room_event::v3::Request>,
State(services): State<crate::State>,
ref body: Ruma<get_room_event::v3::Request>,
) -> Result<get_room_event::v3::Response> {
let event = services
.rooms
@ -47,7 +48,5 @@ pub(crate) async fn get_room_event_route(
let event = event.to_room_event();
Ok(get_room_event::v3::Response {
event,
})
Ok(get_room_event::v3::Response { event })
}

View file

@ -8,7 +8,8 @@ use crate::Ruma;
const LIMIT_MAX: usize = 100;
pub(crate) async fn room_initial_sync_route(
State(services): State<crate::State>, body: Ruma<Request>,
State(services): State<crate::State>,
body: Ruma<Request>,
) -> Result<Response> {
let room_id = &body.room_id;

View file

@ -43,7 +43,8 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
/// - 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>,
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");
@ -72,13 +73,10 @@ pub(crate) async fn upgrade_room_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
String::new(),
&RoomTombstoneEventContent {
body: "This room has been replaced".to_owned(),
replacement_room: replacement_room.clone(),
},
),
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,
@ -108,7 +106,7 @@ pub(crate) async fn upgrade_room_route(
{
use RoomVersionId::*;
match body.new_version {
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
create_event_content.insert(
"creator".into(),
json!(&sender_user).try_into().map_err(|e| {
@ -117,7 +115,7 @@ pub(crate) async fn upgrade_room_route(
})?,
);
},
_ => {
| _ => {
// "creator" key no longer exists in V11+ rooms
create_event_content.remove("creator");
},
@ -154,7 +152,8 @@ pub(crate) async fn upgrade_room_route(
.build_and_append_pdu(
PduBuilder {
event_type: TimelineEventType::RoomCreate,
content: to_raw_value(&create_event_content).expect("event is valid, we just created it"),
content: to_raw_value(&create_event_content)
.expect("event is valid, we just created it"),
unsigned: None,
state_key: Some(String::new()),
redacts: None,
@ -203,8 +202,8 @@ pub(crate) async fn upgrade_room_route(
.room_state_get(&body.room_id, event_type, "")
.await
{
Ok(v) => v.content.clone(),
Err(_) => continue, // Skipping missing events.
| Ok(v) => v.content.clone(),
| Err(_) => continue, // Skipping missing events.
};
services
@ -258,7 +257,9 @@ pub(crate) async fn upgrade_room_route(
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"))))?,
.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
@ -267,14 +268,11 @@ pub(crate) async fn upgrade_room_route(
.rooms
.timeline
.build_and_append_pdu(
PduBuilder::state(
String::new(),
&RoomPowerLevelsEventContent {
events_default: new_level,
invite: new_level,
..power_levels_event_content
},
),
PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
events_default: new_level,
invite: new_level,
..power_levels_event_content
}),
sender_user,
&body.room_id,
&state_lock,
@ -284,7 +282,5 @@ pub(crate) async fn upgrade_room_route(
drop(state_lock);
// Return the replacement room id
Ok(upgrade_room::v3::Response {
replacement_room,
})
Ok(upgrade_room::v3::Response { replacement_room })
}

View file

@ -35,7 +35,10 @@ const BATCH_MAX: usize = 20;
///
/// - Only works if the user is currently joined to the room (TODO: Respect
/// history visibility)
pub(crate) async fn search_events_route(State(services): State<crate::State>, body: Ruma<Request>) -> Result<Response> {
pub(crate) async fn search_events_route(
State(services): State<crate::State>,
body: Ruma<Request>,
) -> Result<Response> {
let sender_user = body.sender_user();
let next_batch = body.next_batch.as_deref();
let room_events_result: OptionFuture<_> = body
@ -56,7 +59,10 @@ pub(crate) async fn search_events_route(State(services): State<crate::State>, bo
#[allow(clippy::map_unwrap_or)]
async fn category_room_events(
services: &Services, sender_user: &UserId, next_batch: Option<&str>, criteria: &Criteria,
services: &Services,
sender_user: &UserId,
next_batch: Option<&str>,
criteria: &Criteria,
) -> Result<ResultRoomEvents> {
let filter = &criteria.filter;
@ -186,11 +192,17 @@ async fn procure_room_state(services: &Services, room_id: &RoomId) -> Result<Roo
Ok(state_events)
}
async fn check_room_visible(services: &Services, user_id: &UserId, room_id: &RoomId, search: &Criteria) -> Result {
async fn check_room_visible(
services: &Services,
user_id: &UserId,
room_id: &RoomId,
search: &Criteria,
) -> Result {
let check_visible = search.filter.rooms.is_some();
let check_state = check_visible && search.include_state.is_some_and(is_true!());
let is_joined = !check_visible || services.rooms.state_cache.is_joined(user_id, room_id).await;
let is_joined =
!check_visible || services.rooms.state_cache.is_joined(user_id, room_id).await;
let state_visible = !check_state
|| services

View file

@ -17,14 +17,17 @@ use crate::{service::pdu::PduBuilder, utils, Result, Ruma};
/// - Tries to send the event into the room, auth rules will determine if it is
/// allowed
pub(crate) async fn send_message_event_route(
State(services): State<crate::State>, body: Ruma<send_message_event::v3::Request>,
State(services): State<crate::State>,
body: Ruma<send_message_event::v3::Request>,
) -> Result<send_message_event::v3::Response> {
let sender_user = body.sender_user();
let sender_device = body.sender_device.as_deref();
let appservice_info = body.appservice_info.as_ref();
// Forbid m.room.encrypted if encryption is disabled
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.globals.allow_encryption() {
if MessageLikeEventType::RoomEncrypted == body.event_type
&& !services.globals.allow_encryption()
{
return Err!(Request(Forbidden("Encryption has been disabled")));
}
@ -60,8 +63,8 @@ pub(crate) async fn send_message_event_route(
let mut unsigned = BTreeMap::new();
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
let content =
from_str(body.body.body.json().get()).map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
let content = from_str(body.body.body.json().get())
.map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
let event_id = services
.rooms
@ -80,13 +83,14 @@ pub(crate) async fn send_message_event_route(
)
.await?;
services
.transaction_ids
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes());
services.transaction_ids.add_txnid(
sender_user,
sender_device,
&body.txn_id,
event_id.as_bytes(),
);
drop(state_lock);
Ok(send_message_event::v3::Response {
event_id: event_id.into(),
})
Ok(send_message_event::v3::Response { event_id: event_id.into() })
}

View file

@ -37,7 +37,8 @@ struct Claims {
/// the `type` field when logging in.
#[tracing::instrument(skip_all, fields(%client), name = "login")]
pub(crate) async fn get_login_types_route(
InsecureClientIp(client): InsecureClientIp, _body: Ruma<get_login_types::v3::Request>,
InsecureClientIp(client): InsecureClientIp,
_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(PasswordLoginType::default()),
@ -61,13 +62,15 @@ pub(crate) async fn get_login_types_route(
/// supported login types.
#[tracing::instrument(skip_all, fields(%client), name = "login")]
pub(crate) async fn login_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<login::v3::Request>,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<login::v3::Request>,
) -> Result<login::v3::Response> {
// Validate login method
// TODO: Other login methods
let user_id = match &body.login_info {
#[allow(deprecated)]
login::v3::LoginInfo::Password(login::v3::Password {
| login::v3::LoginInfo::Password(login::v3::Password {
identifier,
password,
user,
@ -75,7 +78,10 @@ pub(crate) async fn login_route(
}) => {
debug!("Got password login type");
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
UserId::parse_with_server_name(
user_id.to_lowercase(),
services.globals.server_name(),
)
} else if let Some(user) = user {
UserId::parse(user)
} else {
@ -100,22 +106,29 @@ pub(crate) async fn login_route(
user_id
},
login::v3::LoginInfo::Token(login::v3::Token {
token,
}) => {
| login::v3::LoginInfo::Token(login::v3::Token { token }) => {
debug!("Got token login type");
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
let token =
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
.map_err(|e| {
warn!("Failed to parse JWT token from user logging in: {e}");
Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid.")
})?;
let token = jsonwebtoken::decode::<Claims>(
token,
jwt_decoding_key,
&jsonwebtoken::Validation::default(),
)
.map_err(|e| {
warn!("Failed to parse JWT token from user logging in: {e}");
Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid.")
})?;
let username = token.claims.sub.to_lowercase();
UserId::parse_with_server_name(username, services.globals.server_name())
.map_err(|e| err!(Request(InvalidUsername(debug_error!(?e, "Failed to parse login username")))))?
UserId::parse_with_server_name(username, services.globals.server_name()).map_err(
|e| {
err!(Request(InvalidUsername(debug_error!(
?e,
"Failed to parse login username"
))))
},
)?
} else {
return Err!(Request(Unknown(
"Token login is not supported (server has no jwt decoding key)."
@ -123,13 +136,16 @@ pub(crate) async fn login_route(
}
},
#[allow(deprecated)]
login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
| login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
identifier,
user,
}) => {
debug!("Got appservice login type");
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
UserId::parse_with_server_name(
user_id.to_lowercase(),
services.globals.server_name(),
)
} else if let Some(user) = user {
UserId::parse(user)
} else {
@ -143,18 +159,27 @@ pub(crate) async fn login_route(
if let Some(ref info) = body.appservice_info {
if !info.is_user_match(&user_id) {
return Err(Error::BadRequest(ErrorKind::Exclusive, "User is not in namespace."));
return Err(Error::BadRequest(
ErrorKind::Exclusive,
"User is not in namespace.",
));
}
} else {
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
return Err(Error::BadRequest(
ErrorKind::MissingToken,
"Missing appservice token.",
));
}
user_id
},
_ => {
| _ => {
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
debug!("JSON body: {:?}", &body.json_body);
return Err(Error::BadRequest(ErrorKind::Unknown, "Unsupported or unknown login type."));
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Unsupported or unknown login type.",
));
},
};
@ -233,7 +258,9 @@ pub(crate) async fn login_route(
/// - Triggers device list updates
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
pub(crate) async fn logout_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<logout::v3::Request>,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<logout::v3::Request>,
) -> Result<logout::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");
@ -261,7 +288,8 @@ pub(crate) async fn logout_route(
/// user.
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
pub(crate) async fn logout_all_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<logout_all::v3::Request>,
) -> Result<logout_all::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -13,7 +13,8 @@ use crate::{service::rooms::spaces::PaginationToken, Error, Result, Ruma};
/// Paginates over the space tree in a depth-first manner to locate child rooms
/// of a given space.
pub(crate) async fn get_hierarchy_route(
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
State(services): State<crate::State>,
body: Ruma<get_hierarchy::v1::Request>,
) -> Result<get_hierarchy::v1::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");

View file

@ -31,7 +31,8 @@ use crate::{Ruma, RumaResponse};
/// allowed
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
pub(crate) async fn send_state_event_for_key_route(
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
State(services): State<crate::State>,
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");
@ -63,7 +64,8 @@ pub(crate) async fn send_state_event_for_key_route(
/// allowed
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
pub(crate) async fn send_state_event_for_empty_key_route(
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
State(services): State<crate::State>,
body: Ruma<send_state_event::v3::Request>,
) -> Result<RumaResponse<send_state_event::v3::Response>> {
send_state_event_for_key_route(State(services), body)
.await
@ -77,7 +79,8 @@ pub(crate) async fn send_state_event_for_empty_key_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_route(
State(services): State<crate::State>, body: Ruma<get_state_events::v3::Request>,
State(services): State<crate::State>,
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");
@ -111,7 +114,8 @@ pub(crate) async fn get_state_events_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_for_key_route(
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
State(services): State<crate::State>,
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");
@ -157,7 +161,8 @@ pub(crate) async fn get_state_events_for_key_route(
/// - If not joined: Only works if current room history visibility is world
/// readable
pub(crate) async fn get_state_events_for_empty_key_route(
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_state_events_for_key::v3::Request>,
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
get_state_events_for_key_route(State(services), body)
.await
@ -165,8 +170,13 @@ pub(crate) async fn get_state_events_for_empty_key_route(
}
async fn send_state_event_for_key_helper(
services: &Services, sender: &UserId, room_id: &RoomId, event_type: &StateEventType,
json: &Raw<AnyStateEventContent>, state_key: String, timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
services: &Services,
sender: &UserId,
room_id: &RoomId,
event_type: &StateEventType,
json: &Raw<AnyStateEventContent>,
state_key: String,
timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
) -> Result<Arc<EventId>> {
allowed_to_send_state_event(services, room_id, event_type, json).await?;
let state_lock = services.rooms.state.mutex.lock(room_id).await;
@ -191,20 +201,27 @@ async fn send_state_event_for_key_helper(
}
async fn allowed_to_send_state_event(
services: &Services, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
services: &Services,
room_id: &RoomId,
event_type: &StateEventType,
json: &Raw<AnyStateEventContent>,
) -> Result {
match event_type {
// Forbid m.room.encryption if encryption is disabled
StateEventType::RoomEncryption => {
| StateEventType::RoomEncryption =>
if !services.globals.allow_encryption() {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
}
},
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Encryption has been disabled",
));
},
// admin room is a sensitive room, it should not ever be made public
StateEventType::RoomJoinRules => {
| StateEventType::RoomJoinRules => {
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
if admin_room_id == room_id {
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
if let Ok(join_rule) =
serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get())
{
if join_rule.join_rule == JoinRule::Public {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
@ -216,16 +233,20 @@ async fn allowed_to_send_state_event(
}
},
// admin room is a sensitive room, it should not ever be made world readable
StateEventType::RoomHistoryVisibility => {
| StateEventType::RoomHistoryVisibility => {
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
if admin_room_id == room_id {
if let Ok(visibility_content) =
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
if let Ok(visibility_content) = serde_json::from_str::<
RoomHistoryVisibilityEventContent,
>(json.json().get())
{
if visibility_content.history_visibility == HistoryVisibility::WorldReadable {
if visibility_content.history_visibility
== HistoryVisibility::WorldReadable
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Admin room is not allowed to be made world readable (public room history).",
"Admin room is not allowed to be made world readable (public \
room history).",
));
}
}
@ -233,8 +254,10 @@ async fn allowed_to_send_state_event(
}
},
// TODO: allow alias if it previously existed
StateEventType::RoomCanonicalAlias => {
if let Ok(canonical_alias) = serde_json::from_str::<RoomCanonicalAliasEventContent>(json.json().get()) {
| StateEventType::RoomCanonicalAlias => {
if let Ok(canonical_alias) =
serde_json::from_str::<RoomCanonicalAliasEventContent>(json.json().get())
{
let mut aliases = canonical_alias.alt_aliases.clone();
if let Some(alias) = canonical_alias.alias {
@ -243,7 +266,9 @@ async fn allowed_to_send_state_event(
for alias in aliases {
if !services.globals.server_is_ours(alias.server_name()) {
return Err!(Request(Forbidden("canonical_alias must be for this server")));
return Err!(Request(Forbidden(
"canonical_alias must be for this server"
)));
}
if !services
@ -255,13 +280,14 @@ async fn allowed_to_send_state_event(
// Make sure it's the right room
{
return Err!(Request(Forbidden(
"You are only allowed to send canonical_alias events when its aliases already exist"
"You are only allowed to send canonical_alias events when its \
aliases already exist"
)));
}
}
}
},
_ => (),
| _ => (),
}
Ok(())

View file

@ -12,8 +12,12 @@ pub(crate) use self::{v3::sync_events_route, v4::sync_events_v4_route};
use crate::{service::Services, Error, PduEvent, Result};
async fn load_timeline(
services: &Services, sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount,
next_batch: Option<PduCount>, limit: usize,
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
roomsincecount: PduCount,
next_batch: Option<PduCount>,
limit: usize,
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
let last_timeline_count = services
.rooms
@ -51,7 +55,10 @@ async fn load_timeline(
}
async fn share_encrypted_room(
services: &Services, sender_user: &UserId, user_id: &UserId, ignore_room: Option<&RoomId>,
services: &Services,
sender_user: &UserId,
user_id: &UserId,
ignore_room: Option<&RoomId>,
) -> bool {
services
.rooms

View file

@ -32,8 +32,9 @@ use ruma::{
sync::sync_events::{
self,
v3::{
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
RoomAccountData, RoomSummary, Rooms, State as RoomState, Timeline, ToDevice,
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom,
LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State as RoomState,
Timeline, ToDevice,
},
DeviceLists, UnreadNotificationsCount,
},
@ -107,7 +108,8 @@ type PresenceUpdates = HashMap<OwnedUserId, PresenceEvent>;
)
)]
pub(crate) async fn sync_events_route(
State(services): State<crate::State>, body: Ruma<sync_events::v3::Request>,
State(services): State<crate::State>,
body: Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
let (sender_user, sender_device) = body.sender();
@ -127,9 +129,9 @@ pub(crate) async fn sync_events_route(
// Load filter
let filter = match body.body.filter.as_ref() {
None => FilterDefinition::default(),
Some(Filter::FilterDefinition(ref filter)) => filter.clone(),
Some(Filter::FilterId(ref filter_id)) => services
| None => FilterDefinition::default(),
| Some(Filter::FilterDefinition(ref filter)) => filter.clone(),
| Some(Filter::FilterId(ref filter_id)) => services
.users
.get_filter(sender_user, filter_id)
.await
@ -138,11 +140,11 @@ pub(crate) async fn sync_events_route(
// some clients, at least element, seem to require knowledge of redundant
// members for "inline" profiles on the timeline to work properly
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options {
LazyLoadOptions::Enabled {
include_redundant_members,
} => (true, include_redundant_members),
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options
{
| LazyLoadOptions::Enabled { include_redundant_members } =>
(true, include_redundant_members),
| LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
};
let full_state = body.body.full_state;
@ -230,9 +232,7 @@ pub(crate) async fn sync_events_route(
}
let invited_room = InvitedRoom {
invite_state: InviteState {
events: invite_state,
},
invite_state: InviteState { events: invite_state },
};
invited_rooms.insert(room_id, invited_room);
@ -268,9 +268,10 @@ pub(crate) async fn sync_events_route(
.count_one_time_keys(sender_user, sender_device);
// Remove all to-device events the device received *last time*
let remove_to_device_events = services
.users
.remove_to_device_events(sender_user, sender_device, since);
let remove_to_device_events =
services
.users
.remove_to_device_events(sender_user, sender_device, since);
let rooms = join3(joined_rooms, left_rooms, invited_rooms);
let ephemeral = join3(remove_to_device_events, to_device_events, presence_updates);
@ -290,7 +291,8 @@ pub(crate) async fn sync_events_route(
.into_iter()
.stream()
.broad_filter_map(|user_id| async move {
let no_shared_encrypted_room = !share_encrypted_room(&services, sender_user, &user_id, None).await;
let no_shared_encrypted_room =
!share_encrypted_room(&services, sender_user, &user_id, None).await;
no_shared_encrypted_room.then_some(user_id)
})
.ready_fold(HashSet::new(), |mut device_list_left, user_id| {
@ -300,9 +302,7 @@ pub(crate) async fn sync_events_route(
.await;
let response = sync_events::v3::Response {
account_data: GlobalAccountData {
events: account_data,
},
account_data: GlobalAccountData { events: account_data },
device_lists: DeviceLists {
changed: device_list_updates.into_iter().collect(),
left: device_list_left.into_iter().collect(),
@ -324,9 +324,7 @@ pub(crate) async fn sync_events_route(
invite: invited_rooms,
knock: BTreeMap::new(), // TODO
},
to_device: ToDevice {
events: to_device_events,
},
to_device: ToDevice { events: to_device_events },
};
// TODO: Retry the endpoint instead of returning
@ -348,7 +346,11 @@ pub(crate) async fn sync_events_route(
}
#[tracing::instrument(name = "presence", level = "debug", skip_all)]
async fn process_presence_updates(services: &Services, since: u64, syncing_user: &UserId) -> PresenceUpdates {
async fn process_presence_updates(
services: &Services,
since: u64,
syncing_user: &UserId,
) -> PresenceUpdates {
services
.presence
.presence_since(since)
@ -367,10 +369,10 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
})
.ready_fold(PresenceUpdates::new(), |mut updates, (user_id, event)| {
match updates.entry(user_id.into()) {
Entry::Vacant(slot) => {
| Entry::Vacant(slot) => {
slot.insert(event);
},
Entry::Occupied(mut slot) => {
| Entry::Occupied(mut slot) => {
let curr_event = slot.get_mut();
let curr_content = &mut curr_event.content;
let new_content = event.content;
@ -380,7 +382,8 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
curr_content.status_msg = new_content
.status_msg
.or_else(|| curr_content.status_msg.take());
curr_content.last_active_ago = new_content.last_active_ago.or(curr_content.last_active_ago);
curr_content.last_active_ago =
new_content.last_active_ago.or(curr_content.last_active_ago);
curr_content.displayname = new_content
.displayname
.or_else(|| curr_content.displayname.take());
@ -410,8 +413,13 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
)]
#[allow(clippy::too_many_arguments)]
async fn handle_left_room(
services: &Services, since: u64, ref room_id: OwnedRoomId, sender_user: &UserId, next_batch_string: &str,
full_state: bool, lazy_load_enabled: bool,
services: &Services,
since: u64,
ref room_id: OwnedRoomId,
sender_user: &UserId,
next_batch_string: &str,
full_state: bool,
lazy_load_enabled: bool,
) -> Result<Option<LeftRoom>> {
// Get and drop the lock to wait for remaining operations to finish
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
@ -440,7 +448,8 @@ async fn handle_left_room(
.try_into()
.expect("Timestamp is valid js_int value"),
kind: RoomMember,
content: serde_json::from_str(r#"{"membership":"leave"}"#).expect("this is valid JSON"),
content: serde_json::from_str(r#"{"membership":"leave"}"#)
.expect("this is valid JSON"),
state_key: Some(sender_user.to_string()),
unsigned: None,
// The following keys are dropped on conversion
@ -449,16 +458,12 @@ async fn handle_left_room(
depth: uint!(1),
auth_events: vec![],
redacts: None,
hashes: EventHash {
sha256: String::new(),
},
hashes: EventHash { sha256: String::new() },
signatures: None,
};
return Ok(Some(LeftRoom {
account_data: RoomAccountData {
events: Vec::new(),
},
account_data: RoomAccountData { events: Vec::new() },
timeline: Timeline {
limited: false,
prev_batch: Some(next_batch_string.to_owned()),
@ -479,8 +484,8 @@ async fn handle_left_room(
.await;
let since_state_ids = match since_shortstatehash {
Ok(s) => services.rooms.state_accessor.state_full_ids(s).await?,
Err(_) => HashMap::new(),
| Ok(s) => services.rooms.state_accessor.state_full_ids(s).await?,
| Err(_) => HashMap::new(),
};
let Ok(left_event_id): Result<OwnedEventId> = services
@ -542,17 +547,14 @@ async fn handle_left_room(
}
Ok(Some(LeftRoom {
account_data: RoomAccountData {
events: Vec::new(),
},
account_data: RoomAccountData { events: Vec::new() },
timeline: Timeline {
limited: true, // TODO: support left timeline events so we dont need to set this to true
limited: true, /* TODO: support left timeline events so we dont need to set this to
* true */
prev_batch: Some(next_batch_string.to_owned()),
events: Vec::new(), // and so we dont need to set this to empty vec
},
state: RoomState {
events: left_state_events,
},
state: RoomState { events: left_state_events },
}))
}
@ -566,8 +568,15 @@ async fn handle_left_room(
)]
#[allow(clippy::too_many_arguments)]
async fn load_joined_room(
services: &Services, sender_user: &UserId, sender_device: &DeviceId, ref room_id: OwnedRoomId, since: u64,
next_batch: u64, lazy_load_enabled: bool, lazy_load_send_redundant: bool, full_state: bool,
services: &Services,
sender_user: &UserId,
sender_device: &DeviceId,
ref room_id: OwnedRoomId,
since: u64,
next_batch: u64,
lazy_load_enabled: bool,
lazy_load_send_redundant: bool,
full_state: bool,
) -> Result<(JoinedRoom, HashSet<OwnedUserId>, HashSet<OwnedUserId>)> {
// Get and drop the lock to wait for remaining operations to finish
// This will make sure the we have all events until next_batch
@ -590,18 +599,26 @@ async fn load_joined_room(
.ok()
.map(Ok);
let timeline = load_timeline(services, sender_user, room_id, sincecount, Some(next_batchcount), 10_usize);
let timeline = load_timeline(
services,
sender_user,
room_id,
sincecount,
Some(next_batchcount),
10_usize,
);
let (current_shortstatehash, since_shortstatehash, timeline) =
try_join3(current_shortstatehash, since_shortstatehash, timeline).await?;
let (timeline_pdus, limited) = timeline;
let timeline_users = timeline_pdus
.iter()
.fold(HashSet::new(), |mut timeline_users, (_, event)| {
timeline_users.insert(event.sender.as_str().to_owned());
timeline_users
});
let timeline_users =
timeline_pdus
.iter()
.fold(HashSet::new(), |mut timeline_users, (_, event)| {
timeline_users.insert(event.sender.as_str().to_owned());
timeline_users
});
let last_notification_read: OptionFuture<_> = timeline_pdus
.is_empty()
@ -617,13 +634,16 @@ async fn load_joined_room(
.is_none_or(|&count| count > since)
.await;
services
.rooms
.lazy_loading
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount);
services.rooms.lazy_loading.lazy_load_confirm_delivery(
sender_user,
sender_device,
room_id,
sincecount,
);
let no_state_changes = timeline_pdus.is_empty()
&& (since_shortstatehash.is_none() || since_shortstatehash.is_some_and(is_equal_to!(current_shortstatehash)));
&& (since_shortstatehash.is_none()
|| since_shortstatehash.is_some_and(is_equal_to!(current_shortstatehash)));
let mut device_list_updates = HashSet::<OwnedUserId>::new();
let mut left_encrypted_users = HashSet::<OwnedUserId>::new();
@ -732,9 +752,10 @@ async fn load_joined_room(
let events = join4(room_events, account_data_events, receipt_events, typing_events);
let unread_notifications = join(notification_count, highlight_count);
let (unread_notifications, events, device_updates) = join3(unread_notifications, events, device_updates)
.boxed()
.await;
let (unread_notifications, events, device_updates) =
join3(unread_notifications, events, device_updates)
.boxed()
.await;
let (room_events, account_data_events, receipt_events, typing_events) = events;
let (notification_count, highlight_count) = unread_notifications;
@ -773,9 +794,7 @@ async fn load_joined_room(
.await;
let joined_room = JoinedRoom {
account_data: RoomAccountData {
events: account_data_events,
},
account_data: RoomAccountData { events: account_data_events },
summary: RoomSummary {
joined_member_count: joined_member_count.map(ruma_from_u64),
invited_member_count: invited_member_count.map(ruma_from_u64),
@ -786,10 +805,7 @@ async fn load_joined_room(
.filter_map(Result::ok)
.collect(),
},
unread_notifications: UnreadNotificationsCount {
highlight_count,
notification_count,
},
unread_notifications: UnreadNotificationsCount { highlight_count, notification_count },
timeline: Timeline {
limited: limited || joined_since_last_sync,
events: room_events,
@ -805,9 +821,7 @@ async fn load_joined_room(
.map(PduEvent::to_sync_state_event)
.collect(),
},
ephemeral: Ephemeral {
events: edus,
},
ephemeral: Ephemeral { events: edus },
unread_thread_notifications: BTreeMap::new(),
};
@ -827,11 +841,20 @@ async fn load_joined_room(
)]
#[allow(clippy::too_many_arguments)]
async fn calculate_state_changes(
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
lazy_load_enabled: bool, lazy_load_send_redundant: bool, full_state: bool,
device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
since_shortstatehash: Option<ShortStateHash>, current_shortstatehash: ShortStateHash,
timeline_pdus: &Vec<(PduCount, PduEvent)>, timeline_users: &HashSet<String>,
services: &Services,
sender_user: &UserId,
sender_device: &DeviceId,
room_id: &RoomId,
next_batchcount: PduCount,
lazy_load_enabled: bool,
lazy_load_send_redundant: bool,
full_state: bool,
device_list_updates: &mut HashSet<OwnedUserId>,
left_encrypted_users: &mut HashSet<OwnedUserId>,
since_shortstatehash: Option<ShortStateHash>,
current_shortstatehash: ShortStateHash,
timeline_pdus: &Vec<(PduCount, PduEvent)>,
timeline_users: &HashSet<String>,
) -> Result<StateChanges> {
let since_sender_member: OptionFuture<_> = since_shortstatehash
.map(|short| {
@ -843,12 +866,13 @@ async fn calculate_state_changes(
})
.into();
let joined_since_last_sync = since_sender_member
.await
.flatten()
.map_or(true, |content: RoomMemberEventContent| {
content.membership != MembershipState::Join
});
let joined_since_last_sync =
since_sender_member
.await
.flatten()
.is_none_or(|content: RoomMemberEventContent| {
content.membership != MembershipState::Join
});
if since_shortstatehash.is_none() || joined_since_last_sync {
calculate_state_initial(
@ -886,8 +910,14 @@ async fn calculate_state_changes(
#[tracing::instrument(name = "initial", level = "trace", skip_all)]
#[allow(clippy::too_many_arguments)]
async fn calculate_state_initial(
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
lazy_load_enabled: bool, full_state: bool, current_shortstatehash: ShortStateHash,
services: &Services,
sender_user: &UserId,
sender_device: &DeviceId,
room_id: &RoomId,
next_batchcount: PduCount,
lazy_load_enabled: bool,
full_state: bool,
current_shortstatehash: ShortStateHash,
timeline_users: &HashSet<String>,
) -> Result<StateChanges> {
// Probably since = 0, we will do an initial sync
@ -956,10 +986,13 @@ async fn calculate_state_initial(
// The state_events above should contain all timeline_users, let's mark them as
// lazy loaded.
services
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount);
services.rooms.lazy_loading.lazy_load_mark_sent(
sender_user,
sender_device,
room_id,
lazy_loaded,
next_batchcount,
);
Ok(StateChanges {
heroes,
@ -973,13 +1006,23 @@ async fn calculate_state_initial(
#[tracing::instrument(name = "incremental", level = "trace", skip_all)]
#[allow(clippy::too_many_arguments)]
async fn calculate_state_incremental(
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
lazy_load_send_redundant: bool, full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>,
left_encrypted_users: &mut HashSet<OwnedUserId>, since_shortstatehash: Option<ShortStateHash>,
current_shortstatehash: ShortStateHash, timeline_pdus: &Vec<(PduCount, PduEvent)>, joined_since_last_sync: bool,
services: &Services,
sender_user: &UserId,
sender_device: &DeviceId,
room_id: &RoomId,
next_batchcount: PduCount,
lazy_load_send_redundant: bool,
full_state: bool,
device_list_updates: &mut HashSet<OwnedUserId>,
left_encrypted_users: &mut HashSet<OwnedUserId>,
since_shortstatehash: Option<ShortStateHash>,
current_shortstatehash: ShortStateHash,
timeline_pdus: &Vec<(PduCount, PduEvent)>,
joined_since_last_sync: bool,
) -> Result<StateChanges> {
// Incremental /sync
let since_shortstatehash = since_shortstatehash.expect("missing since_shortstatehash on incremental sync");
let since_shortstatehash =
since_shortstatehash.expect("missing since_shortstatehash on incremental sync");
let mut delta_state_events = Vec::new();
@ -994,8 +1037,10 @@ async fn calculate_state_incremental(
.state_accessor
.state_full_ids(since_shortstatehash);
let (current_state_ids, since_state_ids): (HashMap<_, OwnedEventId>, HashMap<_, OwnedEventId>) =
try_join(current_state_ids, since_state_ids).await?;
let (current_state_ids, since_state_ids): (
HashMap<_, OwnedEventId>,
HashMap<_, OwnedEventId>,
) = try_join(current_state_ids, since_state_ids).await?;
current_state_ids
.iter()
@ -1044,17 +1089,19 @@ async fn calculate_state_incremental(
let content: RoomMemberEventContent = state_event.get_content()?;
match content.membership {
MembershipState::Join => {
| MembershipState::Join => {
// A new user joined an encrypted room
if !share_encrypted_room(services, sender_user, &user_id, Some(room_id)).await {
if !share_encrypted_room(services, sender_user, &user_id, Some(room_id))
.await
{
device_list_updates.insert(user_id);
}
},
MembershipState::Leave => {
| MembershipState::Leave => {
// Write down users that have left encrypted rooms we are in
left_encrypted_users.insert(user_id);
},
_ => {},
| _ => {},
}
}
}
@ -1139,10 +1186,13 @@ async fn calculate_state_incremental(
state_events.push(member_event);
}
services
.rooms
.lazy_loading
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount);
services.rooms.lazy_loading.lazy_load_mark_sent(
sender_user,
sender_device,
room_id,
lazy_loaded,
next_batchcount,
);
Ok(StateChanges {
heroes,
@ -1154,7 +1204,9 @@ async fn calculate_state_incremental(
}
async fn calculate_counts(
services: &Services, room_id: &RoomId, sender_user: &UserId,
services: &Services,
room_id: &RoomId,
sender_user: &UserId,
) -> Result<(Option<u64>, Option<u64>, Option<Vec<OwnedUserId>>)> {
let joined_member_count = services
.rooms
@ -1168,7 +1220,8 @@ async fn calculate_counts(
.room_invited_count(room_id)
.unwrap_or(0);
let (joined_member_count, invited_member_count) = join(joined_member_count, invited_member_count).await;
let (joined_member_count, invited_member_count) =
join(joined_member_count, invited_member_count).await;
let small_room = joined_member_count.saturating_add(invited_member_count) > 5;
@ -1179,20 +1232,32 @@ async fn calculate_counts(
Ok((Some(joined_member_count), Some(invited_member_count), heroes.await))
}
async fn calculate_heroes(services: &Services, room_id: &RoomId, sender_user: &UserId) -> Vec<OwnedUserId> {
async fn calculate_heroes(
services: &Services,
room_id: &RoomId,
sender_user: &UserId,
) -> Vec<OwnedUserId> {
services
.rooms
.timeline
.all_pdus(sender_user, room_id)
.ready_filter(|(_, pdu)| pdu.kind == RoomMember)
.fold_default(|heroes: Vec<_>, (_, pdu)| fold_hero(heroes, services, room_id, sender_user, pdu))
.fold_default(|heroes: Vec<_>, (_, pdu)| {
fold_hero(heroes, services, room_id, sender_user, pdu)
})
.await
}
async fn fold_hero(
mut heroes: Vec<OwnedUserId>, services: &Services, room_id: &RoomId, sender_user: &UserId, pdu: PduEvent,
mut heroes: Vec<OwnedUserId>,
services: &Services,
room_id: &RoomId,
sender_user: &UserId,
pdu: PduEvent,
) -> Vec<OwnedUserId> {
let Some(user_id): Option<&UserId> = pdu.state_key.as_deref().map(TryInto::try_into).flat_ok() else {
let Some(user_id): Option<&UserId> =
pdu.state_key.as_deref().map(TryInto::try_into).flat_ok()
else {
return heroes;
};

View file

@ -46,7 +46,8 @@ const DEFAULT_BUMP_TYPES: &[TimelineEventType; 6] =
///
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
pub(crate) async fn sync_events_v4_route(
State(services): State<crate::State>, body: Ruma<sync_events::v4::Request>,
State(services): State<crate::State>,
body: Ruma<sync_events::v4::Request>,
) -> Result<sync_events::v4::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.expect("user is authenticated");
@ -81,16 +82,19 @@ pub(crate) async fn sync_events_v4_route(
}
if globalsince == 0 {
services
.sync
.forget_sync_request_connection(sender_user.clone(), sender_device.clone(), conn_id.clone());
services.sync.forget_sync_request_connection(
sender_user.clone(),
sender_device.clone(),
conn_id.clone(),
);
}
// Get sticky parameters from cache
let known_rooms =
services
.sync
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
let known_rooms = services.sync.update_sync_request_with_cache(
sender_user.clone(),
sender_device.clone(),
&mut body,
);
let all_joined_rooms: Vec<_> = services
.rooms
@ -125,9 +129,7 @@ pub(crate) async fn sync_events_v4_route(
let mut device_list_changes = HashSet::new();
let mut device_list_left = HashSet::new();
let mut receipts = sync_events::v4::Receipts {
rooms: BTreeMap::new(),
};
let mut receipts = sync_events::v4::Receipts { rooms: BTreeMap::new() };
let mut account_data = sync_events::v4::AccountData {
global: Vec::new(),
@ -168,7 +170,9 @@ pub(crate) async fn sync_events_v4_route(
);
for room_id in &all_joined_rooms {
let Ok(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id).await else {
let Ok(current_shortstatehash) =
services.rooms.state.get_room_shortstatehash(room_id).await
else {
error!("Room {room_id} has no state");
continue;
};
@ -202,12 +206,17 @@ pub(crate) async fn sync_events_v4_route(
let since_sender_member: Option<RoomMemberEventContent> = services
.rooms
.state_accessor
.state_get_content(since_shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
.state_get_content(
since_shortstatehash,
&StateEventType::RoomMember,
sender_user.as_str(),
)
.ok()
.await;
let joined_since_last_sync =
since_sender_member.map_or(true, |member| member.membership != MembershipState::Join);
let joined_since_last_sync = since_sender_member
.as_ref()
.is_none_or(|member| member.membership != MembershipState::Join);
let new_encrypted_room = encrypted_room && since_encryption.is_err();
@ -232,8 +241,10 @@ pub(crate) async fn sync_events_v4_route(
};
if pdu.kind == RoomMember {
if let Some(state_key) = &pdu.state_key {
let user_id = UserId::parse(state_key.clone())
.map_err(|_| Error::bad_database("Invalid UserId in member PDU."))?;
let user_id =
UserId::parse(state_key.clone()).map_err(|_| {
Error::bad_database("Invalid UserId in member PDU.")
})?;
if user_id == *sender_user {
continue;
@ -241,19 +252,25 @@ pub(crate) async fn sync_events_v4_route(
let content: RoomMemberEventContent = pdu.get_content()?;
match content.membership {
MembershipState::Join => {
| MembershipState::Join => {
// A new user joined an encrypted room
if !share_encrypted_room(&services, sender_user, &user_id, Some(room_id))
.await
if !share_encrypted_room(
&services,
sender_user,
&user_id,
Some(room_id),
)
.await
{
device_list_changes.insert(user_id);
}
},
MembershipState::Leave => {
// Write down users that have left encrypted rooms we are in
| MembershipState::Leave => {
// Write down users that have left encrypted rooms we
// are in
left_encrypted_users.insert(user_id);
},
_ => {},
| _ => {},
}
}
}
@ -293,7 +310,8 @@ pub(crate) async fn sync_events_v4_route(
}
for user_id in left_encrypted_users {
let dont_share_encrypted_room = !share_encrypted_room(&services, sender_user, &user_id, None).await;
let dont_share_encrypted_room =
!share_encrypted_room(&services, sender_user, &user_id, None).await;
// If the user doesn't share an encrypted room with the target anymore, we need
// to tell them
@ -308,85 +326,85 @@ pub(crate) async fn sync_events_v4_route(
for (list_id, list) in &body.lists {
let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) {
Some(true) => &all_invited_rooms,
Some(false) => &all_joined_rooms,
None => &all_rooms,
| Some(true) => &all_invited_rooms,
| Some(false) => &all_joined_rooms,
| None => &all_rooms,
};
let active_rooms = match list.filters.clone().map(|f| f.not_room_types) {
Some(filter) if filter.is_empty() => active_rooms.clone(),
Some(value) => filter_rooms(&services, active_rooms, &value, true).await,
None => active_rooms.clone(),
| Some(filter) if filter.is_empty() => active_rooms.clone(),
| Some(value) => filter_rooms(&services, active_rooms, &value, true).await,
| None => active_rooms.clone(),
};
let active_rooms = match list.filters.clone().map(|f| f.room_types) {
Some(filter) if filter.is_empty() => active_rooms.clone(),
Some(value) => filter_rooms(&services, &active_rooms, &value, false).await,
None => active_rooms,
| Some(filter) if filter.is_empty() => active_rooms.clone(),
| Some(value) => filter_rooms(&services, &active_rooms, &value, false).await,
| None => active_rooms,
};
let mut new_known_rooms = BTreeSet::new();
let ranges = list.ranges.clone();
lists.insert(
list_id.clone(),
sync_events::v4::SyncList {
ops: ranges
.into_iter()
.map(|mut r| {
r.0 = r.0.clamp(
uint!(0),
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
lists.insert(list_id.clone(), sync_events::v4::SyncList {
ops: ranges
.into_iter()
.map(|mut r| {
r.0 = r.0.clamp(
uint!(0),
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
);
r.1 = r.1.clamp(
r.0,
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
);
let room_ids = if !active_rooms.is_empty() {
active_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
} else {
Vec::new()
};
new_known_rooms.extend(room_ids.iter().cloned());
for room_id in &room_ids {
let todo_room = todo_rooms.entry(room_id.clone()).or_insert((
BTreeSet::new(),
0_usize,
u64::MAX,
));
let limit: usize = list
.room_details
.timeline_limit
.map(u64::from)
.map_or(10, usize_from_u64_truncated)
.min(100);
todo_room
.0
.extend(list.room_details.required_state.iter().cloned());
todo_room.1 = todo_room.1.max(limit);
// 0 means unknown because it got out of date
todo_room.2 = todo_room.2.min(
known_rooms
.get(list_id.as_str())
.and_then(|k| k.get(room_id))
.copied()
.unwrap_or(0),
);
r.1 =
r.1.clamp(r.0, UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX));
let room_ids = if !active_rooms.is_empty() {
active_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
} else {
Vec::new()
};
new_known_rooms.extend(room_ids.iter().cloned());
for room_id in &room_ids {
let todo_room =
todo_rooms
.entry(room_id.clone())
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
let limit: usize = list
.room_details
.timeline_limit
.map(u64::from)
.map_or(10, usize_from_u64_truncated)
.min(100);
todo_room
.0
.extend(list.room_details.required_state.iter().cloned());
todo_room.1 = todo_room.1.max(limit);
// 0 means unknown because it got out of date
todo_room.2 = todo_room.2.min(
known_rooms
.get(list_id.as_str())
.and_then(|k| k.get(room_id))
.copied()
.unwrap_or(0),
);
}
sync_events::v4::SyncOp {
op: SlidingOp::Sync,
range: Some(r),
index: None,
room_ids,
room_id: None,
}
})
.collect(),
count: ruma_from_usize(active_rooms.len()),
},
);
}
sync_events::v4::SyncOp {
op: SlidingOp::Sync,
range: Some(r),
index: None,
room_ids,
room_id: None,
}
})
.collect(),
count: ruma_from_usize(active_rooms.len()),
});
if let Some(conn_id) = &body.conn_id {
services.sync.update_sync_known_rooms(
@ -405,9 +423,10 @@ pub(crate) async fn sync_events_v4_route(
if !services.rooms.metadata.exists(room_id).await {
continue;
}
let todo_room = todo_rooms
.entry(room_id.clone())
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
let todo_room =
todo_rooms
.entry(room_id.clone())
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
let limit: usize = room
.timeline_limit
@ -471,14 +490,22 @@ pub(crate) async fn sync_events_v4_route(
(timeline_pdus, limited) = (Vec::new(), true);
} else {
(timeline_pdus, limited) =
match load_timeline(&services, sender_user, room_id, roomsincecount, None, *timeline_limit).await {
Ok(value) => value,
Err(err) => {
warn!("Encountered missing timeline in {}, error {}", room_id, err);
continue;
},
};
(timeline_pdus, limited) = match load_timeline(
&services,
sender_user,
room_id,
roomsincecount,
None,
*timeline_limit,
)
.await
{
| Ok(value) => value,
| Err(err) => {
warn!("Encountered missing timeline in {}, error {}", room_id, err);
continue;
},
};
}
account_data.rooms.insert(
@ -543,11 +570,11 @@ pub(crate) async fn sync_events_v4_route(
.first()
.map_or(Ok::<_, Error>(None), |(pdu_count, _)| {
Ok(Some(match pdu_count {
PduCount::Backfilled(_) => {
| PduCount::Backfilled(_) => {
error!("timeline in backfill state?!");
"0".to_owned()
},
PduCount::Normal(c) => c.to_string(),
| PduCount::Normal(c) => c.to_string(),
}))
})?
.or_else(|| {
@ -568,7 +595,9 @@ pub(crate) async fn sync_events_v4_route(
for (_, pdu) in timeline_pdus {
let ts = MilliSecondsSinceUnixEpoch(pdu.origin_server_ts);
if DEFAULT_BUMP_TYPES.contains(pdu.event_type()) && timestamp.is_none_or(|time| time <= ts) {
if DEFAULT_BUMP_TYPES.contains(pdu.event_type())
&& timestamp.is_none_or(|time| time <= ts)
{
timestamp = Some(ts);
}
}
@ -611,7 +640,7 @@ pub(crate) async fn sync_events_v4_route(
.await;
let name = match heroes.len().cmp(&(1_usize)) {
Ordering::Greater => {
| Ordering::Greater => {
let firsts = heroes[1..]
.iter()
.map(|h| h.name.clone().unwrap_or_else(|| h.user_id.to_string()))
@ -625,13 +654,13 @@ pub(crate) async fn sync_events_v4_route(
Some(format!("{firsts} and {last}"))
},
Ordering::Equal => Some(
| Ordering::Equal => Some(
heroes[0]
.name
.clone()
.unwrap_or_else(|| heroes[0].user_id.to_string()),
),
Ordering::Less => None,
| Ordering::Less => None,
};
let heroes_avatar = if heroes.len() == 1 {
@ -640,77 +669,74 @@ pub(crate) async fn sync_events_v4_route(
None
};
rooms.insert(
room_id.clone(),
sync_events::v4::SlidingSyncRoom {
name: services
.rooms
.state_accessor
.get_name(room_id)
.await
.ok()
.or(name),
avatar: if let Some(heroes_avatar) = heroes_avatar {
ruma::JsOption::Some(heroes_avatar)
} else {
match services.rooms.state_accessor.get_avatar(room_id).await {
ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
ruma::JsOption::Null => ruma::JsOption::Null,
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
}
},
initial: Some(roomsince == &0),
is_dm: None,
invite_state,
unread_notifications: UnreadNotificationsCount {
highlight_count: Some(
services
.rooms
.user
.highlight_count(sender_user, room_id)
.await
.try_into()
.expect("notification count can't go that high"),
),
notification_count: Some(
services
.rooms
.user
.notification_count(sender_user, room_id)
.await
.try_into()
.expect("notification count can't go that high"),
),
},
timeline: room_events,
required_state,
prev_batch,
limited,
joined_count: Some(
services
.rooms
.state_cache
.room_joined_count(room_id)
.await
.unwrap_or(0)
.try_into()
.unwrap_or_else(|_| uint!(0)),
),
invited_count: Some(
services
.rooms
.state_cache
.room_invited_count(room_id)
.await
.unwrap_or(0)
.try_into()
.unwrap_or_else(|_| uint!(0)),
),
num_live: None, // Count events in timeline greater than global sync counter
timestamp,
heroes: Some(heroes),
rooms.insert(room_id.clone(), sync_events::v4::SlidingSyncRoom {
name: services
.rooms
.state_accessor
.get_name(room_id)
.await
.ok()
.or(name),
avatar: if let Some(heroes_avatar) = heroes_avatar {
ruma::JsOption::Some(heroes_avatar)
} else {
match services.rooms.state_accessor.get_avatar(room_id).await {
| ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
| ruma::JsOption::Null => ruma::JsOption::Null,
| ruma::JsOption::Undefined => ruma::JsOption::Undefined,
}
},
);
initial: Some(roomsince == &0),
is_dm: None,
invite_state,
unread_notifications: UnreadNotificationsCount {
highlight_count: Some(
services
.rooms
.user
.highlight_count(sender_user, room_id)
.await
.try_into()
.expect("notification count can't go that high"),
),
notification_count: Some(
services
.rooms
.user
.notification_count(sender_user, room_id)
.await
.try_into()
.expect("notification count can't go that high"),
),
},
timeline: room_events,
required_state,
prev_batch,
limited,
joined_count: Some(
services
.rooms
.state_cache
.room_joined_count(room_id)
.await
.unwrap_or(0)
.try_into()
.unwrap_or_else(|_| uint!(0)),
),
invited_count: Some(
services
.rooms
.state_cache
.room_invited_count(room_id)
.await
.unwrap_or(0)
.try_into()
.unwrap_or_else(|_| uint!(0)),
),
num_live: None, // Count events in timeline greater than global sync counter
timestamp,
heroes: Some(heroes),
});
}
if rooms
@ -757,16 +783,17 @@ pub(crate) async fn sync_events_v4_route(
},
account_data,
receipts,
typing: sync_events::v4::Typing {
rooms: BTreeMap::new(),
},
typing: sync_events::v4::Typing { rooms: BTreeMap::new() },
},
delta_token: None,
})
}
async fn filter_rooms(
services: &Services, rooms: &[OwnedRoomId], filter: &[RoomTypeFilter], negate: bool,
services: &Services,
rooms: &[OwnedRoomId],
filter: &[RoomTypeFilter],
negate: bool,
) -> Vec<OwnedRoomId> {
rooms
.iter()

View file

@ -17,7 +17,8 @@ use crate::{Result, Ruma};
///
/// - Inserts the tag into the tag event of the room account data.
pub(crate) async fn update_tag_route(
State(services): State<crate::State>, body: Ruma<create_tag::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_tag::v3::Request>,
) -> Result<create_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -26,9 +27,7 @@ pub(crate) async fn update_tag_route(
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
.await
.unwrap_or(TagEvent {
content: TagEventContent {
tags: BTreeMap::new(),
},
content: TagEventContent { tags: BTreeMap::new() },
});
tags_event
@ -55,7 +54,8 @@ pub(crate) async fn update_tag_route(
///
/// - Removes the tag from the tag event of the room account data.
pub(crate) async fn delete_tag_route(
State(services): State<crate::State>, body: Ruma<delete_tag::v3::Request>,
State(services): State<crate::State>,
body: Ruma<delete_tag::v3::Request>,
) -> Result<delete_tag::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -64,9 +64,7 @@ pub(crate) async fn delete_tag_route(
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
.await
.unwrap_or(TagEvent {
content: TagEventContent {
tags: BTreeMap::new(),
},
content: TagEventContent { tags: BTreeMap::new() },
});
tags_event.content.tags.remove(&body.tag.clone().into());
@ -90,7 +88,8 @@ pub(crate) async fn delete_tag_route(
///
/// - Gets the tag event of the room account data.
pub(crate) async fn get_tags_route(
State(services): State<crate::State>, body: Ruma<get_tags::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_tags::v3::Request>,
) -> Result<get_tags::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -99,12 +98,8 @@ pub(crate) async fn get_tags_route(
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
.await
.unwrap_or(TagEvent {
content: TagEventContent {
tags: BTreeMap::new(),
},
content: TagEventContent { tags: BTreeMap::new() },
});
Ok(get_tags::v3::Response {
tags: tags_event.content.tags,
})
Ok(get_tags::v3::Response { tags: tags_event.content.tags })
}

View file

@ -11,9 +11,7 @@ pub(crate) async fn get_protocols_route(
_body: Ruma<get_protocols::v3::Request>,
) -> Result<get_protocols::v3::Response> {
// TODO
Ok(get_protocols::v3::Response {
protocols: BTreeMap::new(),
})
Ok(get_protocols::v3::Response { protocols: BTreeMap::new() })
}
/// # `GET /_matrix/client/unstable/thirdparty/protocols`

View file

@ -7,7 +7,8 @@ use crate::{Result, Ruma};
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
pub(crate) async fn get_threads_route(
State(services): State<crate::State>, ref body: Ruma<get_threads::v1::Request>,
State(services): State<crate::State>,
ref body: Ruma<get_threads::v1::Request>,
) -> Result<get_threads::v1::Response> {
// Use limit or else 10, with maximum 100
let limit = body

View file

@ -17,7 +17,8 @@ use crate::Ruma;
///
/// Send a to-device event to a set of client devices.
pub(crate) async fn send_event_to_device_route(
State(services): State<crate::State>, body: Ruma<send_event_to_device::v3::Request>,
State(services): State<crate::State>,
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();
@ -43,12 +44,14 @@ pub(crate) async fn send_event_to_device_route(
services.sending.send_edu_server(
target_user_id.server_name(),
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
sender: sender_user.clone(),
ev_type: body.event_type.clone(),
message_id: count.to_string().into(),
messages,
}))
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(
DirectDeviceContent {
sender: sender_user.clone(),
ev_type: body.event_type.clone(),
message_id: count.to_string().into(),
messages,
},
))
.expect("DirectToDevice EDU can be serialized"),
)?;
@ -62,14 +65,20 @@ pub(crate) async fn send_event_to_device_route(
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
match target_device_id_maybe {
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
services
.users
.add_to_device_event(sender_user, target_user_id, target_device_id, event_type, event)
.add_to_device_event(
sender_user,
target_user_id,
target_device_id,
event_type,
event,
)
.await;
},
DeviceIdOrAllDevices::AllDevices => {
| DeviceIdOrAllDevices::AllDevices => {
let (event_type, event) = (&event_type, &event);
services
.users

View file

@ -7,7 +7,8 @@ use crate::{utils, Error, Result, Ruma};
///
/// Sets the typing state of the sender user.
pub(crate) async fn create_typing_event_route(
State(services): State<crate::State>, body: Ruma<create_typing_event::v3::Request>,
State(services): State<crate::State>,
body: Ruma<create_typing_event::v3::Request>,
) -> Result<create_typing_event::v3::Response> {
use create_typing_event::v3::Typing;

View file

@ -10,8 +10,8 @@ use ruma::{
error::ErrorKind,
membership::mutual_rooms,
profile::{
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key, set_profile_key,
set_timezone_key,
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key,
set_profile_key, set_timezone_key,
},
room::get_summary,
},
@ -34,7 +34,8 @@ use crate::{Error, Result, Ruma, RumaResponse};
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
pub(crate) async fn get_mutual_rooms_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<mutual_rooms::unstable::Request>,
) -> Result<mutual_rooms::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -47,10 +48,7 @@ pub(crate) async fn get_mutual_rooms_route(
}
if !services.users.exists(&body.user_id).await {
return Ok(mutual_rooms::unstable::Response {
joined: vec![],
next_batch_token: None,
});
return Ok(mutual_rooms::unstable::Response { joined: vec![], next_batch_token: None });
}
let mutual_rooms: Vec<OwnedRoomId> = services
@ -77,7 +75,8 @@ pub(crate) async fn get_mutual_rooms_route(
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
pub(crate) async fn get_room_summary_legacy(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
get_room_summary(State(services), InsecureClientIp(client), body)
@ -94,7 +93,8 @@ pub(crate) async fn get_room_summary_legacy(
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
pub(crate) async fn get_room_summary(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<get_summary::msc3266::Response> {
let sender_user = body.sender_user.as_ref();
@ -194,7 +194,8 @@ pub(crate) async fn get_room_summary(
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn delete_timezone_key_route(
State(services): State<crate::State>, body: Ruma<delete_timezone_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<delete_timezone_key::unstable::Request>,
) -> Result<delete_timezone_key::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -221,7 +222,8 @@ pub(crate) async fn delete_timezone_key_route(
///
/// - Also makes sure other users receive the update using presence EDUs
pub(crate) async fn set_timezone_key_route(
State(services): State<crate::State>, body: Ruma<set_timezone_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<set_timezone_key::unstable::Request>,
) -> Result<set_timezone_key::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -248,7 +250,8 @@ pub(crate) async fn set_timezone_key_route(
///
/// This also handles the avatar_url and displayname being updated.
pub(crate) async fn set_profile_key_route(
State(services): State<crate::State>, body: Ruma<set_profile_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<set_profile_key::unstable::Request>,
) -> Result<set_profile_key::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -264,7 +267,9 @@ pub(crate) async fn set_profile_key_route(
if body.kv_pair.len() > 1 {
// TODO: support PATCH or "recursively" adding keys in some sort
return Err!(Request(BadJson("This endpoint can only take one key-value pair at a time")));
return Err!(Request(BadJson(
"This endpoint can only take one key-value pair at a time"
)));
}
let Some(profile_key_value) = body.kv_pair.get(&body.key) else {
@ -294,7 +299,13 @@ pub(crate) async fn set_profile_key_route(
.collect()
.await;
update_displayname(&services, &body.user_id, Some(profile_key_value.to_string()), &all_joined_rooms).await;
update_displayname(
&services,
&body.user_id,
Some(profile_key_value.to_string()),
&all_joined_rooms,
)
.await;
} else if body.key == "avatar_url" {
let mxc = ruma::OwnedMxcUri::from(profile_key_value.to_string());
@ -330,7 +341,8 @@ pub(crate) async fn set_profile_key_route(
///
/// This also handles the avatar_url and displayname being updated.
pub(crate) async fn delete_profile_key_route(
State(services): State<crate::State>, body: Ruma<delete_profile_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<delete_profile_key::unstable::Request>,
) -> Result<delete_profile_key::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
@ -340,7 +352,9 @@ pub(crate) async fn delete_profile_key_route(
if body.kv_pair.len() > 1 {
// TODO: support PATCH or "recursively" adding keys in some sort
return Err!(Request(BadJson("This endpoint can only take one key-value pair at a time")));
return Err!(Request(BadJson(
"This endpoint can only take one key-value pair at a time"
)));
}
if body.key == "displayname" {
@ -387,7 +401,8 @@ pub(crate) async fn delete_profile_key_route(
/// - If user is on another server and we do not have a local copy already fetch
/// `timezone` over federation
pub(crate) async fn get_timezone_key_route(
State(services): State<crate::State>, body: Ruma<get_timezone_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<get_timezone_key::unstable::Request>,
) -> Result<get_timezone_key::unstable::Response> {
if !services.globals.user_is_local(&body.user_id) {
// Create and update our local copy of the user
@ -422,9 +437,7 @@ pub(crate) async fn get_timezone_key_route(
.users
.set_timezone(&body.user_id, response.tz.clone());
return Ok(get_timezone_key::unstable::Response {
tz: response.tz,
});
return Ok(get_timezone_key::unstable::Response { tz: response.tz });
}
}
@ -446,7 +459,8 @@ pub(crate) async fn get_timezone_key_route(
/// - If user is on another server and we do not have a local copy already fetch
/// `timezone` over federation
pub(crate) async fn get_profile_key_route(
State(services): State<crate::State>, body: Ruma<get_profile_key::unstable::Request>,
State(services): State<crate::State>,
body: Ruma<get_profile_key::unstable::Request>,
) -> Result<get_profile_key::unstable::Response> {
let mut profile_key_value: BTreeMap<String, serde_json::Value> = BTreeMap::new();
@ -492,9 +506,7 @@ pub(crate) async fn get_profile_key_route(
return Err!(Request(NotFound("The requested profile key does not exist.")));
}
return Ok(get_profile_key::unstable::Response {
value: profile_key_value,
});
return Ok(get_profile_key::unstable::Response { value: profile_key_value });
}
}
@ -510,7 +522,5 @@ pub(crate) async fn get_profile_key_route(
return Err!(Request(NotFound("The requested profile key does not exist.")));
}
Ok(get_profile_key::unstable::Response {
value: profile_key_value,
})
Ok(get_profile_key::unstable::Response { value: profile_key_value })
}

View file

@ -74,7 +74,9 @@ pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
/// conduwuit-specific API to return the amount of users registered on this
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
/// only includes active users (not deactivated, no guests, etc)
pub(crate) async fn conduwuit_local_user_count(State(services): State<crate::State>) -> Result<impl IntoResponse> {
pub(crate) async fn conduwuit_local_user_count(
State(services): State<crate::State>,
) -> Result<impl IntoResponse> {
let user_count = services.users.list_local_users().count().await;
Ok(Json(serde_json::json!({

View file

@ -18,7 +18,8 @@ use crate::{Result, 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(crate) async fn search_users_route(
State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
State(services): State<crate::State>,
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 = usize::try_from(body.limit).map_or(10, usize::from).min(100); // default limit is 10
@ -61,7 +62,11 @@ pub(crate) async fn search_users_route(
services
.rooms
.state_accessor
.room_state_get_content::<RoomJoinRulesEventContent>(room, &StateEventType::RoomJoinRules, "")
.room_state_get_content::<RoomJoinRulesEventContent>(
room,
&StateEventType::RoomJoinRules,
"",
)
.map_ok_or(false, |content| content.join_rule == JoinRule::Public)
})
.await;
@ -89,8 +94,5 @@ pub(crate) async fn search_users_route(
let results = users.take(limit).collect().await;
Ok(search_users::v3::Response {
results,
limited,
})
Ok(search_users::v3::Response { results, limited })
}

View file

@ -17,7 +17,8 @@ type HmacSha1 = Hmac<Sha1>;
///
/// TODO: Returns information about the recommended turn server.
pub(crate) async fn turn_server_route(
State(services): State<crate::State>, body: Ruma<get_turn_server_info::v3::Request>,
State(services): State<crate::State>,
body: Ruma<get_turn_server_info::v3::Request>,
) -> Result<get_turn_server_info::v3::Response> {
// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way
if services.server.config.turn_uris.is_empty() {
@ -44,7 +45,8 @@ pub(crate) async fn turn_server_route(
let username: String = format!("{}:{}", expiry.get(), user);
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes()).expect("HMAC can take key of any size");
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(username.as_bytes());
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());

View file

@ -13,21 +13,18 @@ use crate::{Error, Result, Ruma};
///
/// Returns the .well-known URL if it is configured, otherwise returns 404.
pub(crate) async fn well_known_client(
State(services): State<crate::State>, _body: Ruma<discover_homeserver::Request>,
State(services): State<crate::State>,
_body: Ruma<discover_homeserver::Request>,
) -> Result<discover_homeserver::Response> {
let client_url = match services.server.config.well_known.client.as_ref() {
Some(url) => url.to_string(),
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
| Some(url) => url.to_string(),
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
};
Ok(discover_homeserver::Response {
homeserver: HomeserverInfo {
base_url: client_url.clone(),
},
homeserver: HomeserverInfo { base_url: client_url.clone() },
identity_server: None,
sliding_sync_proxy: Some(SlidingSyncProxyInfo {
url: client_url,
}),
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url }),
tile_server: None,
})
}
@ -36,7 +33,8 @@ pub(crate) async fn well_known_client(
///
/// Server support contact and support page of a homeserver's domain.
pub(crate) async fn well_known_support(
State(services): State<crate::State>, _body: Ruma<discover_support::Request>,
State(services): State<crate::State>,
_body: Ruma<discover_support::Request>,
) -> Result<discover_support::Response> {
let support_page = services
.server
@ -65,11 +63,7 @@ pub(crate) async fn well_known_support(
let mut contacts: Vec<Contact> = vec![];
if let Some(role) = role {
let contact = Contact {
role,
email_address,
matrix_id,
};
let contact = Contact { role, email_address, matrix_id };
contacts.push(contact);
}
@ -79,22 +73,21 @@ pub(crate) async fn well_known_support(
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
}
Ok(discover_support::Response {
contacts,
support_page,
})
Ok(discover_support::Response { contacts, support_page })
}
/// # `GET /client/server.json`
///
/// Endpoint provided by sliding sync proxy used by some clients such as Element
/// Web as a non-standard health check.
pub(crate) async fn syncv3_client_server_json(State(services): State<crate::State>) -> Result<impl IntoResponse> {
pub(crate) async fn syncv3_client_server_json(
State(services): State<crate::State>,
) -> Result<impl IntoResponse> {
let server_url = match services.server.config.well_known.client.as_ref() {
Some(url) => url.to_string(),
None => match services.server.config.well_known.server.as_ref() {
Some(url) => url.to_string(),
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
| Some(url) => url.to_string(),
| None => match services.server.config.well_known.server.as_ref() {
| Some(url) => url.to_string(),
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
},
};