mirror of
https://gitlab.com/famedly/conduit.git
synced 2024-12-27 13:03:47 +03:00
refactor: all the clippy lints
This commit is contained in:
parent
3ad7675bbf
commit
41e56baf60
72 changed files with 1091 additions and 1152 deletions
31
Cargo.toml
31
Cargo.toml
|
@ -3,15 +3,46 @@ explicit_outlives_requirements = "warn"
|
||||||
unused_qualifications = "warn"
|
unused_qualifications = "warn"
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
|
# Restrictions
|
||||||
cloned_instead_of_copied = "warn"
|
cloned_instead_of_copied = "warn"
|
||||||
|
create_dir = "warn"
|
||||||
dbg_macro = "warn"
|
dbg_macro = "warn"
|
||||||
|
get_unwrap = "warn"
|
||||||
|
rc_mutex = "warn"
|
||||||
str_to_string = "warn"
|
str_to_string = "warn"
|
||||||
|
string_lit_chars_any = "warn"
|
||||||
|
|
||||||
|
# Groups
|
||||||
|
complexity = "warn"
|
||||||
|
perf = "warn"
|
||||||
|
style = "warn"
|
||||||
|
suspicious = "warn"
|
||||||
|
|
||||||
|
# To allow us to override certain lints
|
||||||
|
cargo = { level = "warn", priority = -1 }
|
||||||
|
pedantic = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
|
# lints we want to allow
|
||||||
|
cast_possible_truncation = "allow" # Not really a good way to handle this issue
|
||||||
|
missing_errors_doc = "allow" # Becomes highly redundant for functions accessing the database
|
||||||
|
missing_panics_doc = "allow" # We *should* only ever use unwraps/expects where infallable
|
||||||
|
module_name_repetitions = "allow" # Many things are ex-expored, and in many cases removing repetitions just makes things more confusing
|
||||||
|
multiple_crate_versions = "allow" # Would require quite a lot of effort for minimal gain
|
||||||
|
must_use_candidate = "allow" # Basically useless, even the docs say it's "Not bad at all"
|
||||||
|
redundant_closure_for_method_calls = "allow" # `.ok()` is nicer than `std::result::Result::ok` (which needs to be done where we use our own `Result`)
|
||||||
|
struct_excessive_bools = "allow" # Very easy to spot, and the behaviour it encourages to use is much worse than not using this at all
|
||||||
|
too_many_lines = "allow" # Some functions are complicated, and won't really be re-used in other places
|
||||||
|
|
||||||
|
# fixed in !670
|
||||||
|
doc_markdown = "allow"
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
authors = ["timokoesters <timo@koesters.xyz>"]
|
authors = ["timokoesters <timo@koesters.xyz>"]
|
||||||
|
categories = ["web-programming::http-server"]
|
||||||
description = "A Matrix homeserver written in Rust"
|
description = "A Matrix homeserver written in Rust"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://conduit.rs"
|
homepage = "https://conduit.rs"
|
||||||
|
keywords = ["homeserver", "matrix", "selfhost"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -17,11 +17,8 @@ pub(crate) async fn send_request<T>(
|
||||||
where
|
where
|
||||||
T: OutgoingRequest + Debug,
|
T: OutgoingRequest + Debug,
|
||||||
{
|
{
|
||||||
let destination = match registration.url {
|
let Some(destination) = registration.url else {
|
||||||
Some(url) => url,
|
|
||||||
None => {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let hs_token = registration.hs_token.as_str();
|
let hs_token = registration.hs_token.as_str();
|
||||||
|
@ -33,7 +30,7 @@ where
|
||||||
&[MatrixVersion::V1_0],
|
&[MatrixVersion::V1_0],
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|body| body.freeze());
|
.map(BytesMut::freeze);
|
||||||
|
|
||||||
let mut parts = http_request.uri().clone().into_parts();
|
let mut parts = http_request.uri().clone().into_parts();
|
||||||
let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned();
|
let old_path_and_query = parts.path_and_query.unwrap().as_str().to_owned();
|
||||||
|
|
|
@ -69,11 +69,11 @@ pub async fn get_register_available_route(
|
||||||
/// to check if the user id is valid and available.
|
/// to check if the user id is valid and available.
|
||||||
///
|
///
|
||||||
/// - Only works if registration is enabled
|
/// - Only works if registration is enabled
|
||||||
/// - If type is guest: ignores all parameters except initial_device_display_name
|
/// - If type is guest: ignores all parameters except `initial_device_display_name`
|
||||||
/// - If sender is not appservice: Requires UIAA (but we only use a dummy stage)
|
/// - If sender is not appservice: Requires UIAA (but we only use a dummy stage)
|
||||||
/// - If type is not guest and no username is given: Always fails after UIAA check
|
/// - If type is not guest and no username is given: Always fails after UIAA check
|
||||||
/// - Creates a new account and populates it with default account data
|
/// - Creates a new account and populates it with default account data
|
||||||
/// - If `inhibit_login` is false: Creates a device and returns device id and access_token
|
/// - If `inhibit_login` is false: Creates a device and returns device id and `access_token`
|
||||||
pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
|
pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
|
||||||
if !services().globals.allow_registration() && body.appservice_info.is_none() {
|
if !services().globals.allow_registration() && body.appservice_info.is_none() {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -149,7 +149,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
stages: vec![AuthType::RegistrationToken],
|
stages: vec![AuthType::RegistrationToken],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -161,7 +161,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
stages: vec![AuthType::Dummy],
|
stages: vec![AuthType::Dummy],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -307,7 +307,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
/// - The password hash is calculated using argon2 with 32 character salt, the plain password is
|
/// - The password hash is calculated using argon2 with 32 character salt, the plain password is
|
||||||
/// not saved
|
/// not saved
|
||||||
///
|
///
|
||||||
/// If logout_devices is true it does the following for each device except the sender device:
|
/// If `logout_devices` is true it does the following for each device except the sender device:
|
||||||
/// - Invalidates access token
|
/// - Invalidates access token
|
||||||
/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
|
/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
|
@ -323,7 +323,7 @@ pub async fn change_password_route(
|
||||||
stages: vec![AuthType::Password],
|
stages: vec![AuthType::Password],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -375,12 +375,12 @@ pub async fn change_password_route(
|
||||||
|
|
||||||
/// # `GET _matrix/client/r0/account/whoami`
|
/// # `GET _matrix/client/r0/account/whoami`
|
||||||
///
|
///
|
||||||
/// Get user_id of the sender user.
|
/// Get `user_id` of the sender user.
|
||||||
///
|
///
|
||||||
/// Note: Also works for Application Services
|
/// Note: Also works for Application Services
|
||||||
pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
|
pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let device_id = body.sender_device.as_ref().cloned();
|
let device_id = body.sender_device.clone();
|
||||||
|
|
||||||
Ok(whoami::v3::Response {
|
Ok(whoami::v3::Response {
|
||||||
user_id: sender_user.clone(),
|
user_id: sender_user.clone(),
|
||||||
|
@ -410,7 +410,7 @@ pub async fn deactivate_route(
|
||||||
stages: vec![AuthType::Password],
|
stages: vec![AuthType::Password],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub(crate) async fn get_alias_helper(
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
room_alias.server_name(),
|
room_alias.server_name(),
|
||||||
federation::query::get_room_information::v1::Request {
|
federation::query::get_room_information::v1::Request {
|
||||||
room_alias: room_alias.to_owned(),
|
room_alias: room_alias.clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -166,14 +166,11 @@ pub(crate) async fn get_alias_helper(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let room_id = match room_id {
|
let Some(room_id) = room_id else {
|
||||||
Some(room_id) => room_id,
|
|
||||||
None => {
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Room with alias not found.",
|
"Room with alias not found.",
|
||||||
))
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_alias::v3::Response::new(
|
Ok(get_alias::v3::Response::new(
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub async fn get_backup_info_route(
|
||||||
etag: services()
|
etag: services()
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_etag(sender_user, &body.version)?,
|
.get_etag(sender_user, &body.version)?,
|
||||||
version: body.version.to_owned(),
|
version: body.version.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ pub async fn add_backup_keys_route(
|
||||||
room_id,
|
room_id,
|
||||||
session_id,
|
session_id,
|
||||||
key_data,
|
key_data,
|
||||||
)?
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ pub async fn add_backup_keys_for_room_route(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
session_id,
|
session_id,
|
||||||
key_data,
|
key_data,
|
||||||
)?
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(add_backup_keys_for_room::v3::Response {
|
Ok(add_backup_keys_for_room::v3::Response {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use tracing::error;
|
||||||
/// Allows loading room history around an event.
|
/// Allows loading room history around an event.
|
||||||
///
|
///
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events if the user was
|
/// - Only works if the user is joined (TODO: always allow, but only show events if the user was
|
||||||
/// joined, depending on history_visibility)
|
/// joined, depending on `history_visibility`)
|
||||||
pub async fn get_context_route(
|
pub async fn get_context_route(
|
||||||
body: Ruma<get_context::v3::Request>,
|
body: Ruma<get_context::v3::Request>,
|
||||||
) -> Result<get_context::v3::Response> {
|
) -> Result<get_context::v3::Response> {
|
||||||
|
@ -22,7 +22,7 @@ pub async fn get_context_route(
|
||||||
LazyLoadOptions::Enabled {
|
LazyLoadOptions::Enabled {
|
||||||
include_redundant_members,
|
include_redundant_members,
|
||||||
} => (true, *include_redundant_members),
|
} => (true, *include_redundant_members),
|
||||||
_ => (false, false),
|
LazyLoadOptions::Disabled => (false, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut lazy_loaded = HashSet::new();
|
let mut lazy_loaded = HashSet::new();
|
||||||
|
@ -103,8 +103,7 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
let start_token = events_before
|
let start_token = events_before
|
||||||
.last()
|
.last()
|
||||||
.map(|(count, _)| count.stringify())
|
.map_or_else(|| base_token.stringify(), |(count, _)| count.stringify());
|
||||||
.unwrap_or_else(|| base_token.stringify());
|
|
||||||
|
|
||||||
let events_before: Vec<_> = events_before
|
let events_before: Vec<_> = events_before
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -159,8 +158,7 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
let end_token = events_after
|
let end_token = events_after
|
||||||
.last()
|
.last()
|
||||||
.map(|(count, _)| count.stringify())
|
.map_or_else(|| base_token.stringify(), |(count, _)| count.stringify());
|
||||||
.unwrap_or_else(|| base_token.stringify());
|
|
||||||
|
|
||||||
let events_after: Vec<_> = events_after
|
let events_after: Vec<_> = events_after
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -176,21 +174,15 @@ pub async fn get_context_route(
|
||||||
.get_statekey_from_short(shortstatekey)?;
|
.get_statekey_from_short(shortstatekey)?;
|
||||||
|
|
||||||
if event_type != StateEventType::RoomMember {
|
if event_type != StateEventType::RoomMember {
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
state.push(pdu.to_state_event());
|
state.push(pdu.to_state_event());
|
||||||
} else if !lazy_load_enabled || lazy_loaded.contains(&state_key) {
|
} else if !lazy_load_enabled || lazy_loaded.contains(&state_key) {
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
state.push(pdu.to_state_event());
|
state.push(pdu.to_state_event());
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ pub async fn delete_device_route(
|
||||||
stages: vec![AuthType::Password],
|
stages: vec![AuthType::Password],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -137,7 +137,7 @@ pub async fn delete_devices_route(
|
||||||
stages: vec![AuthType::Password],
|
stages: vec![AuthType::Password],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -162,7 +162,7 @@ pub async fn delete_devices_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
for device_id in &body.devices {
|
for device_id in &body.devices {
|
||||||
services().users.remove_device(sender_user, device_id)?
|
services().users.remove_device(sender_user, device_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(delete_devices::v3::Response {})
|
Ok(delete_devices::v3::Response {})
|
||||||
|
|
|
@ -13,9 +13,8 @@ pub async fn get_filter_route(
|
||||||
body: Ruma<get_filter::v3::Request>,
|
body: Ruma<get_filter::v3::Request>,
|
||||||
) -> Result<get_filter::v3::Response> {
|
) -> Result<get_filter::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let filter = match services().users.get_filter(sender_user, &body.filter_id)? {
|
let Some(filter) = services().users.get_filter(sender_user, &body.filter_id)? else {
|
||||||
Some(filter) => filter,
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found."));
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found.")),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_filter::v3::Response::new(filter))
|
Ok(get_filter::v3::Response::new(filter))
|
||||||
|
|
|
@ -106,7 +106,7 @@ pub async fn upload_signing_keys_route(
|
||||||
stages: vec![AuthType::Password],
|
stages: vec![AuthType::Password],
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
@ -173,7 +173,6 @@ pub async fn upload_signatures_route(
|
||||||
"Invalid signature.",
|
"Invalid signature.",
|
||||||
))?
|
))?
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
|
||||||
{
|
{
|
||||||
// Signature validation?
|
// Signature validation?
|
||||||
let signature = (
|
let signature = (
|
||||||
|
@ -401,8 +400,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
while let Some((server, response)) = futures.next().await {
|
while let Some((server, response)) = futures.next().await {
|
||||||
match response {
|
if let Ok(Ok(response)) = response {
|
||||||
Ok(Ok(response)) => {
|
|
||||||
for (user, masterkey) in response.master_keys {
|
for (user, masterkey) in response.master_keys {
|
||||||
let (master_key_id, mut master_key) =
|
let (master_key_id, mut master_key) =
|
||||||
services().users.parse_master_key(&user, &masterkey)?;
|
services().users.parse_master_key(&user, &masterkey)?;
|
||||||
|
@ -428,21 +426,19 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
|
|
||||||
self_signing_keys.extend(response.self_signing_keys);
|
self_signing_keys.extend(response.self_signing_keys);
|
||||||
device_keys.extend(response.device_keys);
|
device_keys.extend(response.device_keys);
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
back_off(server.to_owned()).await;
|
back_off(server.to_owned()).await;
|
||||||
|
|
||||||
failures.insert(server.to_string(), json!({}));
|
failures.insert(server.to_string(), json!({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(get_keys::v3::Response {
|
Ok(get_keys::v3::Response {
|
||||||
|
failures,
|
||||||
|
device_keys,
|
||||||
master_keys,
|
master_keys,
|
||||||
self_signing_keys,
|
self_signing_keys,
|
||||||
user_signing_keys,
|
user_signing_keys,
|
||||||
device_keys,
|
|
||||||
failures,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub async fn join_room_by_id_route(
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
||||||
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
||||||
.filter_map(|sender| sender.as_str().map(|s| s.to_owned()))
|
.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
|
||||||
.filter_map(|sender| UserId::parse(sender).ok())
|
.filter_map(|sender| UserId::parse(sender).ok())
|
||||||
.map(|user| user.server_name().to_owned()),
|
.map(|user| user.server_name().to_owned()),
|
||||||
);
|
);
|
||||||
|
@ -105,7 +105,7 @@ pub async fn join_room_by_id_or_alias_route(
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
||||||
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
||||||
.filter_map(|sender| sender.as_str().map(|s| s.to_owned()))
|
.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
|
||||||
.filter_map(|sender| UserId::parse(sender).ok())
|
.filter_map(|sender| UserId::parse(sender).ok())
|
||||||
.map(|user| user.server_name().to_owned()),
|
.map(|user| user.server_name().to_owned()),
|
||||||
);
|
);
|
||||||
|
@ -543,313 +543,11 @@ async fn join_room_by_id_helper(
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
// Ask a remote server if we are not participating in this room
|
// Ask a remote server if we are not participating in this room
|
||||||
if !services()
|
if services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(services().globals.server_name(), room_id)?
|
.server_in_room(services().globals.server_name(), room_id)?
|
||||||
{
|
{
|
||||||
info!("Joining {room_id} over federation.");
|
|
||||||
|
|
||||||
let (make_join_response, remote_server) =
|
|
||||||
make_join_request(sender_user, room_id, servers).await?;
|
|
||||||
|
|
||||||
info!("make_join finished");
|
|
||||||
|
|
||||||
let room_version_id = match make_join_response.room_version {
|
|
||||||
Some(room_version)
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.supported_room_versions()
|
|
||||||
.contains(&room_version) =>
|
|
||||||
{
|
|
||||||
room_version
|
|
||||||
}
|
|
||||||
_ => return Err(Error::BadServerResponse("Room version is not supported")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut join_event_stub: CanonicalJsonObject =
|
|
||||||
serde_json::from_str(make_join_response.event.get()).map_err(|_| {
|
|
||||||
Error::BadServerResponse("Invalid make_join event json received from server.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let join_authorized_via_users_server = join_event_stub
|
|
||||||
.get("content")
|
|
||||||
.map(|s| {
|
|
||||||
s.as_object()?
|
|
||||||
.get("join_authorised_via_users_server")?
|
|
||||||
.as_str()
|
|
||||||
})
|
|
||||||
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok());
|
|
||||||
|
|
||||||
// TODO: Is origin needed?
|
|
||||||
join_event_stub.insert(
|
|
||||||
"origin".to_owned(),
|
|
||||||
CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()),
|
|
||||||
);
|
|
||||||
join_event_stub.insert(
|
|
||||||
"origin_server_ts".to_owned(),
|
|
||||||
CanonicalJsonValue::Integer(
|
|
||||||
utils::millis_since_unix_epoch()
|
|
||||||
.try_into()
|
|
||||||
.expect("Timestamp is valid js_int value"),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
join_event_stub.insert(
|
|
||||||
"content".to_owned(),
|
|
||||||
to_canonical_value(RoomMemberEventContent {
|
|
||||||
membership: MembershipState::Join,
|
|
||||||
displayname: services().users.displayname(sender_user)?,
|
|
||||||
avatar_url: services().users.avatar_url(sender_user)?,
|
|
||||||
is_direct: None,
|
|
||||||
third_party_invite: None,
|
|
||||||
blurhash: services().users.blurhash(sender_user)?,
|
|
||||||
reason,
|
|
||||||
join_authorized_via_users_server,
|
|
||||||
})
|
|
||||||
.expect("event is valid, we just created it"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms
|
|
||||||
join_event_stub.remove("event_id");
|
|
||||||
|
|
||||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs to be present
|
|
||||||
ruma::signatures::hash_and_sign_event(
|
|
||||||
services().globals.server_name().as_str(),
|
|
||||||
services().globals.keypair(),
|
|
||||||
&mut join_event_stub,
|
|
||||||
&room_version_id,
|
|
||||||
)
|
|
||||||
.expect("event is valid, we just created it");
|
|
||||||
|
|
||||||
// Generate event id
|
|
||||||
let event_id = format!(
|
|
||||||
"${}",
|
|
||||||
ruma::signatures::reference_hash(&join_event_stub, &room_version_id)
|
|
||||||
.expect("ruma can calculate reference hashes")
|
|
||||||
);
|
|
||||||
let event_id = <&EventId>::try_from(event_id.as_str())
|
|
||||||
.expect("ruma's reference hashes are valid event ids");
|
|
||||||
|
|
||||||
// Add event_id back
|
|
||||||
join_event_stub.insert(
|
|
||||||
"event_id".to_owned(),
|
|
||||||
CanonicalJsonValue::String(event_id.as_str().to_owned()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// It has enough fields to be called a proper event now
|
|
||||||
let mut join_event = join_event_stub;
|
|
||||||
|
|
||||||
info!("Asking {remote_server} for send_join");
|
|
||||||
let send_join_response = services()
|
|
||||||
.sending
|
|
||||||
.send_federation_request(
|
|
||||||
&remote_server,
|
|
||||||
federation::membership::create_join_event::v2::Request {
|
|
||||||
room_id: room_id.to_owned(),
|
|
||||||
event_id: event_id.to_owned(),
|
|
||||||
pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()),
|
|
||||||
omit_members: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
info!("send_join finished");
|
|
||||||
|
|
||||||
if let Some(signed_raw) = &send_join_response.room_state.event {
|
|
||||||
info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event");
|
|
||||||
let (signed_event_id, signed_value) =
|
|
||||||
match gen_event_id_canonical_json(signed_raw, &room_version_id) {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => {
|
|
||||||
// Event could not be converted to canonical json
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Could not convert event to canonical json.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if signed_event_id != event_id {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Server sent event with wrong event id",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
match signed_value["signatures"]
|
|
||||||
.as_object()
|
|
||||||
.ok_or(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Server sent invalid signatures type",
|
|
||||||
))
|
|
||||||
.and_then(|e| {
|
|
||||||
e.get(remote_server.as_str()).ok_or(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Server did not send its signature",
|
|
||||||
))
|
|
||||||
}) {
|
|
||||||
Ok(signature) => {
|
|
||||||
join_event
|
|
||||||
.get_mut("signatures")
|
|
||||||
.expect("we created a valid pdu")
|
|
||||||
.as_object_mut()
|
|
||||||
.expect("we created a valid pdu")
|
|
||||||
.insert(remote_server.to_string(), signature.clone());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
services().rooms.short.get_or_create_shortroomid(room_id)?;
|
|
||||||
|
|
||||||
info!("Parsing join event");
|
|
||||||
let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone())
|
|
||||||
.map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?;
|
|
||||||
|
|
||||||
let mut state = HashMap::new();
|
|
||||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
|
||||||
|
|
||||||
info!("Fetching join signing keys");
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.event_handler
|
|
||||||
.fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
info!("Going through send_join response room_state");
|
|
||||||
for result in send_join_response
|
|
||||||
.room_state
|
|
||||||
.state
|
|
||||||
.iter()
|
|
||||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
|
|
||||||
{
|
|
||||||
let (event_id, value) = match result.await {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
|
||||||
warn!("Invalid PDU in send_join response: {} {:?}", e, value);
|
|
||||||
Error::BadServerResponse("Invalid PDU in send_join response.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.outlier
|
|
||||||
.add_pdu_outlier(&event_id, &value)?;
|
|
||||||
if let Some(state_key) = &pdu.state_key {
|
|
||||||
let shortstatekey = services()
|
|
||||||
.rooms
|
|
||||||
.short
|
|
||||||
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
|
|
||||||
state.insert(shortstatekey, pdu.event_id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Going through send_join response auth_chain");
|
|
||||||
for result in send_join_response
|
|
||||||
.room_state
|
|
||||||
.auth_chain
|
|
||||||
.iter()
|
|
||||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
|
|
||||||
{
|
|
||||||
let (event_id, value) = match result.await {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.outlier
|
|
||||||
.add_pdu_outlier(&event_id, &value)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Running send_join auth check");
|
|
||||||
let authenticated = state_res::event_auth::auth_check(
|
|
||||||
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
|
||||||
&parsed_join_pdu,
|
|
||||||
None::<PduEvent>, // TODO: third party invite
|
|
||||||
|k, s| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.get_pdu(
|
|
||||||
state.get(
|
|
||||||
&services()
|
|
||||||
.rooms
|
|
||||||
.short
|
|
||||||
.get_or_create_shortstatekey(&k.to_string().into(), s)
|
|
||||||
.ok()?,
|
|
||||||
)?,
|
|
||||||
)
|
|
||||||
.ok()?
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
warn!("Auth check failed: {e}");
|
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if !authenticated {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Auth check failed",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Saving state from send_join");
|
|
||||||
let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state(
|
|
||||||
room_id,
|
|
||||||
Arc::new(
|
|
||||||
state
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, id)| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state_compressor
|
|
||||||
.compress_state_event(k, &id)
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state
|
|
||||||
.force_state(room_id, statehash_before_join, new, removed, &state_lock)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
info!("Updating joined counts for new room");
|
|
||||||
services().rooms.state_cache.update_joined_count(room_id)?;
|
|
||||||
|
|
||||||
// We append to state before appending the pdu, so we don't have a moment in time with the
|
|
||||||
// pdu without it's state. This is okay because append_pdu can't fail.
|
|
||||||
let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?;
|
|
||||||
|
|
||||||
info!("Appending new room join event");
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.append_pdu(
|
|
||||||
&parsed_join_pdu,
|
|
||||||
join_event,
|
|
||||||
vec![(*parsed_join_pdu.event_id).to_owned()],
|
|
||||||
&state_lock,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
info!("Setting final room state for new room");
|
|
||||||
// We set the room state after inserting the pdu, so that we never have a moment in time
|
|
||||||
// where events in the current room state do not exist
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state
|
|
||||||
.set_room_state(room_id, statehash_after_join, &state_lock)?;
|
|
||||||
} else {
|
|
||||||
info!("We can join locally");
|
info!("We can join locally");
|
||||||
|
|
||||||
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
||||||
|
@ -870,10 +568,7 @@ async fn join_room_by_id_helper(
|
||||||
|
|
||||||
let restriction_rooms = match join_rules_event_content {
|
let restriction_rooms = match join_rules_event_content {
|
||||||
Some(RoomJoinRulesEventContent {
|
Some(RoomJoinRulesEventContent {
|
||||||
join_rule: JoinRule::Restricted(restricted),
|
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
|
||||||
})
|
|
||||||
| Some(RoomJoinRulesEventContent {
|
|
||||||
join_rule: JoinRule::KnockRestricted(restricted),
|
|
||||||
}) => restricted
|
}) => restricted
|
||||||
.allow
|
.allow
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -901,12 +596,12 @@ async fn join_room_by_id_helper(
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
{
|
{
|
||||||
if user.server_name() == services().globals.server_name()
|
if user.server_name() == services().globals.server_name()
|
||||||
&& services()
|
&& services().rooms.state_accessor.user_can_invite(
|
||||||
.rooms
|
room_id,
|
||||||
.state_accessor
|
&user,
|
||||||
.user_can_invite(room_id, &user, sender_user, &state_lock)
|
sender_user,
|
||||||
.await
|
&state_lock,
|
||||||
.unwrap_or(false)
|
)
|
||||||
{
|
{
|
||||||
auth_user = Some(user);
|
auth_user = Some(user);
|
||||||
break;
|
break;
|
||||||
|
@ -1056,16 +751,14 @@ async fn join_room_by_id_helper(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(signed_raw) = send_join_response.room_state.event {
|
if let Some(signed_raw) = send_join_response.room_state.event {
|
||||||
let (signed_event_id, signed_value) =
|
let Ok((signed_event_id, signed_value)) =
|
||||||
match gen_event_id_canonical_json(&signed_raw, &room_version_id) {
|
gen_event_id_canonical_json(&signed_raw, &room_version_id)
|
||||||
Ok(t) => t,
|
else {
|
||||||
Err(_) => {
|
|
||||||
// Event could not be converted to canonical json
|
// Event could not be converted to canonical json
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Could not convert event to canonical json.",
|
"Could not convert event to canonical json.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if signed_event_id != event_id {
|
if signed_event_id != event_id {
|
||||||
|
@ -1095,6 +788,304 @@ async fn join_room_by_id_helper(
|
||||||
} else {
|
} else {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
info!("Joining {room_id} over federation.");
|
||||||
|
|
||||||
|
let (make_join_response, remote_server) =
|
||||||
|
make_join_request(sender_user, room_id, servers).await?;
|
||||||
|
|
||||||
|
info!("make_join finished");
|
||||||
|
|
||||||
|
let room_version_id = match make_join_response.room_version {
|
||||||
|
Some(room_version)
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.supported_room_versions()
|
||||||
|
.contains(&room_version) =>
|
||||||
|
{
|
||||||
|
room_version
|
||||||
|
}
|
||||||
|
_ => return Err(Error::BadServerResponse("Room version is not supported")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut join_event_stub: CanonicalJsonObject =
|
||||||
|
serde_json::from_str(make_join_response.event.get()).map_err(|_| {
|
||||||
|
Error::BadServerResponse("Invalid make_join event json received from server.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let join_authorized_via_users_server = join_event_stub
|
||||||
|
.get("content")
|
||||||
|
.map(|s| {
|
||||||
|
s.as_object()?
|
||||||
|
.get("join_authorised_via_users_server")?
|
||||||
|
.as_str()
|
||||||
|
})
|
||||||
|
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok());
|
||||||
|
|
||||||
|
// TODO: Is origin needed?
|
||||||
|
join_event_stub.insert(
|
||||||
|
"origin".to_owned(),
|
||||||
|
CanonicalJsonValue::String(services().globals.server_name().as_str().to_owned()),
|
||||||
|
);
|
||||||
|
join_event_stub.insert(
|
||||||
|
"origin_server_ts".to_owned(),
|
||||||
|
CanonicalJsonValue::Integer(
|
||||||
|
utils::millis_since_unix_epoch()
|
||||||
|
.try_into()
|
||||||
|
.expect("Timestamp is valid js_int value"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
join_event_stub.insert(
|
||||||
|
"content".to_owned(),
|
||||||
|
to_canonical_value(RoomMemberEventContent {
|
||||||
|
membership: MembershipState::Join,
|
||||||
|
displayname: services().users.displayname(sender_user)?,
|
||||||
|
avatar_url: services().users.avatar_url(sender_user)?,
|
||||||
|
is_direct: None,
|
||||||
|
third_party_invite: None,
|
||||||
|
blurhash: services().users.blurhash(sender_user)?,
|
||||||
|
reason,
|
||||||
|
join_authorized_via_users_server,
|
||||||
|
})
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms
|
||||||
|
join_event_stub.remove("event_id");
|
||||||
|
|
||||||
|
// In order to create a compatible ref hash (EventID) the `hashes` field needs to be present
|
||||||
|
ruma::signatures::hash_and_sign_event(
|
||||||
|
services().globals.server_name().as_str(),
|
||||||
|
services().globals.keypair(),
|
||||||
|
&mut join_event_stub,
|
||||||
|
&room_version_id,
|
||||||
|
)
|
||||||
|
.expect("event is valid, we just created it");
|
||||||
|
|
||||||
|
// Generate event id
|
||||||
|
let event_id = format!(
|
||||||
|
"${}",
|
||||||
|
ruma::signatures::reference_hash(&join_event_stub, &room_version_id)
|
||||||
|
.expect("ruma can calculate reference hashes")
|
||||||
|
);
|
||||||
|
let event_id = <&EventId>::try_from(event_id.as_str())
|
||||||
|
.expect("ruma's reference hashes are valid event ids");
|
||||||
|
|
||||||
|
// Add event_id back
|
||||||
|
join_event_stub.insert(
|
||||||
|
"event_id".to_owned(),
|
||||||
|
CanonicalJsonValue::String(event_id.as_str().to_owned()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// It has enough fields to be called a proper event now
|
||||||
|
let mut join_event = join_event_stub;
|
||||||
|
|
||||||
|
info!("Asking {remote_server} for send_join");
|
||||||
|
let send_join_response = services()
|
||||||
|
.sending
|
||||||
|
.send_federation_request(
|
||||||
|
&remote_server,
|
||||||
|
federation::membership::create_join_event::v2::Request {
|
||||||
|
room_id: room_id.to_owned(),
|
||||||
|
event_id: event_id.to_owned(),
|
||||||
|
pdu: PduEvent::convert_to_outgoing_federation_event(join_event.clone()),
|
||||||
|
omit_members: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("send_join finished");
|
||||||
|
|
||||||
|
if let Some(signed_raw) = &send_join_response.room_state.event {
|
||||||
|
info!("There is a signed event. This room is probably using restricted joins. Adding signature to our event");
|
||||||
|
let Ok((signed_event_id, signed_value)) =
|
||||||
|
gen_event_id_canonical_json(signed_raw, &room_version_id)
|
||||||
|
else {
|
||||||
|
// Event could not be converted to canonical json
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Could not convert event to canonical json.",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
if signed_event_id != event_id {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Server sent event with wrong event id",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
match signed_value["signatures"]
|
||||||
|
.as_object()
|
||||||
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Server sent invalid signatures type",
|
||||||
|
))
|
||||||
|
.and_then(|e| {
|
||||||
|
e.get(remote_server.as_str()).ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Server did not send its signature",
|
||||||
|
))
|
||||||
|
}) {
|
||||||
|
Ok(signature) => {
|
||||||
|
join_event
|
||||||
|
.get_mut("signatures")
|
||||||
|
.expect("we created a valid pdu")
|
||||||
|
.as_object_mut()
|
||||||
|
.expect("we created a valid pdu")
|
||||||
|
.insert(remote_server.to_string(), signature.clone());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Server {remote_server} sent invalid signature in sendjoin signatures for event {signed_value:?}: {e:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
services().rooms.short.get_or_create_shortroomid(room_id)?;
|
||||||
|
|
||||||
|
info!("Parsing join event");
|
||||||
|
let parsed_join_pdu = PduEvent::from_id_val(event_id, join_event.clone())
|
||||||
|
.map_err(|_| Error::BadServerResponse("Invalid join event PDU."))?;
|
||||||
|
|
||||||
|
let mut state = HashMap::new();
|
||||||
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
|
|
||||||
|
info!("Fetching join signing keys");
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.event_handler
|
||||||
|
.fetch_join_signing_keys(&send_join_response, &room_version_id, &pub_key_map)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("Going through send_join response room_state");
|
||||||
|
for result in send_join_response
|
||||||
|
.room_state
|
||||||
|
.state
|
||||||
|
.iter()
|
||||||
|
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
|
||||||
|
{
|
||||||
|
let Ok((event_id, value)) = result.await else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
||||||
|
warn!("Invalid PDU in send_join response: {} {:?}", e, value);
|
||||||
|
Error::BadServerResponse("Invalid PDU in send_join response.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.outlier
|
||||||
|
.add_pdu_outlier(&event_id, &value)?;
|
||||||
|
if let Some(state_key) = &pdu.state_key {
|
||||||
|
let shortstatekey = services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
|
||||||
|
state.insert(shortstatekey, pdu.event_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Going through send_join response auth_chain");
|
||||||
|
for result in send_join_response
|
||||||
|
.room_state
|
||||||
|
.auth_chain
|
||||||
|
.iter()
|
||||||
|
.map(|pdu| validate_and_add_event_id(pdu, &room_version_id, &pub_key_map))
|
||||||
|
{
|
||||||
|
let Ok((event_id, value)) = result.await else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.outlier
|
||||||
|
.add_pdu_outlier(&event_id, &value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Running send_join auth check");
|
||||||
|
let authenticated = state_res::event_auth::auth_check(
|
||||||
|
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
||||||
|
&parsed_join_pdu,
|
||||||
|
None::<PduEvent>, // TODO: third party invite
|
||||||
|
|k, s| {
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.get_pdu(
|
||||||
|
state.get(
|
||||||
|
&services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_or_create_shortstatekey(&k.to_string().into(), s)
|
||||||
|
.ok()?,
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.ok()?
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Auth check failed: {e}");
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !authenticated {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Auth check failed",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Saving state from send_join");
|
||||||
|
let (statehash_before_join, new, removed) = services().rooms.state_compressor.save_state(
|
||||||
|
room_id,
|
||||||
|
Arc::new(
|
||||||
|
state
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, id)| {
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.state_compressor
|
||||||
|
.compress_state_event(k, &id)
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.state
|
||||||
|
.force_state(room_id, statehash_before_join, new, removed, &state_lock)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("Updating joined counts for new room");
|
||||||
|
services().rooms.state_cache.update_joined_count(room_id)?;
|
||||||
|
|
||||||
|
// We append to state before appending the pdu, so we don't have a moment in time with the
|
||||||
|
// pdu without it's state. This is okay because append_pdu can't fail.
|
||||||
|
let statehash_after_join = services().rooms.state.append_to_state(&parsed_join_pdu)?;
|
||||||
|
|
||||||
|
info!("Appending new room join event");
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.append_pdu(
|
||||||
|
&parsed_join_pdu,
|
||||||
|
join_event,
|
||||||
|
vec![(*parsed_join_pdu.event_id).to_owned()],
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("Setting final room state for new room");
|
||||||
|
// We set the room state after inserting the pdu, so that we never have a moment in time
|
||||||
|
// where events in the current room state do not exist
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.state
|
||||||
|
.set_room_state(room_id, statehash_after_join, &state_lock)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
||||||
|
@ -1275,16 +1266,13 @@ pub(crate) async fn invite_helper<'a>(
|
||||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
|
|
||||||
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
||||||
let (event_id, value) = match gen_event_id_canonical_json(&response.event, &room_version_id)
|
let Ok((event_id, value)) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||||
{
|
else {
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => {
|
|
||||||
// Event could not be converted to canonical json
|
// Event could not be converted to canonical json
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Could not convert event to canonical json.",
|
"Could not convert event to canonical json.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if *pdu.event_id != *event_id {
|
if *pdu.event_id != *event_id {
|
||||||
|
@ -1393,10 +1381,7 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for room_id in all_rooms {
|
for room_id in all_rooms {
|
||||||
let room_id = match room_id {
|
let Ok(room_id) = room_id else { continue };
|
||||||
Ok(room_id) => room_id,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = leave_room(user_id, &room_id, None).await;
|
let _ = leave_room(user_id, &room_id, None).await;
|
||||||
}
|
}
|
||||||
|
@ -1406,35 +1391,11 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||||
|
|
||||||
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||||
// Ask a remote server if we don't have this room
|
// Ask a remote server if we don't have this room
|
||||||
if !services()
|
if services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.server_in_room(services().globals.server_name(), room_id)?
|
.server_in_room(services().globals.server_name(), room_id)?
|
||||||
{
|
{
|
||||||
if let Err(e) = remote_leave_room(user_id, room_id).await {
|
|
||||||
warn!("Failed to leave room {} remotely: {}", user_id, e);
|
|
||||||
// Don't tell the client about this error
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_state = services()
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.invite_state(user_id, room_id)?
|
|
||||||
.map_or_else(
|
|
||||||
|| services().rooms.state_cache.left_state(user_id, room_id),
|
|
||||||
|s| Ok(Some(s)),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// We always drop the invite, we can't rely on other servers
|
|
||||||
services().rooms.state_cache.update_membership(
|
|
||||||
room_id,
|
|
||||||
user_id,
|
|
||||||
MembershipState::Leave,
|
|
||||||
user_id,
|
|
||||||
last_state,
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
let mutex_state = Arc::clone(
|
let mutex_state = Arc::clone(
|
||||||
services()
|
services()
|
||||||
.globals
|
.globals
|
||||||
|
@ -1493,6 +1454,30 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
} else {
|
||||||
|
if let Err(e) = remote_leave_room(user_id, room_id).await {
|
||||||
|
warn!("Failed to leave room {} remotely: {}", user_id, e);
|
||||||
|
// Don't tell the client about this error
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_state = services()
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.invite_state(user_id, room_id)?
|
||||||
|
.map_or_else(
|
||||||
|
|| services().rooms.state_cache.left_state(user_id, room_id),
|
||||||
|
|s| Ok(Some(s)),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// We always drop the invite, we can't rely on other servers
|
||||||
|
services().rooms.state_cache.update_membership(
|
||||||
|
room_id,
|
||||||
|
user_id,
|
||||||
|
MembershipState::Leave,
|
||||||
|
user_id,
|
||||||
|
last_state,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1516,7 +1501,7 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
||||||
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
||||||
.filter_map(|sender| sender.as_str().map(|s| s.to_owned()))
|
.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
|
||||||
.filter_map(|sender| UserId::parse(sender).ok())
|
.filter_map(|sender| UserId::parse(sender).ok())
|
||||||
.map(|user| user.server_name().to_owned())
|
.map(|user| user.server_name().to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -110,7 +110,7 @@ pub async fn send_message_event_route(
|
||||||
/// Allows paginating through room history.
|
/// Allows paginating through room history.
|
||||||
///
|
///
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events where the user was
|
/// - Only works if the user is joined (TODO: always allow, but only show events where the user was
|
||||||
/// joined, depending on history_visibility)
|
/// joined, depending on `history_visibility`)
|
||||||
pub async fn get_message_events_route(
|
pub async fn get_message_events_route(
|
||||||
body: Ruma<get_message_events::v3::Request>,
|
body: Ruma<get_message_events::v3::Request>,
|
||||||
) -> Result<get_message_events::v3::Response> {
|
) -> Result<get_message_events::v3::Response> {
|
||||||
|
|
|
@ -147,7 +147,7 @@ pub async fn get_displayname_route(
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url`
|
/// # `PUT /_matrix/client/r0/profile/{userId}/avatar_url`
|
||||||
///
|
///
|
||||||
/// Updates the avatar_url and blurhash.
|
/// Updates the `avatar_url` and blurhash.
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub async fn set_avatar_url_route(
|
pub async fn set_avatar_url_route(
|
||||||
|
@ -252,9 +252,9 @@ pub async fn set_avatar_url_route(
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/profile/{userId}/avatar_url`
|
/// # `GET /_matrix/client/r0/profile/{userId}/avatar_url`
|
||||||
///
|
///
|
||||||
/// Returns the avatar_url and blurhash of the user.
|
/// Returns the `avatar_url` and blurhash of the user.
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches avatar_url and blurhash over federation
|
/// - If user is on another server: Fetches `avatar_url` and blurhash over federation
|
||||||
pub async fn get_avatar_url_route(
|
pub async fn get_avatar_url_route(
|
||||||
body: Ruma<get_avatar_url::v3::Request>,
|
body: Ruma<get_avatar_url::v3::Request>,
|
||||||
) -> Result<get_avatar_url::v3::Response> {
|
) -> Result<get_avatar_url::v3::Response> {
|
||||||
|
@ -284,7 +284,7 @@ pub async fn get_avatar_url_route(
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/profile/{userId}`
|
/// # `GET /_matrix/client/r0/profile/{userId}`
|
||||||
///
|
///
|
||||||
/// Returns the displayname, avatar_url and blurhash of the user.
|
/// Returns the displayname, `avatar_url` and blurhash of the user.
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches profile over federation
|
/// - If user is on another server: Fetches profile over federation
|
||||||
pub async fn get_profile_route(
|
pub async fn get_profile_route(
|
||||||
|
|
|
@ -281,7 +281,7 @@ pub async fn get_pushrule_enabled_route(
|
||||||
let global = account_data.content.global;
|
let global = account_data.content.global;
|
||||||
let enabled = global
|
let enabled = global
|
||||||
.get(body.kind.clone(), &body.rule_id)
|
.get(body.kind.clone(), &body.rule_id)
|
||||||
.map(|r| r.enabled())
|
.map(ruma::push::AnyPushRuleRef::enabled)
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Push rule not found.",
|
"Push rule not found.",
|
||||||
|
|
|
@ -140,7 +140,7 @@ pub async fn create_receipt_route(
|
||||||
receipts.insert(ReceiptType::Read, user_receipts);
|
receipts.insert(ReceiptType::Read, user_receipts);
|
||||||
|
|
||||||
let mut receipt_content = BTreeMap::new();
|
let mut receipt_content = BTreeMap::new();
|
||||||
receipt_content.insert(body.event_id.to_owned(), receipts);
|
receipt_content.insert(body.event_id.clone(), receipts);
|
||||||
|
|
||||||
services().rooms.edus.read_receipt.readreceipt_update(
|
services().rooms.edus.read_receipt.readreceipt_update(
|
||||||
sender_user,
|
sender_user,
|
||||||
|
|
|
@ -39,8 +39,8 @@ pub async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
Some(body.event_type.clone()),
|
&Some(body.event_type.clone()),
|
||||||
Some(body.rel_type.clone()),
|
&Some(body.rel_type.clone()),
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
limit,
|
limit,
|
||||||
|
@ -89,8 +89,8 @@ pub async fn get_relating_events_with_rel_type_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
None,
|
&None,
|
||||||
Some(body.rel_type.clone()),
|
&Some(body.rel_type.clone()),
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
limit,
|
limit,
|
||||||
|
@ -137,8 +137,8 @@ pub async fn get_relating_events_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
None,
|
&None,
|
||||||
None,
|
&None,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
limit,
|
limit,
|
||||||
|
|
|
@ -14,14 +14,11 @@ pub async fn report_event_route(
|
||||||
) -> Result<report_content::v3::Response> {
|
) -> Result<report_content::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&body.event_id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&body.event_id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
_ => {
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Invalid Event ID",
|
"Invalid Event ID",
|
||||||
))
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(true) = body.score.map(|s| s > int!(0) || s < int!(-100)) {
|
if let Some(true) = body.score.map(|s| s > int!(0) || s < int!(-100)) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ use tracing::{info, warn};
|
||||||
/// Creates a new room.
|
/// Creates a new room.
|
||||||
///
|
///
|
||||||
/// - Room ID is randomly generated
|
/// - Room ID is randomly generated
|
||||||
/// - Create alias if room_alias_name is set
|
/// - Create alias if `room_alias_name` is set
|
||||||
/// - Send create event
|
/// - Send create event
|
||||||
/// - Join sender user
|
/// - Join sender user
|
||||||
/// - Send power levels event
|
/// - Send power levels event
|
||||||
|
@ -228,7 +228,7 @@ pub async fn create_room_route(
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: TimelineEventType::RoomCreate,
|
||||||
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -269,9 +269,10 @@ pub async fn create_room_route(
|
||||||
|
|
||||||
// Figure out preset. We need it for preset specific events
|
// Figure out preset. We need it for preset specific events
|
||||||
let preset = body.preset.clone().unwrap_or(match &body.visibility {
|
let preset = body.preset.clone().unwrap_or(match &body.visibility {
|
||||||
room::Visibility::Private => RoomPreset::PrivateChat,
|
|
||||||
room::Visibility::Public => RoomPreset::PublicChat,
|
room::Visibility::Public => RoomPreset::PublicChat,
|
||||||
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
// Room visibility is set to private, or custom
|
||||||
|
// Room visibility should not be custom
|
||||||
|
_ => RoomPreset::PrivateChat,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut users = BTreeMap::new();
|
let mut users = BTreeMap::new();
|
||||||
|
@ -309,7 +310,7 @@ pub async fn create_room_route(
|
||||||
content: to_raw_value(&power_levels_content)
|
content: to_raw_value(&power_levels_content)
|
||||||
.expect("to_raw_value always works on serde_json::Value"),
|
.expect("to_raw_value always works on serde_json::Value"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -332,7 +333,7 @@ pub async fn create_room_route(
|
||||||
})
|
})
|
||||||
.expect("We checked that alias earlier, it must be fine"),
|
.expect("We checked that alias earlier, it must be fine"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -358,7 +359,7 @@ pub async fn create_room_route(
|
||||||
}))
|
}))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -379,7 +380,7 @@ pub async fn create_room_route(
|
||||||
))
|
))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -401,7 +402,7 @@ pub async fn create_room_route(
|
||||||
}))
|
}))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -418,7 +419,7 @@ pub async fn create_room_route(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Implicit state key defaults to ""
|
// Implicit state key defaults to ""
|
||||||
pdu_builder.state_key.get_or_insert_with(|| "".to_owned());
|
pdu_builder.state_key.get_or_insert_with(String::new);
|
||||||
|
|
||||||
// Silently skip encryption events if they are not allowed
|
// Silently skip encryption events if they are not allowed
|
||||||
if pdu_builder.event_type == TimelineEventType::RoomEncryption
|
if pdu_builder.event_type == TimelineEventType::RoomEncryption
|
||||||
|
@ -445,7 +446,7 @@ pub async fn create_room_route(
|
||||||
content: to_raw_value(&RoomNameEventContent::new(name.clone()))
|
content: to_raw_value(&RoomNameEventContent::new(name.clone()))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -467,7 +468,7 @@ pub async fn create_room_route(
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -539,7 +540,7 @@ pub async fn get_room_event_route(
|
||||||
///
|
///
|
||||||
/// Lists all aliases of the room.
|
/// Lists all aliases of the room.
|
||||||
///
|
///
|
||||||
/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable
|
/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if `history_visibility` is world readable
|
||||||
pub async fn get_room_aliases_route(
|
pub async fn get_room_aliases_route(
|
||||||
body: Ruma<aliases::v3::Request>,
|
body: Ruma<aliases::v3::Request>,
|
||||||
) -> Result<aliases::v3::Response> {
|
) -> Result<aliases::v3::Response> {
|
||||||
|
@ -624,7 +625,7 @@ pub async fn upgrade_room_route(
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -725,7 +726,7 @@ pub async fn upgrade_room_route(
|
||||||
content: to_raw_value(&create_event_content)
|
content: to_raw_value(&create_event_content)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -795,7 +796,7 @@ pub async fn upgrade_room_route(
|
||||||
event_type: event_type.to_string().into(),
|
event_type: event_type.to_string().into(),
|
||||||
content: event_content,
|
content: event_content,
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -845,7 +846,7 @@ pub async fn upgrade_room_route(
|
||||||
content: to_raw_value(&power_levels_event_content)
|
content: to_raw_value(&power_levels_event_content)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
|
|
@ -3,7 +3,13 @@ use crate::{services, utils, Error, Result, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
session::{get_login_types, login, logout, logout_all},
|
session::{
|
||||||
|
get_login_types::{
|
||||||
|
self,
|
||||||
|
v3::{ApplicationServiceLoginType, PasswordLoginType},
|
||||||
|
},
|
||||||
|
login, logout, logout_all,
|
||||||
|
},
|
||||||
uiaa::UserIdentifier,
|
uiaa::UserIdentifier,
|
||||||
},
|
},
|
||||||
UserId,
|
UserId,
|
||||||
|
@ -25,8 +31,8 @@ pub async fn get_login_types_route(
|
||||||
_body: Ruma<get_login_types::v3::Request>,
|
_body: Ruma<get_login_types::v3::Request>,
|
||||||
) -> Result<get_login_types::v3::Response> {
|
) -> Result<get_login_types::v3::Response> {
|
||||||
Ok(get_login_types::v3::Response::new(vec![
|
Ok(get_login_types::v3::Response::new(vec![
|
||||||
get_login_types::v3::LoginType::Password(Default::default()),
|
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
||||||
get_login_types::v3::LoginType::ApplicationService(Default::default()),
|
get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{services, Result, Ruma};
|
||||||
use ruma::api::client::space::get_hierarchy;
|
use ruma::api::client::space::get_hierarchy;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy``
|
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
||||||
///
|
///
|
||||||
/// Paginates over the space tree in a depth-first manner to locate child rooms of a given space.
|
/// Paginates over the space tree in a depth-first manner to locate child rooms of a given space.
|
||||||
pub async fn get_hierarchy_route(
|
pub async fn get_hierarchy_route(
|
||||||
|
|
|
@ -20,7 +20,7 @@ use tracing::log::warn;
|
||||||
///
|
///
|
||||||
/// - The only requirement for the content is that it has to be valid json
|
/// - The only requirement for the content is that it has to be valid json
|
||||||
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
||||||
/// - If event is new canonical_alias: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub async fn send_state_event_for_key_route(
|
pub async fn send_state_event_for_key_route(
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<send_state_event::v3::Response> {
|
) -> Result<send_state_event::v3::Response> {
|
||||||
|
@ -31,7 +31,7 @@ pub async fn send_state_event_for_key_route(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_type,
|
&body.event_type,
|
||||||
&body.body.body, // Yes, I hate it too
|
&body.body.body, // Yes, I hate it too
|
||||||
body.state_key.to_owned(),
|
body.state_key.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub async fn send_state_event_for_key_route(
|
||||||
///
|
///
|
||||||
/// - The only requirement for the content is that it has to be valid json
|
/// - The only requirement for the content is that it has to be valid json
|
||||||
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
||||||
/// - If event is new canonical_alias: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub async fn send_state_event_for_empty_key_route(
|
pub async fn send_state_event_for_empty_key_route(
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||||
|
@ -64,7 +64,7 @@ pub async fn send_state_event_for_empty_key_route(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_type.to_string().into(),
|
&body.event_type.to_string().into(),
|
||||||
&body.body.body,
|
&body.body.body,
|
||||||
body.state_key.to_owned(),
|
body.state_key.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ use tracing::{error, info};
|
||||||
/// - If the user was invited after `since`: A subset of the state of the room at the point of the invite
|
/// - If the user was invited after `since`: A subset of the state of the room at the point of the invite
|
||||||
///
|
///
|
||||||
/// For left rooms:
|
/// For left rooms:
|
||||||
/// - If the user left after `since`: prev_batch token, empty state (TODO: subset of the state at the point of the leave)
|
/// - If the user left after `since`: `prev_batch` token, empty state (TODO: subset of the state at the point of the leave)
|
||||||
///
|
///
|
||||||
/// - Sync is handled in an async task, multiple requests from the same device with the same
|
/// - Sync is handled in an async task, multiple requests from the same device with the same
|
||||||
/// `since` will be cached
|
/// `since` will be cached
|
||||||
|
@ -83,7 +83,7 @@ pub async fn sync_events_route(
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
let (tx, rx) = tokio::sync::watch::channel(None);
|
let (tx, rx) = tokio::sync::watch::channel(None);
|
||||||
|
|
||||||
v.insert((body.since.to_owned(), rx.clone()));
|
v.insert((body.since.clone(), rx.clone()));
|
||||||
|
|
||||||
tokio::spawn(sync_helper_wrapper(
|
tokio::spawn(sync_helper_wrapper(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
|
@ -95,7 +95,9 @@ pub async fn sync_events_route(
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
Entry::Occupied(mut o) => {
|
Entry::Occupied(mut o) => {
|
||||||
if o.get().0 != body.since {
|
if o.get().0 == body.since {
|
||||||
|
o.get().1.clone()
|
||||||
|
} else {
|
||||||
let (tx, rx) = tokio::sync::watch::channel(None);
|
let (tx, rx) = tokio::sync::watch::channel(None);
|
||||||
|
|
||||||
o.insert((body.since.clone(), rx.clone()));
|
o.insert((body.since.clone(), rx.clone()));
|
||||||
|
@ -110,8 +112,6 @@ pub async fn sync_events_route(
|
||||||
));
|
));
|
||||||
|
|
||||||
rx
|
rx
|
||||||
} else {
|
|
||||||
o.get().1.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -198,7 +198,7 @@ async fn sync_helper(
|
||||||
LazyLoadOptions::Enabled {
|
LazyLoadOptions::Enabled {
|
||||||
include_redundant_members: redundant,
|
include_redundant_members: redundant,
|
||||||
} => (true, redundant),
|
} => (true, redundant),
|
||||||
_ => (false, false),
|
LazyLoadOptions::Disabled => (false, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let full_state = body.full_state;
|
let full_state = body.full_state;
|
||||||
|
@ -376,28 +376,23 @@ async fn sync_helper(
|
||||||
None => HashMap::new(),
|
None => HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let left_event_id = match services().rooms.state_accessor.room_state_get_id(
|
let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id(
|
||||||
&room_id,
|
&room_id,
|
||||||
&StateEventType::RoomMember,
|
&StateEventType::RoomMember,
|
||||||
sender_user.as_str(),
|
sender_user.as_str(),
|
||||||
)? {
|
)?
|
||||||
Some(e) => e,
|
else {
|
||||||
None => {
|
|
||||||
error!("Left room but no left state event");
|
error!("Left room but no left state event");
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let left_shortstatehash = match services()
|
let Some(left_shortstatehash) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.pdu_shortstatehash(&left_event_id)?
|
.pdu_shortstatehash(&left_event_id)?
|
||||||
{
|
else {
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
error!("Leave event has no state");
|
error!("Leave event has no state");
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut left_state_ids = services()
|
let mut left_state_ids = services()
|
||||||
|
@ -425,12 +420,9 @@ async fn sync_helper(
|
||||||
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||||
|| *sender_user == state_key
|
|| *sender_user == state_key
|
||||||
{
|
{
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
left_state_events.push(pdu.to_sync_state_event());
|
left_state_events.push(pdu.to_sync_state_event());
|
||||||
|
@ -648,10 +640,8 @@ async fn load_joined_room(
|
||||||
|
|
||||||
// Database queries:
|
// Database queries:
|
||||||
|
|
||||||
let current_shortstatehash =
|
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)?
|
||||||
if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? {
|
else {
|
||||||
s
|
|
||||||
} else {
|
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
return Err(Error::BadDatabase("Room has no state"));
|
return Err(Error::BadDatabase("Room has no state"));
|
||||||
};
|
};
|
||||||
|
@ -788,12 +778,9 @@ async fn load_joined_room(
|
||||||
.get_statekey_from_short(shortstatekey)?;
|
.get_statekey_from_short(shortstatekey)?;
|
||||||
|
|
||||||
if event_type != StateEventType::RoomMember {
|
if event_type != StateEventType::RoomMember {
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
state_events.push(pdu);
|
state_events.push(pdu);
|
||||||
|
|
||||||
|
@ -807,12 +794,9 @@ async fn load_joined_room(
|
||||||
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||||
|| *sender_user == state_key
|
|| *sender_user == state_key
|
||||||
{
|
{
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This check is in case a bad user ID made it into the database
|
// This check is in case a bad user ID made it into the database
|
||||||
|
@ -877,12 +861,9 @@ async fn load_joined_room(
|
||||||
|
|
||||||
for (key, id) in current_state_ids {
|
for (key, id) in current_state_ids {
|
||||||
if full_state || since_state_ids.get(&key) != Some(&id) {
|
if full_state || since_state_ids.get(&key) != Some(&id) {
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if pdu.kind == TimelineEventType::RoomMember {
|
if pdu.kind == TimelineEventType::RoomMember {
|
||||||
|
@ -1248,7 +1229,7 @@ pub async fn sync_events_v4_route(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
sender_device.clone(),
|
sender_device.clone(),
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1286,10 +1267,9 @@ pub async fn sync_events_v4_route(
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in &all_joined_rooms {
|
for room_id in &all_joined_rooms {
|
||||||
let current_shortstatehash =
|
let Some(current_shortstatehash) =
|
||||||
if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? {
|
services().rooms.state.get_room_shortstatehash(room_id)?
|
||||||
s
|
else {
|
||||||
} else {
|
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -1354,12 +1334,9 @@ pub async fn sync_events_v4_route(
|
||||||
|
|
||||||
for (key, id) in current_state_ids {
|
for (key, id) in current_state_ids {
|
||||||
if since_state_ids.get(&key) != Some(&id) {
|
if since_state_ids.get(&key) != Some(&id) {
|
||||||
let pdu = match services().rooms.timeline.get_pdu(&id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
|
||||||
Some(pdu) => pdu,
|
|
||||||
None => {
|
|
||||||
error!("Pdu in state not found: {}", id);
|
error!("Pdu in state not found: {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if pdu.kind == TimelineEventType::RoomMember {
|
if pdu.kind == TimelineEventType::RoomMember {
|
||||||
if let Some(state_key) = &pdu.state_key {
|
if let Some(state_key) = &pdu.state_key {
|
||||||
|
@ -1599,10 +1576,10 @@ pub async fn sync_events_v4_route(
|
||||||
}))
|
}))
|
||||||
})?
|
})?
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if roomsince != &0 {
|
if roomsince == &0 {
|
||||||
Some(roomsince.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
None
|
||||||
|
} else {
|
||||||
|
Some(roomsince.to_string())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1613,7 +1590,7 @@ pub async fn sync_events_v4_route(
|
||||||
|
|
||||||
let required_state = required_state_request
|
let required_state = required_state_request
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|state| {
|
.filter_map(|state| {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
|
@ -1631,7 +1608,7 @@ pub async fn sync_events_v4_route(
|
||||||
.room_members(room_id)
|
.room_members(room_id)
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
.filter(|member| member != &sender_user)
|
.filter(|member| member != &sender_user)
|
||||||
.flat_map(|member| {
|
.filter_map(|member| {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
|
|
|
@ -24,18 +24,19 @@ pub async fn update_tag_route(
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut tags_event = event
|
let mut tags_event = event.map_or_else(
|
||||||
.map(|e| {
|
|| {
|
||||||
serde_json::from_str(e.get())
|
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
Ok(TagEvent {
|
Ok(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent {
|
||||||
tags: BTreeMap::new(),
|
tags: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})?;
|
},
|
||||||
|
|e| {
|
||||||
|
serde_json::from_str(e.get())
|
||||||
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
tags_event
|
tags_event
|
||||||
.content
|
.content
|
||||||
|
@ -68,18 +69,19 @@ pub async fn delete_tag_route(
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut tags_event = event
|
let mut tags_event = event.map_or_else(
|
||||||
.map(|e| {
|
|| {
|
||||||
serde_json::from_str(e.get())
|
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
Ok(TagEvent {
|
Ok(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent {
|
||||||
tags: BTreeMap::new(),
|
tags: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})?;
|
},
|
||||||
|
|e| {
|
||||||
|
serde_json::from_str(e.get())
|
||||||
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
tags_event.content.tags.remove(&body.tag.clone().into());
|
||||||
|
|
||||||
|
@ -107,18 +109,19 @@ pub async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tag
|
||||||
RoomAccountDataEventType::Tag,
|
RoomAccountDataEventType::Tag,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let tags_event = event
|
let tags_event = event.map_or_else(
|
||||||
.map(|e| {
|
|| {
|
||||||
serde_json::from_str(e.get())
|
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
Ok(TagEvent {
|
Ok(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent {
|
||||||
tags: BTreeMap::new(),
|
tags: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})?;
|
},
|
||||||
|
|e| {
|
||||||
|
serde_json::from_str(e.get())
|
||||||
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(get_tags::v3::Response {
|
Ok(get_tags::v3::Response {
|
||||||
tags: tags_event.content.tags,
|
tags: tags_event.content.tags,
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub async fn send_event_to_device_route(
|
||||||
event.deserialize_as().map_err(|_| {
|
event.deserialize_as().map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||||
})?,
|
})?,
|
||||||
)?
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
DeviceIdOrAllDevices::AllDevices => {
|
||||||
|
|
|
@ -17,7 +17,12 @@ pub async fn turn_server_route(
|
||||||
|
|
||||||
let turn_secret = services().globals.turn_secret().clone();
|
let turn_secret = services().globals.turn_secret().clone();
|
||||||
|
|
||||||
let (username, password) = if !turn_secret.is_empty() {
|
let (username, password) = if turn_secret.is_empty() {
|
||||||
|
(
|
||||||
|
services().globals.turn_username().clone(),
|
||||||
|
services().globals.turn_password().clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
||||||
SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()),
|
SystemTime::now() + Duration::from_secs(services().globals.turn_ttl()),
|
||||||
)
|
)
|
||||||
|
@ -32,11 +37,6 @@ pub async fn turn_server_route(
|
||||||
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
|
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
|
||||||
|
|
||||||
(username, password)
|
(username, password)
|
||||||
} else {
|
|
||||||
(
|
|
||||||
services().globals.turn_username().clone(),
|
|
||||||
services().globals.turn_password().clone(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_turn_server_info::v3::Response {
|
Ok(get_turn_server_info::v3::Response {
|
||||||
|
|
|
@ -226,7 +226,7 @@ where
|
||||||
let keys_result = services()
|
let keys_result = services()
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.to_owned()])
|
.fetch_signing_keys(&x_matrix.origin, vec![x_matrix.key.clone()])
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let keys = match keys_result {
|
let keys = match keys_result {
|
||||||
|
@ -334,8 +334,8 @@ where
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
sender_servername,
|
sender_servername,
|
||||||
appservice_info,
|
|
||||||
json_body,
|
json_body,
|
||||||
|
appservice_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ use tracing::{debug, error, warn};
|
||||||
/// (colon-plus-port if it was specified).
|
/// (colon-plus-port if it was specified).
|
||||||
///
|
///
|
||||||
/// Note: A `FedDest::Named` might contain an IP address in string form if there
|
/// Note: A `FedDest::Named` might contain an IP address in string form if there
|
||||||
/// was no port specified to construct a SocketAddr with.
|
/// was no port specified to construct a `SocketAddr` with.
|
||||||
///
|
///
|
||||||
/// # Examples:
|
/// # Examples:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -344,7 +344,7 @@ fn add_port_to_hostname(destination_str: &str) -> FedDest {
|
||||||
FedDest::Named(host.to_owned(), port.to_owned())
|
FedDest::Named(host.to_owned(), port.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns: actual_destination, host header
|
/// Returns: `actual_destination`, host header
|
||||||
/// Implemented according to the specification at <https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names>
|
/// Implemented according to the specification at <https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names>
|
||||||
/// Numbers in comments below refer to bullet points in linked section of specification
|
/// Numbers in comments below refer to bullet points in linked section of specification
|
||||||
async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) {
|
async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDest) {
|
||||||
|
@ -363,8 +363,7 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
|
||||||
FedDest::Named(host.to_owned(), port.to_owned())
|
FedDest::Named(host.to_owned(), port.to_owned())
|
||||||
} else {
|
} else {
|
||||||
debug!("Requesting well known for {destination}");
|
debug!("Requesting well known for {destination}");
|
||||||
match request_well_known(destination.as_str()).await {
|
if let Some(delegated_hostname) = request_well_known(destination.as_str()).await {
|
||||||
Some(delegated_hostname) => {
|
|
||||||
debug!("3: A .well-known file is available");
|
debug!("3: A .well-known file is available");
|
||||||
hostname = add_port_to_hostname(&delegated_hostname).into_uri_string();
|
hostname = add_port_to_hostname(&delegated_hostname).into_uri_string();
|
||||||
match get_ip_with_port(&delegated_hostname) {
|
match get_ip_with_port(&delegated_hostname) {
|
||||||
|
@ -416,11 +415,9 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
debug!("4: No .well-known or an error occured");
|
debug!("4: No .well-known or an error occured");
|
||||||
match query_srv_record(&destination_str).await {
|
if let Some(hostname_override) = query_srv_record(&destination_str).await {
|
||||||
Some(hostname_override) => {
|
|
||||||
debug!("4: SRV record found");
|
debug!("4: SRV record found");
|
||||||
let force_port = hostname_override.port();
|
let force_port = hostname_override.port();
|
||||||
|
|
||||||
|
@ -437,10 +434,7 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(
|
.insert(
|
||||||
hostname.clone(),
|
hostname.clone(),
|
||||||
(
|
(override_ip.iter().collect(), force_port.unwrap_or(8448)),
|
||||||
override_ip.iter().collect(),
|
|
||||||
force_port.unwrap_or(8448),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!("Using SRV record, but could not resolve to IP");
|
warn!("Using SRV record, but could not resolve to IP");
|
||||||
|
@ -451,16 +445,13 @@ async fn find_actual_destination(destination: &'_ ServerName) -> (FedDest, FedDe
|
||||||
} else {
|
} else {
|
||||||
add_port_to_hostname(&hostname)
|
add_port_to_hostname(&hostname)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
debug!("5: No SRV record found");
|
debug!("5: No SRV record found");
|
||||||
add_port_to_hostname(&destination_str)
|
add_port_to_hostname(&destination_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
debug!("Actual destination: {actual_destination:?}");
|
debug!("Actual destination: {actual_destination:?}");
|
||||||
|
|
||||||
|
@ -660,15 +651,12 @@ pub fn parse_incoming_pdu(
|
||||||
|
|
||||||
let room_version_id = services().rooms.state.get_room_version(&room_id)?;
|
let room_version_id = services().rooms.state.get_room_version(&room_id)?;
|
||||||
|
|
||||||
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
|
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => {
|
|
||||||
// Event could not be converted to canonical json
|
// Event could not be converted to canonical json
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Could not convert event to canonical json.",
|
"Could not convert event to canonical json.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok((event_id, value, room_id))
|
Ok((event_id, value, room_id))
|
||||||
}
|
}
|
||||||
|
@ -731,7 +719,7 @@ pub async fn send_transaction_message_route(
|
||||||
.roomid_mutex_federation
|
.roomid_mutex_federation
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.entry(room_id.to_owned())
|
.entry(room_id.clone())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
let mutex_lock = mutex.lock().await;
|
let mutex_lock = mutex.lock().await;
|
||||||
|
@ -777,7 +765,6 @@ pub async fn send_transaction_message_route(
|
||||||
.filter_map(|edu| serde_json::from_str::<Edu>(edu.json().get()).ok())
|
.filter_map(|edu| serde_json::from_str::<Edu>(edu.json().get()).ok())
|
||||||
{
|
{
|
||||||
match edu {
|
match edu {
|
||||||
Edu::Presence(_) => {}
|
|
||||||
Edu::Receipt(receipt) => {
|
Edu::Receipt(receipt) => {
|
||||||
for (room_id, room_updates) in receipt.receipts {
|
for (room_id, room_updates) in receipt.receipts {
|
||||||
for (user_id, user_updates) in room_updates.read {
|
for (user_id, user_updates) in room_updates.read {
|
||||||
|
@ -881,7 +868,7 @@ pub async fn send_transaction_message_route(
|
||||||
"Event is invalid",
|
"Event is invalid",
|
||||||
)
|
)
|
||||||
})?,
|
})?,
|
||||||
)?
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
DeviceIdOrAllDevices::AllDevices => {
|
||||||
|
@ -929,7 +916,7 @@ pub async fn send_transaction_message_route(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Edu::_Custom(_) => {}
|
Edu::_Custom(_) | Edu::Presence(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1278,15 +1265,14 @@ pub async fn get_room_state_route(
|
||||||
|
|
||||||
Ok(get_room_state::v1::Response {
|
Ok(get_room_state::v1::Response {
|
||||||
auth_chain: auth_chain_ids
|
auth_chain: auth_chain_ids
|
||||||
.filter_map(
|
.filter_map(|id| {
|
||||||
|id| match services().rooms.timeline.get_pdu_json(&id).ok()? {
|
if let Some(json) = services().rooms.timeline.get_pdu_json(&id).ok()? {
|
||||||
Some(json) => Some(PduEvent::convert_to_outgoing_federation_event(json)),
|
Some(PduEvent::convert_to_outgoing_federation_event(json))
|
||||||
None => {
|
} else {
|
||||||
error!("Could not find event json for {id} in db.");
|
error!("Could not find event json for {id} in db.");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.collect(),
|
.collect(),
|
||||||
pdus,
|
pdus,
|
||||||
})
|
})
|
||||||
|
@ -1378,7 +1364,7 @@ pub async fn create_join_event_template_route(
|
||||||
.roomid_mutex_state
|
.roomid_mutex_state
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.entry(body.room_id.to_owned())
|
.entry(body.room_id.clone())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
@ -1518,15 +1504,12 @@ async fn create_join_event(
|
||||||
|
|
||||||
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
||||||
let room_version_id = services().rooms.state.get_room_version(room_id)?;
|
let room_version_id = services().rooms.state.get_room_version(room_id)?;
|
||||||
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
|
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => {
|
|
||||||
// Event could not be converted to canonical json
|
// Event could not be converted to canonical json
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Could not convert event to canonical json.",
|
"Could not convert event to canonical json.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let origin: OwnedServerName = serde_json::from_value(
|
let origin: OwnedServerName = serde_json::from_value(
|
||||||
|
@ -1841,11 +1824,11 @@ pub async fn get_profile_information_route(
|
||||||
|
|
||||||
match &body.field {
|
match &body.field {
|
||||||
Some(ProfileField::DisplayName) => {
|
Some(ProfileField::DisplayName) => {
|
||||||
displayname = services().users.displayname(&body.user_id)?
|
displayname = services().users.displayname(&body.user_id)?;
|
||||||
}
|
}
|
||||||
Some(ProfileField::AvatarUrl) => {
|
Some(ProfileField::AvatarUrl) => {
|
||||||
avatar_url = services().users.avatar_url(&body.user_id)?;
|
avatar_url = services().users.avatar_url(&body.user_id)?;
|
||||||
blurhash = services().users.blurhash(&body.user_id)?
|
blurhash = services().users.blurhash(&body.user_id)?;
|
||||||
}
|
}
|
||||||
// TODO: what to do with custom
|
// TODO: what to do with custom
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
|
@ -1857,9 +1840,9 @@ pub async fn get_profile_information_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_profile_information::v1::Response {
|
Ok(get_profile_information::v1::Response {
|
||||||
blurhash,
|
|
||||||
displayname,
|
displayname,
|
||||||
avatar_url,
|
avatar_url,
|
||||||
|
blurhash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1958,7 +1941,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
add_port_to_hostname("example.com"),
|
add_port_to_hostname("example.com"),
|
||||||
FedDest::Named(String::from("example.com"), String::from(":8448"))
|
FedDest::Named(String::from("example.com"), String::from(":8448"))
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1966,6 +1949,6 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
add_port_to_hostname("example.com:1337"),
|
add_port_to_hostname("example.com:1337"),
|
||||||
FedDest::Named(String::from("example.com"), String::from(":1337"))
|
FedDest::Named(String::from("example.com"), String::from(":1337"))
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ use clap::Parser;
|
||||||
/// Returns the current version of the crate with extra info if supplied
|
/// Returns the current version of the crate with extra info if supplied
|
||||||
///
|
///
|
||||||
/// Set the environment variable `CONDUIT_VERSION_EXTRA` to any UTF-8 string to
|
/// Set the environment variable `CONDUIT_VERSION_EXTRA` to any UTF-8 string to
|
||||||
/// include it in parenthesis after the SemVer version. A common value are git
|
/// include it in parenthesis after the `SemVer` version. A common value are git
|
||||||
/// commit hashes.
|
/// commit hashes.
|
||||||
fn version() -> String {
|
fn version() -> String {
|
||||||
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
|
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
match option_env!("CONDUIT_VERSION_EXTRA") {
|
match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||||
Some(x) => format!("{} ({})", cargo_pkg_version, x),
|
Some(x) => format!("{cargo_pkg_version} ({x})"),
|
||||||
None => cargo_pkg_version.to_owned(),
|
None => cargo_pkg_version.to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ pub struct Config {
|
||||||
pub emergency_password: Option<String>,
|
pub emergency_password: Option<String>,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[allow(clippy::zero_sized_map_values)]
|
||||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ impl Config {
|
||||||
Some(server_name) => server_name.to_owned(),
|
Some(server_name) => server_name.to_owned(),
|
||||||
None => {
|
None => {
|
||||||
if self.server_name.port().is_some() {
|
if self.server_name.port().is_some() {
|
||||||
self.server_name.to_owned()
|
self.server_name.clone()
|
||||||
} else {
|
} else {
|
||||||
format!("{}:443", self.server_name.host())
|
format!("{}:443", self.server_name.host())
|
||||||
.try_into()
|
.try_into()
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl PartialProxyConfig {
|
||||||
let mut excluded_because = None; // most specific reason it was excluded
|
let mut excluded_because = None; // most specific reason it was excluded
|
||||||
if self.include.is_empty() {
|
if self.include.is_empty() {
|
||||||
// treat empty include list as `*`
|
// treat empty include list as `*`
|
||||||
included_because = Some(&WildCardedDomain::WildCard)
|
included_because = Some(&WildCardedDomain::WildCard);
|
||||||
}
|
}
|
||||||
for wc_domain in &self.include {
|
for wc_domain in &self.include {
|
||||||
if wc_domain.matches(domain) {
|
if wc_domain.matches(domain) {
|
||||||
|
@ -127,7 +127,7 @@ impl std::str::FromStr for WildCardedDomain {
|
||||||
Ok(if s.starts_with("*.") {
|
Ok(if s.starts_with("*.") {
|
||||||
WildCardedDomain::WildCarded(s[1..].to_owned())
|
WildCardedDomain::WildCarded(s[1..].to_owned())
|
||||||
} else if s == "*" {
|
} else if s == "*" {
|
||||||
WildCardedDomain::WildCarded("".to_owned())
|
WildCardedDomain::WildCarded(String::new())
|
||||||
} else {
|
} else {
|
||||||
WildCardedDomain::Exact(s.to_owned())
|
WildCardedDomain::Exact(s.to_owned())
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O
|
||||||
let mut db_opts = rocksdb::Options::default();
|
let mut db_opts = rocksdb::Options::default();
|
||||||
db_opts.set_block_based_table_factory(&block_based_options);
|
db_opts.set_block_based_table_factory(&block_based_options);
|
||||||
db_opts.create_if_missing(true);
|
db_opts.create_if_missing(true);
|
||||||
db_opts.increase_parallelism(num_cpus::get() as i32);
|
db_opts.increase_parallelism(num_cpus::get().try_into().unwrap_or(i32::MAX));
|
||||||
db_opts.set_max_open_files(max_open_files);
|
db_opts.set_max_open_files(max_open_files);
|
||||||
db_opts.set_compression_type(rocksdb::DBCompressionType::Lz4);
|
db_opts.set_compression_type(rocksdb::DBCompressionType::Lz4);
|
||||||
db_opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd);
|
db_opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd);
|
||||||
|
@ -41,7 +41,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O
|
||||||
// https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning
|
// https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning
|
||||||
db_opts.set_level_compaction_dynamic_level_bytes(true);
|
db_opts.set_level_compaction_dynamic_level_bytes(true);
|
||||||
db_opts.set_max_background_jobs(6);
|
db_opts.set_max_background_jobs(6);
|
||||||
db_opts.set_bytes_per_sync(1048576);
|
db_opts.set_bytes_per_sync(1_048_576);
|
||||||
|
|
||||||
// https://github.com/facebook/rocksdb/issues/849
|
// https://github.com/facebook/rocksdb/issues/849
|
||||||
db_opts.set_keep_log_file_num(100);
|
db_opts.set_keep_log_file_num(100);
|
||||||
|
@ -112,6 +112,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
fn memory_usage(&self) -> Result<String> {
|
fn memory_usage(&self) -> Result<String> {
|
||||||
let stats =
|
let stats =
|
||||||
rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?;
|
rocksdb::perf::get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.cache]))?;
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"Approximate memory usage of all the mem-tables: {:.3} MB\n\
|
"Approximate memory usage of all the mem-tables: {:.3} MB\n\
|
||||||
Approximate memory usage of un-flushed mem-tables: {:.3} MB\n\
|
Approximate memory usage of un-flushed mem-tables: {:.3} MB\n\
|
||||||
|
@ -219,7 +220,7 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
let lock = self.write_lock.write().unwrap();
|
let lock = self.write_lock.write().unwrap();
|
||||||
|
|
||||||
let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?;
|
let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?;
|
||||||
let new = utils::increment(old.as_deref()).unwrap();
|
let new = utils::increment(old.as_deref());
|
||||||
self.db
|
self.db
|
||||||
.rocks
|
.rocks
|
||||||
.put_cf_opt(&self.cf(), key, &new, &writeoptions)?;
|
.put_cf_opt(&self.cf(), key, &new, &writeoptions)?;
|
||||||
|
@ -236,7 +237,7 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
|
|
||||||
for key in iter {
|
for key in iter {
|
||||||
let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?;
|
let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?;
|
||||||
let new = utils::increment(old.as_deref()).unwrap();
|
let new = utils::increment(old.as_deref());
|
||||||
self.db
|
self.db
|
||||||
.rocks
|
.rocks
|
||||||
.put_cf_opt(&self.cf(), key, new, &writeoptions)?;
|
.put_cf_opt(&self.cf(), key, new, &writeoptions)?;
|
||||||
|
|
|
@ -88,6 +88,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
// 1. convert MB to KiB
|
// 1. convert MB to KiB
|
||||||
// 2. divide by permanent connections + permanent iter connections + write connection
|
// 2. divide by permanent connections + permanent iter connections + write connection
|
||||||
// 3. round down to nearest integer
|
// 3. round down to nearest integer
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
let cache_size_per_thread: u32 = ((config.db_cache_capacity_mb * 1024.0)
|
let cache_size_per_thread: u32 = ((config.db_cache_capacity_mb * 1024.0)
|
||||||
/ ((num_cpus::get().max(1) * 2) + 1) as f64)
|
/ ((num_cpus::get().max(1) * 2) + 1) as f64)
|
||||||
as u32;
|
as u32;
|
||||||
|
@ -217,8 +218,7 @@ impl KvTree for SqliteTable {
|
||||||
guard.execute("BEGIN", [])?;
|
guard.execute("BEGIN", [])?;
|
||||||
for key in iter {
|
for key in iter {
|
||||||
let old = self.get_with_guard(&guard, &key)?;
|
let old = self.get_with_guard(&guard, &key)?;
|
||||||
let new = crate::utils::increment(old.as_deref())
|
let new = crate::utils::increment(old.as_deref());
|
||||||
.expect("utils::increment always returns Some");
|
|
||||||
self.insert_with_guard(&guard, &key, &new)?;
|
self.insert_with_guard(&guard, &key, &new)?;
|
||||||
}
|
}
|
||||||
guard.execute("COMMIT", [])?;
|
guard.execute("COMMIT", [])?;
|
||||||
|
@ -308,8 +308,7 @@ impl KvTree for SqliteTable {
|
||||||
|
|
||||||
let old = self.get_with_guard(&guard, key)?;
|
let old = self.get_with_guard(&guard, key)?;
|
||||||
|
|
||||||
let new =
|
let new = crate::utils::increment(old.as_deref());
|
||||||
crate::utils::increment(old.as_deref()).expect("utils::increment always returns Some");
|
|
||||||
|
|
||||||
self.insert_with_guard(&guard, key, &new)?;
|
self.insert_with_guard(&guard, key, &new)?;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl service::account_data::Data for KeyValueDatabase {
|
||||||
data: &serde_json::Value,
|
data: &serde_json::Value,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut prefix = room_id
|
let mut prefix = room_id
|
||||||
.map(|r| r.to_string())
|
.map(ToString::to_string)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
@ -70,7 +70,7 @@ impl service::account_data::Data for KeyValueDatabase {
|
||||||
kind: RoomAccountDataEventType,
|
kind: RoomAccountDataEventType,
|
||||||
) -> Result<Option<Box<serde_json::value::RawValue>>> {
|
) -> Result<Option<Box<serde_json::value::RawValue>>> {
|
||||||
let mut key = room_id
|
let mut key = room_id
|
||||||
.map(|r| r.to_string())
|
.map(ToString::to_string)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
@ -105,7 +105,7 @@ impl service::account_data::Data for KeyValueDatabase {
|
||||||
let mut userdata = HashMap::new();
|
let mut userdata = HashMap::new();
|
||||||
|
|
||||||
let mut prefix = room_id
|
let mut prefix = room_id
|
||||||
.map(|r| r.to_string())
|
.map(ToString::to_string)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
|
@ -136,7 +136,7 @@ impl service::globals::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(&self) -> Result<()> {
|
fn cleanup(&self) -> Result<()> {
|
||||||
self._db.cleanup()
|
self.db.cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memory_usage(&self) -> String {
|
fn memory_usage(&self) -> String {
|
||||||
|
@ -160,7 +160,7 @@ our_real_users_cache: {our_real_users_cache}
|
||||||
appservice_in_room_cache: {appservice_in_room_cache}
|
appservice_in_room_cache: {appservice_in_room_cache}
|
||||||
lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
);
|
);
|
||||||
if let Ok(db_stats) = self._db.memory_usage() {
|
if let Ok(db_stats) = self.db.memory_usage() {
|
||||||
response += &db_stats;
|
response += &db_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
self.global.insert(b"keypair", &keypair)?;
|
self.global.insert(b"keypair", &keypair)?;
|
||||||
Ok::<_, Error>(keypair)
|
Ok::<_, Error>(keypair)
|
||||||
},
|
},
|
||||||
|s| Ok(s.to_vec()),
|
|s| Ok(s.clone()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff);
|
let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff);
|
||||||
|
@ -285,7 +285,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
.server_signingkeys
|
.server_signingkeys
|
||||||
.get(origin.as_bytes())?
|
.get(origin.as_bytes())?
|
||||||
.and_then(|bytes| serde_json::from_slice(&bytes).ok())
|
.and_then(|bytes| serde_json::from_slice(&bytes).ok())
|
||||||
.map(|keys: ServerSigningKeys| {
|
.map_or_else(BTreeMap::new, |keys: ServerSigningKeys| {
|
||||||
let mut tree = keys.verify_keys;
|
let mut tree = keys.verify_keys;
|
||||||
tree.extend(
|
tree.extend(
|
||||||
keys.old_verify_keys
|
keys.old_verify_keys
|
||||||
|
@ -293,8 +293,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
.map(|old| (old.0, VerifyKey::new(old.1.key))),
|
.map(|old| (old.0, VerifyKey::new(old.1.key))),
|
||||||
);
|
);
|
||||||
tree
|
tree
|
||||||
})
|
});
|
||||||
.unwrap_or_else(BTreeMap::new);
|
|
||||||
|
|
||||||
Ok(signingkeys)
|
Ok(signingkeys)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,7 @@ impl service::pusher::Data for KeyValueDatabase {
|
||||||
let mut key = sender.as_bytes().to_vec();
|
let mut key = sender.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(ids.pushkey.as_bytes());
|
key.extend_from_slice(ids.pushkey.as_bytes());
|
||||||
self.senderkey_pusher
|
self.senderkey_pusher.remove(&key).map_err(Into::into)
|
||||||
.remove(&key)
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl service::rooms::alias::Data for KeyValueDatabase {
|
||||||
|
|
||||||
fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> {
|
fn remove_alias(&self, alias: &RoomAliasId) -> Result<()> {
|
||||||
if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? {
|
if let Some(room_id) = self.alias_roomid.get(alias.alias().as_bytes())? {
|
||||||
let mut prefix = room_id.to_vec();
|
let mut prefix = room_id.clone();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
for (key, _) in self.aliasid_alias.scan_prefix(prefix) {
|
for (key, _) in self.aliasid_alias.scan_prefix(prefix) {
|
||||||
|
|
|
@ -54,12 +54,11 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
.map(move |(key, _)| key[prefix3.len()..].to_vec())
|
.map(move |(key, _)| key[prefix3.len()..].to_vec())
|
||||||
});
|
});
|
||||||
|
|
||||||
let common_elements = match utils::common_elements(iterators, |a, b| {
|
let Some(common_elements) = utils::common_elements(iterators, |a, b| {
|
||||||
// We compare b with a because we reversed the iterator earlier
|
// We compare b with a because we reversed the iterator earlier
|
||||||
b.cmp(a)
|
b.cmp(a)
|
||||||
}) {
|
}) else {
|
||||||
Some(it) => it,
|
return Ok(None);
|
||||||
None => return Ok(None),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some((Box::new(common_elements), words)))
|
Ok(Some((Box::new(common_elements), words)))
|
||||||
|
|
|
@ -10,17 +10,17 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
return Ok(*short);
|
return Ok(*short);
|
||||||
}
|
}
|
||||||
|
|
||||||
let short = match self.eventid_shorteventid.get(event_id.as_bytes())? {
|
let short =
|
||||||
Some(shorteventid) => utils::u64_from_bytes(&shorteventid)
|
if let Some(shorteventid) = self.eventid_shorteventid.get(event_id.as_bytes())? {
|
||||||
.map_err(|_| Error::bad_database("Invalid shorteventid in db."))?,
|
utils::u64_from_bytes(&shorteventid)
|
||||||
None => {
|
.map_err(|_| Error::bad_database("Invalid shorteventid in db."))?
|
||||||
|
} else {
|
||||||
let shorteventid = services().globals.next_count()?;
|
let shorteventid = services().globals.next_count()?;
|
||||||
self.eventid_shorteventid
|
self.eventid_shorteventid
|
||||||
.insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?;
|
.insert(event_id.as_bytes(), &shorteventid.to_be_bytes())?;
|
||||||
self.shorteventid_eventid
|
self.shorteventid_eventid
|
||||||
.insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?;
|
.insert(&shorteventid.to_be_bytes(), event_id.as_bytes())?;
|
||||||
shorteventid
|
shorteventid
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.eventidshort_cache
|
self.eventidshort_cache
|
||||||
|
@ -45,13 +45,13 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
return Ok(Some(*short));
|
return Ok(Some(*short));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut statekey = event_type.to_string().as_bytes().to_vec();
|
let mut state_key_bytes = event_type.to_string().as_bytes().to_vec();
|
||||||
statekey.push(0xff);
|
state_key_bytes.push(0xff);
|
||||||
statekey.extend_from_slice(state_key.as_bytes());
|
state_key_bytes.extend_from_slice(state_key.as_bytes());
|
||||||
|
|
||||||
let short = self
|
let short = self
|
||||||
.statekey_shortstatekey
|
.statekey_shortstatekey
|
||||||
.get(&statekey)?
|
.get(&state_key_bytes)?
|
||||||
.map(|shortstatekey| {
|
.map(|shortstatekey| {
|
||||||
utils::u64_from_bytes(&shortstatekey)
|
utils::u64_from_bytes(&shortstatekey)
|
||||||
.map_err(|_| Error::bad_database("Invalid shortstatekey in db."))
|
.map_err(|_| Error::bad_database("Invalid shortstatekey in db."))
|
||||||
|
@ -82,21 +82,21 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
return Ok(*short);
|
return Ok(*short);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut statekey = event_type.to_string().as_bytes().to_vec();
|
let mut state_key_bytes = event_type.to_string().as_bytes().to_vec();
|
||||||
statekey.push(0xff);
|
state_key_bytes.push(0xff);
|
||||||
statekey.extend_from_slice(state_key.as_bytes());
|
state_key_bytes.extend_from_slice(state_key.as_bytes());
|
||||||
|
|
||||||
let short = match self.statekey_shortstatekey.get(&statekey)? {
|
let short =
|
||||||
Some(shortstatekey) => utils::u64_from_bytes(&shortstatekey)
|
if let Some(shortstatekey) = self.statekey_shortstatekey.get(&state_key_bytes)? {
|
||||||
.map_err(|_| Error::bad_database("Invalid shortstatekey in db."))?,
|
utils::u64_from_bytes(&shortstatekey)
|
||||||
None => {
|
.map_err(|_| Error::bad_database("Invalid shortstatekey in db."))?
|
||||||
|
} else {
|
||||||
let shortstatekey = services().globals.next_count()?;
|
let shortstatekey = services().globals.next_count()?;
|
||||||
self.statekey_shortstatekey
|
self.statekey_shortstatekey
|
||||||
.insert(&statekey, &shortstatekey.to_be_bytes())?;
|
.insert(&state_key_bytes, &shortstatekey.to_be_bytes())?;
|
||||||
self.shortstatekey_statekey
|
self.shortstatekey_statekey
|
||||||
.insert(&shortstatekey.to_be_bytes(), &statekey)?;
|
.insert(&shortstatekey.to_be_bytes(), &state_key_bytes)?;
|
||||||
shortstatekey
|
shortstatekey
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.statekeyshort_cache
|
self.statekeyshort_cache
|
||||||
|
@ -175,21 +175,22 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns (shortstatehash, already_existed)
|
/// Returns (shortstatehash, `already_existed`)
|
||||||
fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> {
|
fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> {
|
||||||
Ok(match self.statehash_shortstatehash.get(state_hash)? {
|
Ok(
|
||||||
Some(shortstatehash) => (
|
if let Some(shortstatehash) = self.statehash_shortstatehash.get(state_hash)? {
|
||||||
|
(
|
||||||
utils::u64_from_bytes(&shortstatehash)
|
utils::u64_from_bytes(&shortstatehash)
|
||||||
.map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?,
|
.map_err(|_| Error::bad_database("Invalid shortstatehash in db."))?,
|
||||||
true,
|
true,
|
||||||
),
|
)
|
||||||
None => {
|
} else {
|
||||||
let shortstatehash = services().globals.next_count()?;
|
let shortstatehash = services().globals.next_count()?;
|
||||||
self.statehash_shortstatehash
|
self.statehash_shortstatehash
|
||||||
.insert(state_hash, &shortstatehash.to_be_bytes())?;
|
.insert(state_hash, &shortstatehash.to_be_bytes())?;
|
||||||
(shortstatehash, false)
|
(shortstatehash, false)
|
||||||
}
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_shortroomid(&self, room_id: &RoomId) -> Result<Option<u64>> {
|
fn get_shortroomid(&self, room_id: &RoomId) -> Result<Option<u64>> {
|
||||||
|
@ -203,15 +204,16 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result<u64> {
|
fn get_or_create_shortroomid(&self, room_id: &RoomId) -> Result<u64> {
|
||||||
Ok(match self.roomid_shortroomid.get(room_id.as_bytes())? {
|
Ok(
|
||||||
Some(short) => utils::u64_from_bytes(&short)
|
if let Some(short) = self.roomid_shortroomid.get(room_id.as_bytes())? {
|
||||||
.map_err(|_| Error::bad_database("Invalid shortroomid in db."))?,
|
utils::u64_from_bytes(&short)
|
||||||
None => {
|
.map_err(|_| Error::bad_database("Invalid shortroomid in db."))?
|
||||||
|
} else {
|
||||||
let short = services().globals.next_count()?;
|
let short = services().globals.next_count()?;
|
||||||
self.roomid_shortroomid
|
self.roomid_shortroomid
|
||||||
.insert(room_id.as_bytes(), &short.to_be_bytes())?;
|
.insert(room_id.as_bytes(), &short.to_be_bytes())?;
|
||||||
short
|
short
|
||||||
}
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl service::rooms::state::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
for event_id in event_ids {
|
for event_id in event_ids {
|
||||||
let mut key = prefix.to_owned();
|
let mut key = prefix.clone();
|
||||||
key.extend_from_slice(event_id.as_bytes());
|
key.extend_from_slice(event_id.as_bytes());
|
||||||
self.roomid_pduleaves.insert(&key, event_id.as_bytes())?;
|
self.roomid_pduleaves.insert(&key, event_id.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,13 +79,12 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
event_type: &StateEventType,
|
event_type: &StateEventType,
|
||||||
state_key: &str,
|
state_key: &str,
|
||||||
) -> Result<Option<Arc<EventId>>> {
|
) -> Result<Option<Arc<EventId>>> {
|
||||||
let shortstatekey = match services()
|
let Some(shortstatekey) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_shortstatekey(event_type, state_key)?
|
.get_shortstatekey(event_type, state_key)?
|
||||||
{
|
else {
|
||||||
Some(s) => s,
|
return Ok(None);
|
||||||
None => return Ok(None),
|
|
||||||
};
|
};
|
||||||
let full_state = services()
|
let full_state = services()
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -31,11 +31,10 @@ impl service::rooms::user::Data for KeyValueDatabase {
|
||||||
|
|
||||||
self.userroomid_notificationcount
|
self.userroomid_notificationcount
|
||||||
.get(&userroom_id)?
|
.get(&userroom_id)?
|
||||||
.map(|bytes| {
|
.map_or(Ok(0), |bytes| {
|
||||||
utils::u64_from_bytes(&bytes)
|
utils::u64_from_bytes(&bytes)
|
||||||
.map_err(|_| Error::bad_database("Invalid notification count in db."))
|
.map_err(|_| Error::bad_database("Invalid notification count in db."))
|
||||||
})
|
})
|
||||||
.unwrap_or(Ok(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64> {
|
fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64> {
|
||||||
|
@ -45,11 +44,10 @@ impl service::rooms::user::Data for KeyValueDatabase {
|
||||||
|
|
||||||
self.userroomid_highlightcount
|
self.userroomid_highlightcount
|
||||||
.get(&userroom_id)?
|
.get(&userroom_id)?
|
||||||
.map(|bytes| {
|
.map_or(Ok(0), |bytes| {
|
||||||
utils::u64_from_bytes(&bytes)
|
utils::u64_from_bytes(&bytes)
|
||||||
.map_err(|_| Error::bad_database("Invalid highlight count in db."))
|
.map_err(|_| Error::bad_database("Invalid highlight count in db."))
|
||||||
})
|
})
|
||||||
.unwrap_or(Ok(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64> {
|
fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64> {
|
||||||
|
|
|
@ -67,9 +67,9 @@ impl service::sending::Data for KeyValueDatabase {
|
||||||
for (outgoing_kind, event) in requests {
|
for (outgoing_kind, event) in requests {
|
||||||
let mut key = outgoing_kind.get_prefix();
|
let mut key = outgoing_kind.get_prefix();
|
||||||
if let SendingEventType::Pdu(value) = &event {
|
if let SendingEventType::Pdu(value) = &event {
|
||||||
key.extend_from_slice(value)
|
key.extend_from_slice(value);
|
||||||
} else {
|
} else {
|
||||||
key.extend_from_slice(&services().globals.next_count()?.to_be_bytes())
|
key.extend_from_slice(&services().globals.next_count()?.to_be_bytes());
|
||||||
}
|
}
|
||||||
let value = if let SendingEventType::Edu(value) = &event {
|
let value = if let SendingEventType::Edu(value) = &event {
|
||||||
&**value
|
&**value
|
||||||
|
|
|
@ -12,7 +12,7 @@ impl service::transaction_ids::Data for KeyValueDatabase {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default());
|
key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(txn_id.as_bytes());
|
key.extend_from_slice(txn_id.as_bytes());
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ impl service::transaction_ids::Data for KeyValueDatabase {
|
||||||
) -> Result<Option<Vec<u8>>> {
|
) -> Result<Option<Vec<u8>>> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(device_id.map(|d| d.as_bytes()).unwrap_or_default());
|
key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(txn_id.as_bytes());
|
key.extend_from_slice(txn_id.as_bytes());
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ impl service::uiaa::Data for KeyValueDatabase {
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned()))
|
.get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned()))
|
||||||
.map(|j| j.to_owned())
|
.map(ToOwned::to_owned)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_uiaa_session(
|
fn update_uiaa_session(
|
||||||
|
|
|
@ -141,7 +141,7 @@ impl service::users::Data for KeyValueDatabase {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the avatar_url of a user.
|
/// Get the `avatar_url` of a user.
|
||||||
fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>> {
|
fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>> {
|
||||||
self.userid_avatarurl
|
self.userid_avatarurl
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.as_bytes())?
|
||||||
|
@ -153,7 +153,7 @@ impl service::users::Data for KeyValueDatabase {
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()> {
|
fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()> {
|
||||||
if let Some(avatar_url) = avatar_url {
|
if let Some(avatar_url) = avatar_url {
|
||||||
self.userid_avatarurl
|
self.userid_avatarurl
|
||||||
|
@ -178,7 +178,7 @@ impl service::users::Data for KeyValueDatabase {
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
|
fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
|
||||||
if let Some(blurhash) = blurhash {
|
if let Some(blurhash) = blurhash {
|
||||||
self.userid_blurhash
|
self.userid_blurhash
|
||||||
|
@ -343,12 +343,11 @@ impl service::users::Data for KeyValueDatabase {
|
||||||
fn last_one_time_keys_update(&self, user_id: &UserId) -> Result<u64> {
|
fn last_one_time_keys_update(&self, user_id: &UserId) -> Result<u64> {
|
||||||
self.userid_lastonetimekeyupdate
|
self.userid_lastonetimekeyupdate
|
||||||
.get(user_id.as_bytes())?
|
.get(user_id.as_bytes())?
|
||||||
.map(|bytes| {
|
.map_or(Ok(0), |bytes| {
|
||||||
utils::u64_from_bytes(&bytes).map_err(|_| {
|
utils::u64_from_bytes(&bytes).map_err(|_| {
|
||||||
Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.")
|
Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(Ok(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_one_time_key(
|
fn take_one_time_key(
|
||||||
|
@ -949,7 +948,7 @@ impl KeyValueDatabase {}
|
||||||
|
|
||||||
/// Will only return with Some(username) if the password was not empty and the
|
/// Will only return with Some(username) if the password was not empty and the
|
||||||
/// username could be successfully parsed.
|
/// username could be successfully parsed.
|
||||||
/// If utils::string_from_bytes(...) returns an error that username will be skipped
|
/// If `utils::string_from_bytes`(...) returns an error that username will be skipped
|
||||||
/// and the error will be logged.
|
/// and the error will be logged.
|
||||||
fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option<String> {
|
fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option<String> {
|
||||||
// A valid password is not empty
|
// A valid password is not empty
|
||||||
|
|
|
@ -34,7 +34,7 @@ use tokio::time::interval;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
pub struct KeyValueDatabase {
|
pub struct KeyValueDatabase {
|
||||||
_db: Arc<dyn KeyValueDatabaseEngine>,
|
db: Arc<dyn KeyValueDatabaseEngine>,
|
||||||
|
|
||||||
//pub globals: globals::Globals,
|
//pub globals: globals::Globals,
|
||||||
pub(super) global: Arc<dyn KvTree>,
|
pub(super) global: Arc<dyn KvTree>,
|
||||||
|
@ -113,7 +113,7 @@ pub struct KeyValueDatabase {
|
||||||
pub(super) roomsynctoken_shortstatehash: Arc<dyn KvTree>,
|
pub(super) roomsynctoken_shortstatehash: Arc<dyn KvTree>,
|
||||||
/// Remember the state hash at events in the past.
|
/// Remember the state hash at events in the past.
|
||||||
pub(super) shorteventid_shortstatehash: Arc<dyn KvTree>,
|
pub(super) shorteventid_shortstatehash: Arc<dyn KvTree>,
|
||||||
/// StateKey = EventType + StateKey, ShortStateKey = Count
|
/// `StateKey` = `EventType` + `StateKey`, `ShortStateKey` = Count
|
||||||
pub(super) statekey_shortstatekey: Arc<dyn KvTree>,
|
pub(super) statekey_shortstatekey: Arc<dyn KvTree>,
|
||||||
pub(super) shortstatekey_statekey: Arc<dyn KvTree>,
|
pub(super) shortstatekey_statekey: Arc<dyn KvTree>,
|
||||||
|
|
||||||
|
@ -127,14 +127,14 @@ pub struct KeyValueDatabase {
|
||||||
|
|
||||||
pub(super) shorteventid_authchain: Arc<dyn KvTree>,
|
pub(super) shorteventid_authchain: Arc<dyn KvTree>,
|
||||||
|
|
||||||
/// RoomId + EventId -> outlier PDU.
|
/// `RoomId` + `EventId` -> outlier PDU.
|
||||||
/// Any pdu that has passed the steps 1-8 in the incoming event /federation/send/txn.
|
/// Any pdu that has passed the steps 1-8 in the incoming event /federation/send/txn.
|
||||||
pub(super) eventid_outlierpdu: Arc<dyn KvTree>,
|
pub(super) eventid_outlierpdu: Arc<dyn KvTree>,
|
||||||
pub(super) softfailedeventids: Arc<dyn KvTree>,
|
pub(super) softfailedeventids: Arc<dyn KvTree>,
|
||||||
|
|
||||||
/// ShortEventId + ShortEventId -> ().
|
/// `ShortEventId` + `ShortEventId` -> ().
|
||||||
pub(super) tofrom_relation: Arc<dyn KvTree>,
|
pub(super) tofrom_relation: Arc<dyn KvTree>,
|
||||||
/// RoomId + EventId -> Parent PDU EventId.
|
/// `RoomId` + `EventId` -> Parent PDU `EventId`.
|
||||||
pub(super) referencedevents: Arc<dyn KvTree>,
|
pub(super) referencedevents: Arc<dyn KvTree>,
|
||||||
|
|
||||||
//pub account_data: account_data::AccountData,
|
//pub account_data: account_data::AccountData,
|
||||||
|
@ -241,6 +241,8 @@ impl KeyValueDatabase {
|
||||||
.map_err(|_| Error::BadConfig("Database folder doesn't exists and couldn't be created (e.g. due to missing permissions). Please create the database folder yourself."))?;
|
.map_err(|_| Error::BadConfig("Database folder doesn't exists and couldn't be created (e.g. due to missing permissions). Please create the database folder yourself."))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Databases which are disabled will trigger this
|
||||||
|
#[allow(clippy::match_same_arms)]
|
||||||
let builder: Arc<dyn KeyValueDatabaseEngine> = match &*config.database_backend {
|
let builder: Arc<dyn KeyValueDatabaseEngine> = match &*config.database_backend {
|
||||||
"sqlite" => {
|
"sqlite" => {
|
||||||
#[cfg(not(feature = "sqlite"))]
|
#[cfg(not(feature = "sqlite"))]
|
||||||
|
@ -274,7 +276,7 @@ impl KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
let db_raw = Box::new(Self {
|
let db_raw = Box::new(Self {
|
||||||
_db: builder.clone(),
|
db: builder.clone(),
|
||||||
userid_password: builder.open_tree("userid_password")?,
|
userid_password: builder.open_tree("userid_password")?,
|
||||||
userid_displayname: builder.open_tree("userid_displayname")?,
|
userid_displayname: builder.open_tree("userid_displayname")?,
|
||||||
userid_avatarurl: builder.open_tree("userid_avatarurl")?,
|
userid_avatarurl: builder.open_tree("userid_avatarurl")?,
|
||||||
|
@ -428,12 +430,9 @@ impl KeyValueDatabase {
|
||||||
for (roomserverid, _) in db.roomserverids.iter() {
|
for (roomserverid, _) in db.roomserverids.iter() {
|
||||||
let mut parts = roomserverid.split(|&b| b == 0xff);
|
let mut parts = roomserverid.split(|&b| b == 0xff);
|
||||||
let room_id = parts.next().expect("split always returns one element");
|
let room_id = parts.next().expect("split always returns one element");
|
||||||
let servername = match parts.next() {
|
let Some(servername) = parts.next() else {
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
error!("Migration: Invalid roomserverid in db.");
|
error!("Migration: Invalid roomserverid in db.");
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let mut serverroomid = servername.to_vec();
|
let mut serverroomid = servername.to_vec();
|
||||||
serverroomid.push(0xff);
|
serverroomid.push(0xff);
|
||||||
|
@ -620,7 +619,7 @@ impl KeyValueDatabase {
|
||||||
Ok::<_, Error>(())
|
Ok::<_, Error>(())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (k, seventid) in db._db.open_tree("stateid_shorteventid")?.iter() {
|
for (k, seventid) in db.db.open_tree("stateid_shorteventid")?.iter() {
|
||||||
let sstatehash = utils::u64_from_bytes(&k[0..size_of::<u64>()])
|
let sstatehash = utils::u64_from_bytes(&k[0..size_of::<u64>()])
|
||||||
.expect("number of bytes is correct");
|
.expect("number of bytes is correct");
|
||||||
let sstatekey = k[size_of::<u64>()..].to_vec();
|
let sstatekey = k[size_of::<u64>()..].to_vec();
|
||||||
|
@ -793,7 +792,7 @@ impl KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force E2EE device list updates so we can send them over federation
|
// Force E2EE device list updates so we can send them over federation
|
||||||
for user_id in services().users.iter().filter_map(|r| r.ok()) {
|
for user_id in services().users.iter().filter_map(std::result::Result::ok) {
|
||||||
services().users.mark_device_key_update(&user_id)?;
|
services().users.mark_device_key_update(&user_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,7 +802,7 @@ impl KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if services().globals.database_version()? < 11 {
|
if services().globals.database_version()? < 11 {
|
||||||
db._db
|
db.db
|
||||||
.open_tree("userdevicesessionid_uiaarequest")?
|
.open_tree("userdevicesessionid_uiaarequest")?
|
||||||
.clear()?;
|
.clear()?;
|
||||||
services().globals.bump_database_version(11)?;
|
services().globals.bump_database_version(11)?;
|
||||||
|
@ -979,7 +978,7 @@ impl KeyValueDatabase {
|
||||||
error!(
|
error!(
|
||||||
"Could not set the configured emergency password for the conduit user: {}",
|
"Could not set the configured emergency password for the conduit user: {}",
|
||||||
e
|
e
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -997,7 +996,7 @@ impl KeyValueDatabase {
|
||||||
pub fn flush(&self) -> Result<()> {
|
pub fn flush(&self) -> Result<()> {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
let res = self._db.flush();
|
let res = self.db.flush();
|
||||||
|
|
||||||
debug!("flush: took {:?}", start.elapsed());
|
debug!("flush: took {:?}", start.elapsed());
|
||||||
|
|
||||||
|
@ -1017,17 +1016,10 @@ impl KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_handle_updates() -> Result<()> {
|
async fn try_handle_updates() -> Result<()> {
|
||||||
let response = services()
|
|
||||||
.globals
|
|
||||||
.default_client()
|
|
||||||
.get("https://conduit.rs/check-for-updates/stable")
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct CheckForUpdatesResponseEntry {
|
struct CheckForUpdatesResponseEntry {
|
||||||
id: u64,
|
|
||||||
date: String,
|
date: String,
|
||||||
|
id: u64,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -1035,6 +1027,13 @@ impl KeyValueDatabase {
|
||||||
updates: Vec<CheckForUpdatesResponseEntry>,
|
updates: Vec<CheckForUpdatesResponseEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let response = services()
|
||||||
|
.globals
|
||||||
|
.default_client()
|
||||||
|
.get("https://conduit.rs/check-for-updates/stable")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
let response = serde_json::from_str::<CheckForUpdatesResponse>(&response.text().await?)
|
let response = serde_json::from_str::<CheckForUpdatesResponse>(&response.text().await?)
|
||||||
.map_err(|_| Error::BadServerResponse("Bad version check response"))?;
|
.map_err(|_| Error::BadServerResponse("Bad version check response"))?;
|
||||||
|
|
||||||
|
@ -1048,7 +1047,7 @@ impl KeyValueDatabase {
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}",
|
"@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}",
|
||||||
update.date, update.message
|
update.date, update.message
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
services()
|
services()
|
||||||
|
@ -1066,7 +1065,7 @@ impl KeyValueDatabase {
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
let timer_interval =
|
let timer_interval =
|
||||||
Duration::from_secs(services().globals.config.cleanup_second_interval as u64);
|
Duration::from_secs(u64::from(services().globals.config.cleanup_second_interval));
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut i = interval(timer_interval);
|
let mut i = interval(timer_interval);
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
|
// All API endpoints must be async
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
// We expect request users and servers (probably shouldn't tho)
|
||||||
|
#[allow(clippy::missing_panics_doc)]
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod clap;
|
pub mod clap;
|
||||||
mod config;
|
mod config;
|
||||||
|
// Results in large capacity if set to a negative number, user's fault really :P
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
mod database;
|
mod database;
|
||||||
|
// `self` is required for easy access to methods
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
mod service;
|
mod service;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ async fn run_server() -> io::Result<()> {
|
||||||
#[cfg(feature = "systemd")]
|
#[cfg(feature = "systemd")]
|
||||||
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
|
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
|
||||||
|
|
||||||
server.await?
|
server.await?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let server = bind(addr).handle(handle).serve(app);
|
let server = bind(addr).handle(handle).serve(app);
|
||||||
|
@ -209,7 +209,7 @@ async fn run_server() -> io::Result<()> {
|
||||||
#[cfg(feature = "systemd")]
|
#[cfg(feature = "systemd")]
|
||||||
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
|
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
|
||||||
|
|
||||||
server.await?
|
server.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,8 +461,8 @@ async fn shutdown_signal(handle: ServerHandle) {
|
||||||
let sig: &str;
|
let sig: &str;
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = ctrl_c => { sig = "Ctrl+C"; },
|
() = ctrl_c => { sig = "Ctrl+C"; },
|
||||||
_ = terminate => { sig = "SIGTERM"; },
|
() = terminate => { sig = "SIGTERM"; },
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("Received {}, shutting down...", sig);
|
warn!("Received {}, shutting down...", sig);
|
||||||
|
|
|
@ -110,7 +110,7 @@ enum AdminCommand {
|
||||||
force: bool,
|
force: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Get the auth_chain of a PDU
|
/// Get the `auth_chain` of a PDU
|
||||||
GetAuthChain {
|
GetAuthChain {
|
||||||
/// An event ID (the $ character followed by the base64 reference hash)
|
/// An event ID (the $ character followed by the base64 reference hash)
|
||||||
event_id: Box<EventId>,
|
event_id: Box<EventId>,
|
||||||
|
@ -229,7 +229,7 @@ impl Service {
|
||||||
.roomid_mutex_state
|
.roomid_mutex_state
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.entry(conduit_room.to_owned())
|
.entry(conduit_room.clone())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -711,11 +711,11 @@ impl Service {
|
||||||
match <&UserId>::try_from(user) {
|
match <&UserId>::try_from(user) {
|
||||||
Ok(user_id) => {
|
Ok(user_id) => {
|
||||||
if user_id.server_name() != services().globals.server_name() {
|
if user_id.server_name() != services().globals.server_name() {
|
||||||
remote_ids.push(user_id)
|
remote_ids.push(user_id);
|
||||||
} else if !services().users.exists(user_id)? {
|
} else if !services().users.exists(user_id)? {
|
||||||
non_existant_ids.push(user_id)
|
non_existant_ids.push(user_id);
|
||||||
} else {
|
} else {
|
||||||
user_ids.push(user_id)
|
user_ids.push(user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -770,20 +770,21 @@ impl Service {
|
||||||
|
|
||||||
if !force {
|
if !force {
|
||||||
user_ids.retain(|&user_id| match services().users.is_admin(user_id) {
|
user_ids.retain(|&user_id| match services().users.is_admin(user_id) {
|
||||||
Ok(is_admin) => match is_admin {
|
Ok(is_admin) => {
|
||||||
true => {
|
if is_admin {
|
||||||
admins.push(user_id.localpart());
|
admins.push(user_id.localpart());
|
||||||
false
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
false => true,
|
|
||||||
},
|
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for &user_id in &user_ids {
|
for &user_id in &user_ids {
|
||||||
if services().users.deactivate_account(user_id).is_ok() {
|
if services().users.deactivate_account(user_id).is_ok() {
|
||||||
deactivation_count += 1
|
deactivation_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,7 +847,7 @@ impl Service {
|
||||||
|
|
||||||
let pub_key_map = pub_key_map.read().await;
|
let pub_key_map = pub_key_map.read().await;
|
||||||
match ruma::signatures::verify_json(&pub_key_map, &value) {
|
match ruma::signatures::verify_json(&pub_key_map, &value) {
|
||||||
Ok(_) => RoomMessageEventContent::text_plain("Signature correct"),
|
Ok(()) => RoomMessageEventContent::text_plain("Signature correct"),
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
Err(e) => RoomMessageEventContent::text_plain(format!(
|
||||||
"Signature verification failed: {e}"
|
"Signature verification failed: {e}"
|
||||||
)),
|
)),
|
||||||
|
@ -909,8 +910,7 @@ impl Service {
|
||||||
|
|
||||||
while text_lines
|
while text_lines
|
||||||
.get(line_index)
|
.get(line_index)
|
||||||
.map(|line| line.starts_with('#'))
|
.is_some_and(|line| line.starts_with('#'))
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
command_body += if text_lines[line_index].starts_with("# ") {
|
command_body += if text_lines[line_index].starts_with("# ") {
|
||||||
&text_lines[line_index][2..]
|
&text_lines[line_index][2..]
|
||||||
|
@ -1001,7 +1001,7 @@ impl Service {
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: TimelineEventType::RoomCreate,
|
||||||
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1054,7 +1054,7 @@ impl Service {
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1073,7 +1073,7 @@ impl Service {
|
||||||
content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite))
|
content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1094,7 +1094,7 @@ impl Service {
|
||||||
))
|
))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1115,7 +1115,7 @@ impl Service {
|
||||||
))
|
))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1135,7 +1135,7 @@ impl Service {
|
||||||
content: to_raw_value(&RoomNameEventContent::new(room_name))
|
content: to_raw_value(&RoomNameEventContent::new(room_name))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1155,7 +1155,7 @@ impl Service {
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1181,7 +1181,7 @@ impl Service {
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
@ -1291,7 +1291,7 @@ impl Service {
|
||||||
|
|
||||||
// Set power level
|
// Set power level
|
||||||
let mut users = BTreeMap::new();
|
let mut users = BTreeMap::new();
|
||||||
users.insert(conduit_user.to_owned(), 100.into());
|
users.insert(conduit_user.clone(), 100.into());
|
||||||
users.insert(user_id.to_owned(), 100.into());
|
users.insert(user_id.to_owned(), 100.into());
|
||||||
|
|
||||||
services()
|
services()
|
||||||
|
@ -1306,7 +1306,7 @@ impl Service {
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&conduit_user,
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub trait Data: Send + Sync {
|
||||||
content_type: Option<&str>,
|
content_type: Option<&str>,
|
||||||
) -> Result<Vec<u8>>;
|
) -> Result<Vec<u8>>;
|
||||||
|
|
||||||
/// Returns content_disposition, content_type and the metadata key.
|
/// Returns `content_disposition`, `content_type` and the metadata key.
|
||||||
fn search_file_metadata(
|
fn search_file_metadata(
|
||||||
&self,
|
&self,
|
||||||
mxc: String,
|
mxc: String,
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl Service {
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
content_disposition,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
file: file.clone(),
|
||||||
}))
|
}))
|
||||||
} else if let Ok((content_disposition, content_type, key)) =
|
} else if let Ok((content_disposition, content_type, key)) =
|
||||||
self.db.search_file_metadata(mxc.clone(), 0, 0)
|
self.db.search_file_metadata(mxc.clone(), 0, 0)
|
||||||
|
@ -145,7 +145,7 @@ impl Service {
|
||||||
return Ok(Some(FileMeta {
|
return Ok(Some(FileMeta {
|
||||||
content_disposition,
|
content_disposition,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
file: file.clone(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,14 +211,14 @@ impl Service {
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
content_disposition,
|
||||||
content_type,
|
content_type,
|
||||||
file: thumbnail_bytes.to_vec(),
|
file: thumbnail_bytes.clone(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
// Couldn't parse file to generate thumbnail, send original
|
// Couldn't parse file to generate thumbnail, send original
|
||||||
Ok(Some(FileMeta {
|
Ok(Some(FileMeta {
|
||||||
content_disposition,
|
content_disposition,
|
||||||
content_type,
|
content_type,
|
||||||
file: file.to_vec(),
|
file: file.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,6 +39,8 @@ pub struct Services {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Services {
|
impl Services {
|
||||||
|
// Results in large capacity if set to a negative number, user's fault really :P
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn build<
|
pub fn build<
|
||||||
D: appservice::Data
|
D: appservice::Data
|
||||||
+ pusher::Data
|
+ pusher::Data
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl Service {
|
||||||
warn!("Failed to find destination {}: {}", destination, e);
|
warn!("Failed to find destination {}: {}", destination, e);
|
||||||
Error::BadServerResponse("Invalid destination")
|
Error::BadServerResponse("Invalid destination")
|
||||||
})?
|
})?
|
||||||
.map(|body| body.freeze());
|
.map(BytesMut::freeze);
|
||||||
|
|
||||||
let reqwest_request = reqwest::Request::try_from(http_request)?;
|
let reqwest_request = reqwest::Request::try_from(http_request)?;
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ impl Service {
|
||||||
.iter()
|
.iter()
|
||||||
.any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_)))
|
.any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_)))
|
||||||
{
|
{
|
||||||
notifi.prio = NotificationPriority::High
|
notifi.prio = NotificationPriority::High;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event_id_only {
|
if event_id_only {
|
||||||
|
@ -279,7 +279,6 @@ impl Service {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// TODO: Handle email
|
// TODO: Handle email
|
||||||
PusherKind::Email(_) => Ok(()),
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruma::{events::presence::PresenceEvent, OwnedUserId, RoomId, UserId};
|
||||||
pub trait Data: Send + Sync {
|
pub trait Data: Send + Sync {
|
||||||
/// Adds a presence event which will be saved until a new event replaces it.
|
/// Adds a presence event which will be saved until a new event replaces it.
|
||||||
///
|
///
|
||||||
/// Note: This method takes a RoomId because presence updates are always bound to rooms to
|
/// Note: This method takes a `RoomId` because presence updates are always bound to rooms to
|
||||||
/// make sure users outside these rooms can't see them.
|
/// make sure users outside these rooms can't see them.
|
||||||
fn update_presence(
|
fn update_presence(
|
||||||
&self,
|
&self,
|
||||||
|
@ -21,7 +21,7 @@ pub trait Data: Send + Sync {
|
||||||
/// Returns the timestamp of the last presence update of this user in millis since the unix epoch.
|
/// Returns the timestamp of the last presence update of this user in millis since the unix epoch.
|
||||||
fn last_presence_update(&self, user_id: &UserId) -> Result<Option<u64>>;
|
fn last_presence_update(&self, user_id: &UserId) -> Result<Option<u64>>;
|
||||||
|
|
||||||
/// Returns the presence event with correct last_active_ago.
|
/// Returns the presence event with correct `last_active_ago`.
|
||||||
fn get_presence_event(
|
fn get_presence_event(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
|
|
|
@ -10,10 +10,13 @@ pub struct Service {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove when presence is implemented
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
impl Service {
|
impl Service {
|
||||||
/// Adds a presence event which will be saved until a new event replaces it.
|
/// Adds a presence event which will be saved until a new event replaces it.
|
||||||
///
|
///
|
||||||
/// Note: This method takes a RoomId because presence updates are always bound to rooms to
|
/// Note: This method takes a `RoomId` because presence updates are always bound to rooms to
|
||||||
/// make sure users outside these rooms can't see them.
|
/// make sure users outside these rooms can't see them.
|
||||||
pub fn update_presence(
|
pub fn update_presence(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub trait Data: Send + Sync {
|
||||||
event: ReceiptEvent,
|
event: ReceiptEvent,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
|
/// Returns an iterator over the most recent `read_receipts` in a room that happened after the event with id `since`.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn readreceipts_since<'a>(
|
fn readreceipts_since<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub struct Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
/// Sets a user as typing until the timeout timestamp is reached or roomtyping_remove is
|
/// Sets a user as typing until the timeout timestamp is reached or `roomtyping_remove` is
|
||||||
/// called.
|
/// called.
|
||||||
pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> {
|
pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> {
|
||||||
self.typing
|
self.typing
|
||||||
|
|
|
@ -39,7 +39,7 @@ use serde_json::value::RawValue as RawJsonValue;
|
||||||
use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore};
|
use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use crate::{service::*, services, Error, PduEvent, Result};
|
use crate::{service::pdu, services, Error, PduEvent, Result};
|
||||||
|
|
||||||
use super::state_compressor::CompressedStateEvent;
|
use super::state_compressor::CompressedStateEvent;
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ impl Service {
|
||||||
|
|
||||||
// 1. Skip the PDU if we already have it as a timeline event
|
// 1. Skip the PDU if we already have it as a timeline event
|
||||||
if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? {
|
if let Some(pdu_id) = services().rooms.timeline.get_pdu_id(event_id)? {
|
||||||
return Ok(Some(pdu_id.to_vec()));
|
return Ok(Some(pdu_id.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_event = services()
|
let create_event = services()
|
||||||
|
@ -199,7 +199,7 @@ impl Service {
|
||||||
e.insert((Instant::now(), 1));
|
e.insert((Instant::now(), 1));
|
||||||
}
|
}
|
||||||
hash_map::Entry::Occupied(mut e) => {
|
hash_map::Entry::Occupied(mut e) => {
|
||||||
*e.get_mut() = (Instant::now(), e.get().1 + 1)
|
*e.get_mut() = (Instant::now(), e.get().1 + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -243,7 +243,7 @@ impl Service {
|
||||||
e.insert((Instant::now(), 1));
|
e.insert((Instant::now(), 1));
|
||||||
}
|
}
|
||||||
hash_map::Entry::Occupied(mut e) => {
|
hash_map::Entry::Occupied(mut e) => {
|
||||||
*e.get_mut() = (Instant::now(), e.get().1 + 1)
|
*e.get_mut() = (Instant::now(), e.get().1 + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,14 +342,11 @@ impl Service {
|
||||||
Ok(ruma::signatures::Verified::Signatures) => {
|
Ok(ruma::signatures::Verified::Signatures) => {
|
||||||
// Redact
|
// Redact
|
||||||
warn!("Calculated hash does not match: {}", event_id);
|
warn!("Calculated hash does not match: {}", event_id);
|
||||||
let obj = match ruma::canonical_json::redact(value, room_version_id, None) {
|
let Ok(obj) = ruma::canonical_json::redact(value, room_version_id, None) else {
|
||||||
Ok(obj) => obj,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Redaction failed",
|
"Redaction failed",
|
||||||
))
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip the PDU if it is redacted and we already have it as an outlier event
|
// Skip the PDU if it is redacted and we already have it as an outlier event
|
||||||
|
@ -409,12 +406,9 @@ impl Service {
|
||||||
// Build map of auth events
|
// Build map of auth events
|
||||||
let mut auth_events = HashMap::new();
|
let mut auth_events = HashMap::new();
|
||||||
for id in &incoming_pdu.auth_events {
|
for id in &incoming_pdu.auth_events {
|
||||||
let auth_event = match services().rooms.timeline.get_pdu(id)? {
|
let Some(auth_event) = services().rooms.timeline.get_pdu(id)? else {
|
||||||
Some(e) => e,
|
|
||||||
None => {
|
|
||||||
warn!("Could not find auth event {}", id);
|
warn!("Could not find auth event {}", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.check_room_id(room_id, &auth_event)?;
|
self.check_room_id(room_id, &auth_event)?;
|
||||||
|
@ -441,8 +435,8 @@ impl Service {
|
||||||
// The original create event must be in the auth events
|
// The original create event must be in the auth events
|
||||||
if !matches!(
|
if !matches!(
|
||||||
auth_events
|
auth_events
|
||||||
.get(&(StateEventType::RoomCreate, "".to_owned()))
|
.get(&(StateEventType::RoomCreate, String::new()))
|
||||||
.map(|a| a.as_ref()),
|
.map(AsRef::as_ref),
|
||||||
Some(_) | None
|
Some(_) | None
|
||||||
) {
|
) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -574,21 +568,16 @@ impl Service {
|
||||||
|
|
||||||
let mut okay = true;
|
let mut okay = true;
|
||||||
for prev_eventid in &incoming_pdu.prev_events {
|
for prev_eventid in &incoming_pdu.prev_events {
|
||||||
let prev_event =
|
let Ok(Some(prev_event)) = services().rooms.timeline.get_pdu(prev_eventid) else {
|
||||||
if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(prev_eventid) {
|
|
||||||
pdu
|
|
||||||
} else {
|
|
||||||
okay = false;
|
okay = false;
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
let sstatehash = if let Ok(Some(s)) = services()
|
let Ok(Some(sstatehash)) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.pdu_shortstatehash(prev_eventid)
|
.pdu_shortstatehash(prev_eventid)
|
||||||
{
|
else {
|
||||||
s
|
|
||||||
} else {
|
|
||||||
okay = false;
|
okay = false;
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
@ -738,7 +727,7 @@ impl Service {
|
||||||
.get_shortstatekey(&StateEventType::RoomCreate, "")?
|
.get_shortstatekey(&StateEventType::RoomCreate, "")?
|
||||||
.expect("Room exists");
|
.expect("Room exists");
|
||||||
|
|
||||||
if state.get(&create_shortstatekey).map(|id| id.as_ref())
|
if state.get(&create_shortstatekey).map(AsRef::as_ref)
|
||||||
!= Some(&create_event.event_id)
|
!= Some(&create_event.event_id)
|
||||||
{
|
{
|
||||||
return Err(Error::bad_database(
|
return Err(Error::bad_database(
|
||||||
|
@ -1046,16 +1035,12 @@ impl Service {
|
||||||
};
|
};
|
||||||
|
|
||||||
let lock = services().globals.stateres_mutex.lock();
|
let lock = services().globals.stateres_mutex.lock();
|
||||||
let state = match state_res::resolve(
|
let Ok(state) =
|
||||||
room_version_id,
|
state_res::resolve(room_version_id, &fork_states, auth_chain_sets, fetch_event)
|
||||||
&fork_states,
|
else {
|
||||||
auth_chain_sets,
|
return Err(Error::bad_database(
|
||||||
fetch_event,
|
"State resolution failed, either an event could not be found or deserialization",
|
||||||
) {
|
));
|
||||||
Ok(new_state) => new_state,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization"));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
drop(lock);
|
drop(lock);
|
||||||
|
@ -1113,7 +1098,7 @@ impl Service {
|
||||||
e.insert((Instant::now(), 1));
|
e.insert((Instant::now(), 1));
|
||||||
}
|
}
|
||||||
hash_map::Entry::Occupied(mut e) => {
|
hash_map::Entry::Occupied(mut e) => {
|
||||||
*e.get_mut() = (Instant::now(), e.get().1 + 1)
|
*e.get_mut() = (Instant::now(), e.get().1 + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1172,7 +1157,7 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Fetching {} over federation.", next_id);
|
info!("Fetching {} over federation.", next_id);
|
||||||
match services()
|
if let Ok(res) = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
origin,
|
origin,
|
||||||
|
@ -1182,15 +1167,12 @@ impl Service {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(res) => {
|
|
||||||
info!("Got {} over federation", next_id);
|
info!("Got {} over federation", next_id);
|
||||||
let (calculated_event_id, value) =
|
let Ok((calculated_event_id, value)) =
|
||||||
match pdu::gen_event_id_canonical_json(&res.pdu, room_version_id) {
|
pdu::gen_event_id_canonical_json(&res.pdu, room_version_id)
|
||||||
Ok(t) => t,
|
else {
|
||||||
Err(_) => {
|
|
||||||
back_off((*next_id).to_owned()).await;
|
back_off((*next_id).to_owned()).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if calculated_event_id != *next_id {
|
if calculated_event_id != *next_id {
|
||||||
|
@ -1217,13 +1199,11 @@ impl Service {
|
||||||
|
|
||||||
events_in_reverse_order.push((next_id.clone(), value));
|
events_in_reverse_order.push((next_id.clone(), value));
|
||||||
events_all.insert(next_id);
|
events_all.insert(next_id);
|
||||||
}
|
} else {
|
||||||
Err(_) => {
|
|
||||||
warn!("Failed to fetch event: {}", next_id);
|
warn!("Failed to fetch event: {}", next_id);
|
||||||
back_off((*next_id).to_owned()).await;
|
back_off((*next_id).to_owned()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (next_id, value) in events_in_reverse_order.iter().rev() {
|
for (next_id, value) in events_in_reverse_order.iter().rev() {
|
||||||
if let Some((time, tries)) = services()
|
if let Some((time, tries)) = services()
|
||||||
|
@ -1410,12 +1390,9 @@ impl Service {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let keys = match fetch_res {
|
let Ok(keys) = fetch_res else {
|
||||||
Ok(keys) => keys,
|
|
||||||
Err(_) => {
|
|
||||||
warn!("Signature verification failed: Could not fetch signing key.",);
|
warn!("Signature verification failed: Could not fetch signing key.",);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub_key_map
|
pub_key_map
|
||||||
|
@ -1637,22 +1614,21 @@ impl Service {
|
||||||
|
|
||||||
/// Returns Ok if the acl allows the server
|
/// Returns Ok if the acl allows the server
|
||||||
pub fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> {
|
pub fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> {
|
||||||
let acl_event = match services().rooms.state_accessor.room_state_get(
|
let Some(acl_event) = services().rooms.state_accessor.room_state_get(
|
||||||
room_id,
|
room_id,
|
||||||
&StateEventType::RoomServerAcl,
|
&StateEventType::RoomServerAcl,
|
||||||
"",
|
"",
|
||||||
)? {
|
)?
|
||||||
Some(acl) => acl,
|
else {
|
||||||
None => return Ok(()),
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let acl_event_content: RoomServerAclEventContent =
|
let acl_event_content: RoomServerAclEventContent =
|
||||||
match serde_json::from_str(acl_event.content.get()) {
|
if let Ok(content) = serde_json::from_str(acl_event.content.get()) {
|
||||||
Ok(content) => content,
|
content
|
||||||
Err(_) => {
|
} else {
|
||||||
warn!("Invalid ACL event");
|
warn!("Invalid ACL event");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if acl_event_content.allow.is_empty() {
|
if acl_event_content.allow.is_empty() {
|
||||||
|
@ -1693,9 +1669,9 @@ impl Service {
|
||||||
.get(origin)
|
.get(origin)
|
||||||
.map(|s| Arc::clone(s).acquire_owned());
|
.map(|s| Arc::clone(s).acquire_owned());
|
||||||
|
|
||||||
let permit = match permit {
|
let permit = if let Some(permit) = permit {
|
||||||
Some(p) => p,
|
permit
|
||||||
None => {
|
} else {
|
||||||
let mut write = services().globals.servername_ratelimiter.write().await;
|
let mut write = services().globals.servername_ratelimiter.write().await;
|
||||||
let s = Arc::clone(
|
let s = Arc::clone(
|
||||||
write
|
write
|
||||||
|
@ -1705,7 +1681,6 @@ impl Service {
|
||||||
|
|
||||||
s.acquire_owned()
|
s.acquire_owned()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let back_off = |id| async {
|
let back_off = |id| async {
|
||||||
|
|
|
@ -46,8 +46,8 @@ impl Service {
|
||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
target: &EventId,
|
target: &EventId,
|
||||||
filter_event_type: Option<TimelineEventType>,
|
filter_event_type: &Option<TimelineEventType>,
|
||||||
filter_rel_type: Option<RelationType>,
|
filter_rel_type: &Option<RelationType>,
|
||||||
from: PduCount,
|
from: PduCount,
|
||||||
to: Option<PduCount>,
|
to: Option<PduCount>,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub trait Data: Send + Sync {
|
||||||
|
|
||||||
fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>;
|
fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>;
|
||||||
|
|
||||||
/// Returns (shortstatehash, already_existed)
|
/// Returns (shortstatehash, `already_existed`)
|
||||||
fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>;
|
fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>;
|
||||||
|
|
||||||
fn get_shortroomid(&self, room_id: &RoomId) -> Result<Option<u64>>;
|
fn get_shortroomid(&self, room_id: &RoomId) -> Result<Option<u64>>;
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl Service {
|
||||||
self.db.get_statekey_from_short(shortstatekey)
|
self.db.get_statekey_from_short(shortstatekey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns (shortstatehash, already_existed)
|
/// Returns (shortstatehash, `already_existed`)
|
||||||
pub fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> {
|
pub fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)> {
|
||||||
self.db.get_or_create_shortstatehash(state_hash)
|
self.db.get_or_create_shortstatehash(state_hash)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,13 +63,13 @@ impl Service {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
while let Some(current_room) = {
|
while let Some(current_room) = {
|
||||||
while stack.last().map_or(false, |s| s.is_empty()) {
|
while stack.last().map_or(false, Vec::is_empty) {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
if !stack.is_empty() {
|
if stack.is_empty() {
|
||||||
stack.last_mut().and_then(|s| s.pop())
|
|
||||||
} else {
|
|
||||||
None
|
None
|
||||||
|
} else {
|
||||||
|
stack.last_mut().and_then(Vec::pop)
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
rooms_in_path.push(current_room.clone());
|
rooms_in_path.push(current_room.clone());
|
||||||
|
@ -81,7 +81,7 @@ impl Service {
|
||||||
.roomid_spacechunk_cache
|
.roomid_spacechunk_cache
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.get_mut(¤t_room.to_owned())
|
.get_mut(¤t_room.clone())
|
||||||
.as_ref()
|
.as_ref()
|
||||||
{
|
{
|
||||||
if let Some(cached) = cached {
|
if let Some(cached) = cached {
|
||||||
|
@ -202,7 +202,7 @@ impl Service {
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server,
|
server,
|
||||||
federation::space::get_hierarchy::v1::Request {
|
federation::space::get_hierarchy::v1::Request {
|
||||||
room_id: current_room.to_owned(),
|
room_id: current_room.clone(),
|
||||||
suggested_only,
|
suggested_only,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -453,8 +453,7 @@ impl Service {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let allowed = match join_rule {
|
let allowed = match join_rule {
|
||||||
SpaceRoomJoinRule::Public => true,
|
SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::Public => true,
|
||||||
SpaceRoomJoinRule::Knock => true,
|
|
||||||
SpaceRoomJoinRule::Invite => services()
|
SpaceRoomJoinRule::Invite => services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub trait Data: Send + Sync {
|
||||||
/// Returns the last state hash key added to the db for the given room.
|
/// Returns the last state hash key added to the db for the given room.
|
||||||
fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>>;
|
fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>>;
|
||||||
|
|
||||||
/// Set the state hash to a new version, but does not update state_cache.
|
/// Set the state hash to a new version, but does not update `state_cache`.
|
||||||
fn set_room_state(
|
fn set_room_state(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
|
@ -18,7 +18,7 @@ pub trait Data: Send + Sync {
|
||||||
/// Associates a state with an event.
|
/// Associates a state with an event.
|
||||||
fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>;
|
fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>;
|
||||||
|
|
||||||
/// Returns all events we would send as the prev_events of the next event.
|
/// Returns all events we would send as the `prev_events` of the next event.
|
||||||
fn get_forward_extremities(&self, room_id: &RoomId) -> Result<HashSet<Arc<EventId>>>;
|
fn get_forward_extremities(&self, room_id: &RoomId) -> Result<HashSet<Arc<EventId>>>;
|
||||||
|
|
||||||
/// Replace the forward extremities of the room.
|
/// Replace the forward extremities of the room.
|
||||||
|
|
|
@ -45,9 +45,8 @@ impl Service {
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(_, id)| id)
|
.map(|(_, id)| id)
|
||||||
}) {
|
}) {
|
||||||
let pdu = match services().rooms.timeline.get_pdu_json(&event_id)? {
|
let Some(pdu) = services().rooms.timeline.get_pdu_json(&event_id)? else {
|
||||||
Some(pdu) => pdu,
|
continue;
|
||||||
None => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let pdu: PduEvent = match serde_json::from_str(
|
let pdu: PduEvent = match serde_json::from_str(
|
||||||
|
@ -70,14 +69,12 @@ impl Service {
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_key = match pdu.state_key {
|
let Some(state_key) = pdu.state_key else {
|
||||||
Some(k) => k,
|
continue;
|
||||||
None => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_id = match UserId::parse(state_key) {
|
let Ok(user_id) = UserId::parse(state_key) else {
|
||||||
Ok(id) => id,
|
continue;
|
||||||
Err(_) => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services().rooms.state_cache.update_membership(
|
services().rooms.state_cache.update_membership(
|
||||||
|
@ -374,11 +371,7 @@ impl Service {
|
||||||
state_key: Option<&str>,
|
state_key: Option<&str>,
|
||||||
content: &serde_json::value::RawValue,
|
content: &serde_json::value::RawValue,
|
||||||
) -> Result<StateMap<Arc<PduEvent>>> {
|
) -> Result<StateMap<Arc<PduEvent>>> {
|
||||||
let shortstatehash = if let Some(current_shortstatehash) =
|
let Some(shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
|
||||||
services().rooms.state.get_room_shortstatehash(room_id)?
|
|
||||||
{
|
|
||||||
current_shortstatehash
|
|
||||||
} else {
|
|
||||||
return Ok(HashMap::new());
|
return Ok(HashMap::new());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -85,16 +85,15 @@ impl Service {
|
||||||
/// The user was a joined member at this state (potentially in the past)
|
/// The user was a joined member at this state (potentially in the past)
|
||||||
fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool {
|
fn user_was_joined(&self, shortstatehash: u64, user_id: &UserId) -> bool {
|
||||||
self.user_membership(shortstatehash, user_id)
|
self.user_membership(shortstatehash, user_id)
|
||||||
.map(|s| s == MembershipState::Join)
|
.is_ok_and(|s| s == MembershipState::Join) // Return sensible default, i.e. false
|
||||||
.unwrap_or_default() // Return sensible default, i.e. false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The user was an invited or joined room member at this state (potentially
|
/// The user was an invited or joined room member at this state (potentially
|
||||||
/// in the past)
|
/// in the past)
|
||||||
fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool {
|
fn user_was_invited(&self, shortstatehash: u64, user_id: &UserId) -> bool {
|
||||||
self.user_membership(shortstatehash, user_id)
|
self.user_membership(shortstatehash, user_id)
|
||||||
.map(|s| s == MembershipState::Join || s == MembershipState::Invite)
|
.is_ok_and(|s| s == MembershipState::Join || s == MembershipState::Invite)
|
||||||
.unwrap_or_default() // Return sensible default, i.e. false
|
// Return sensible default, i.e. false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a server is allowed to see an event through federation, based on
|
/// Whether a server is allowed to see an event through federation, based on
|
||||||
|
@ -106,9 +105,8 @@ impl Service {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event_id: &EventId,
|
event_id: &EventId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let shortstatehash = match self.pdu_shortstatehash(event_id)? {
|
let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else {
|
||||||
Some(shortstatehash) => shortstatehash,
|
return Ok(true);
|
||||||
None => return Ok(true),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(visibility) = self
|
if let Some(visibility) = self
|
||||||
|
@ -170,9 +168,8 @@ impl Service {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event_id: &EventId,
|
event_id: &EventId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let shortstatehash = match self.pdu_shortstatehash(event_id)? {
|
let Some(shortstatehash) = self.pdu_shortstatehash(event_id)? else {
|
||||||
Some(shortstatehash) => shortstatehash,
|
return Ok(true);
|
||||||
None => return Ok(true),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(visibility) = self
|
if let Some(visibility) = self
|
||||||
|
@ -305,13 +302,13 @@ impl Service {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_can_invite(
|
pub fn user_can_invite(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
target_user: &UserId,
|
target_user: &UserId,
|
||||||
state_lock: &MutexGuard<'_, ()>,
|
state_lock: &MutexGuard<'_, ()>,
|
||||||
) -> Result<bool> {
|
) -> bool {
|
||||||
let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))
|
let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))
|
||||||
.expect("Event content always serializes");
|
.expect("Event content always serializes");
|
||||||
|
|
||||||
|
@ -323,11 +320,11 @@ impl Service {
|
||||||
redacts: None,
|
redacts: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.create_hash_and_sign_event(new_event, sender, room_id, state_lock)
|
.create_hash_and_sign_event(new_event, sender, room_id, state_lock)
|
||||||
.is_ok())
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_member(
|
pub fn get_member(
|
||||||
|
@ -358,13 +355,32 @@ impl Service {
|
||||||
federation: bool,
|
federation: bool,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
.map(|e| {
|
.map_or_else(
|
||||||
|
// Falling back on m.room.create to judge power levels
|
||||||
|
|| {
|
||||||
|
if let Some(pdu) =
|
||||||
|
self.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||||
|
{
|
||||||
|
Ok(pdu.sender == sender
|
||||||
|
|| if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
|
||||||
|
pdu.sender == sender
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::bad_database(
|
||||||
|
"No m.room.power_levels or m.room.create events in database for room",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|e| {
|
||||||
serde_json::from_str(e.content.get())
|
serde_json::from_str(e.content.get())
|
||||||
.map(|c: RoomPowerLevelsEventContent| c.into())
|
.map(|c: RoomPowerLevelsEventContent| c.into())
|
||||||
.map(|e: RoomPowerLevels| {
|
.map(|e: RoomPowerLevels| {
|
||||||
e.user_can_redact_event_of_other(sender)
|
e.user_can_redact_event_of_other(sender)
|
||||||
|| e.user_can_redact_own_event(sender)
|
|| e.user_can_redact_own_event(sender)
|
||||||
&& if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts)
|
&& if let Ok(Some(pdu)) =
|
||||||
|
services().rooms.timeline.get_pdu(redacts)
|
||||||
{
|
{
|
||||||
if federation {
|
if federation {
|
||||||
pdu.sender().server_name() == sender.server_name()
|
pdu.sender().server_name() == sender.server_name()
|
||||||
|
@ -378,21 +394,7 @@ impl Service {
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::bad_database("Invalid m.room.power_levels event in database")
|
Error::bad_database("Invalid m.room.power_levels event in database")
|
||||||
})
|
})
|
||||||
})
|
},
|
||||||
// Falling back on m.room.create to judge power levels
|
)
|
||||||
.unwrap_or_else(|| {
|
|
||||||
if let Some(pdu) = self.room_state_get(room_id, &StateEventType::RoomCreate, "")? {
|
|
||||||
Ok(pdu.sender == sender
|
|
||||||
|| if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
|
|
||||||
pdu.sender == sender
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(Error::bad_database(
|
|
||||||
"No m.room.power_levels or m.room.create events in database for room",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod data;
|
mod data;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads},
|
api::client::{error::ErrorKind, threads::get_threads::v1::IncludeThreads},
|
||||||
|
@ -56,7 +58,7 @@ impl Service {
|
||||||
|
|
||||||
if let CanonicalJsonValue::Object(unsigned) = root_pdu_json
|
if let CanonicalJsonValue::Object(unsigned) = root_pdu_json
|
||||||
.entry("unsigned".to_owned())
|
.entry("unsigned".to_owned())
|
||||||
.or_insert_with(|| CanonicalJsonValue::Object(Default::default()))
|
.or_insert_with(|| CanonicalJsonValue::Object(BTreeMap::default()))
|
||||||
{
|
{
|
||||||
if let Some(mut relations) = unsigned
|
if let Some(mut relations) = unsigned
|
||||||
.get("m.relations")
|
.get("m.relations")
|
||||||
|
|
|
@ -204,6 +204,23 @@ impl Service {
|
||||||
leaves: Vec<OwnedEventId>,
|
leaves: Vec<OwnedEventId>,
|
||||||
state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex
|
state_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
|
// Update Relationships
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ExtractRelatesTo {
|
||||||
|
#[serde(rename = "m.relates_to")]
|
||||||
|
relates_to: Relation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
struct ExtractEventId {
|
||||||
|
event_id: OwnedEventId,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
struct ExtractRelatesToEventId {
|
||||||
|
#[serde(rename = "m.relates_to")]
|
||||||
|
relates_to: ExtractEventId,
|
||||||
|
}
|
||||||
|
|
||||||
let shortroomid = services()
|
let shortroomid = services()
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
|
@ -216,7 +233,7 @@ impl Service {
|
||||||
if let Some(state_key) = &pdu.state_key {
|
if let Some(state_key) = &pdu.state_key {
|
||||||
if let CanonicalJsonValue::Object(unsigned) = pdu_json
|
if let CanonicalJsonValue::Object(unsigned) = pdu_json
|
||||||
.entry("unsigned".to_owned())
|
.entry("unsigned".to_owned())
|
||||||
.or_insert_with(|| CanonicalJsonValue::Object(Default::default()))
|
.or_insert_with(|| CanonicalJsonValue::Object(BTreeMap::default()))
|
||||||
{
|
{
|
||||||
if let Some(shortstatehash) = services()
|
if let Some(shortstatehash) = services()
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -340,8 +357,10 @@ impl Service {
|
||||||
.map_err(|_| Error::bad_database("Invalid push rules event in db."))
|
.map_err(|_| Error::bad_database("Invalid push rules event in db."))
|
||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.map(|ev: PushRulesEvent| ev.content.global)
|
.map_or_else(
|
||||||
.unwrap_or_else(|| Ruleset::server_default(user));
|
|| Ruleset::server_default(user),
|
||||||
|
|ev: PushRulesEvent| ev.content.global,
|
||||||
|
);
|
||||||
|
|
||||||
let mut highlight = false;
|
let mut highlight = false;
|
||||||
let mut notify = false;
|
let mut notify = false;
|
||||||
|
@ -505,23 +524,6 @@ impl Service {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Relationships
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ExtractRelatesTo {
|
|
||||||
#[serde(rename = "m.relates_to")]
|
|
||||||
relates_to: Relation,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
struct ExtractEventId {
|
|
||||||
event_id: OwnedEventId,
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
struct ExtractRelatesToEventId {
|
|
||||||
#[serde(rename = "m.relates_to")]
|
|
||||||
relates_to: ExtractEventId,
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(content) = serde_json::from_str::<ExtractRelatesToEventId>(pdu.content.get()) {
|
if let Ok(content) = serde_json::from_str::<ExtractRelatesToEventId>(pdu.content.get()) {
|
||||||
if let Some(related_pducount) = services()
|
if let Some(related_pducount) = services()
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -786,7 +788,7 @@ impl Service {
|
||||||
&mut pdu_json,
|
&mut pdu_json,
|
||||||
&room_version_id,
|
&room_version_id,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return match e {
|
return match e {
|
||||||
ruma::signatures::Error::PduSize => Err(Error::BadRequest(
|
ruma::signatures::Error::PduSize => Err(Error::BadRequest(
|
||||||
|
@ -857,7 +859,7 @@ impl Service {
|
||||||
.filter(|v| v.starts_with('@'))
|
.filter(|v| v.starts_with('@'))
|
||||||
.unwrap_or(sender.as_str());
|
.unwrap_or(sender.as_str());
|
||||||
let server_name = services().globals.server_name();
|
let server_name = services().globals.server_name();
|
||||||
let server_user = format!("@conduit:{}", server_name);
|
let server_user = format!("@conduit:{server_name}");
|
||||||
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
|
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
||||||
|
|
||||||
|
@ -1199,7 +1201,7 @@ impl Service {
|
||||||
.roomid_mutex_federation
|
.roomid_mutex_federation
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.entry(room_id.to_owned())
|
.entry(room_id.clone())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
let mutex_lock = mutex.lock().await;
|
let mutex_lock = mutex.lock().await;
|
||||||
|
|
|
@ -160,7 +160,9 @@ impl Service {
|
||||||
// Find events that have been added since starting the last request
|
// Find events that have been added since starting the last request
|
||||||
let new_events = self.db.queued_requests(&outgoing_kind).filter_map(|r| r.ok()).take(30).collect::<Vec<_>>();
|
let new_events = self.db.queued_requests(&outgoing_kind).filter_map(|r| r.ok()).take(30).collect::<Vec<_>>();
|
||||||
|
|
||||||
if !new_events.is_empty() {
|
if new_events.is_empty() {
|
||||||
|
current_transaction_status.remove(&outgoing_kind);
|
||||||
|
} else {
|
||||||
// Insert pdus we found
|
// Insert pdus we found
|
||||||
self.db.mark_as_active(&new_events)?;
|
self.db.mark_as_active(&new_events)?;
|
||||||
|
|
||||||
|
@ -170,8 +172,6 @@ impl Service {
|
||||||
new_events.into_iter().map(|(event, _)| event).collect(),
|
new_events.into_iter().map(|(event, _)| event).collect(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
current_transaction_status.remove(&outgoing_kind);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((outgoing_kind, _)) => {
|
Err((outgoing_kind, _)) => {
|
||||||
|
@ -305,8 +305,7 @@ impl Service {
|
||||||
let event: AnySyncEphemeralRoomEvent =
|
let event: AnySyncEphemeralRoomEvent =
|
||||||
serde_json::from_str(read_receipt.json().get())
|
serde_json::from_str(read_receipt.json().get())
|
||||||
.map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?;
|
.map_err(|_| Error::bad_database("Invalid edu event in read_receipts."))?;
|
||||||
let federation_event = match event {
|
let federation_event = if let AnySyncEphemeralRoomEvent::Receipt(r) = event {
|
||||||
AnySyncEphemeralRoomEvent::Receipt(r) => {
|
|
||||||
let mut read = BTreeMap::new();
|
let mut read = BTreeMap::new();
|
||||||
|
|
||||||
let (event_id, mut receipt) = r
|
let (event_id, mut receipt) = r
|
||||||
|
@ -335,11 +334,9 @@ impl Service {
|
||||||
receipts.insert(room_id.clone(), receipt_map);
|
receipts.insert(room_id.clone(), receipt_map);
|
||||||
|
|
||||||
Edu::Receipt(ReceiptContent { receipts })
|
Edu::Receipt(ReceiptContent { receipts })
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
Error::bad_database("Invalid event type in read_receipts");
|
Error::bad_database("Invalid event type in read_receipts");
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
events.push(serde_json::to_vec(&federation_event).expect("json can be serialized"));
|
events.push(serde_json::to_vec(&federation_event).expect("json can be serialized"));
|
||||||
|
@ -404,7 +401,7 @@ impl Service {
|
||||||
)?;
|
)?;
|
||||||
for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) {
|
for ((outgoing_kind, event), key) in requests.into_iter().zip(keys) {
|
||||||
self.sender
|
self.sender
|
||||||
.send((outgoing_kind.to_owned(), event, key))
|
.send((outgoing_kind.clone(), event, key))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +471,7 @@ impl Service {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.to_room_event())
|
.to_room_event());
|
||||||
}
|
}
|
||||||
SendingEventType::Edu(_) => {
|
SendingEventType::Edu(_) => {
|
||||||
// Appservices don't need EDUs (?)
|
// Appservices don't need EDUs (?)
|
||||||
|
@ -559,13 +556,12 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pusher = match services()
|
let Some(pusher) = services()
|
||||||
.pusher
|
.pusher
|
||||||
.get_pusher(userid, pushkey)
|
.get_pusher(userid, pushkey)
|
||||||
.map_err(|e| (OutgoingKind::Push(userid.clone(), pushkey.clone()), e))?
|
.map_err(|e| (OutgoingKind::Push(userid.clone(), pushkey.clone()), e))?
|
||||||
{
|
else {
|
||||||
Some(pusher) => pusher,
|
continue;
|
||||||
None => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let rules_for_user = services()
|
let rules_for_user = services()
|
||||||
|
@ -577,8 +573,10 @@ impl Service {
|
||||||
)
|
)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.and_then(|event| serde_json::from_str::<PushRulesEvent>(event.get()).ok())
|
.and_then(|event| serde_json::from_str::<PushRulesEvent>(event.get()).ok())
|
||||||
.map(|ev: PushRulesEvent| ev.content.global)
|
.map_or_else(
|
||||||
.unwrap_or_else(|| push::Ruleset::server_default(userid));
|
|| push::Ruleset::server_default(userid),
|
||||||
|
|ev: PushRulesEvent| ev.content.global,
|
||||||
|
);
|
||||||
|
|
||||||
let unread: UInt = services()
|
let unread: UInt = services()
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -47,10 +47,10 @@ impl Service {
|
||||||
auth: &AuthData,
|
auth: &AuthData,
|
||||||
uiaainfo: &UiaaInfo,
|
uiaainfo: &UiaaInfo,
|
||||||
) -> Result<(bool, UiaaInfo)> {
|
) -> Result<(bool, UiaaInfo)> {
|
||||||
let mut uiaainfo = auth
|
let mut uiaainfo = auth.session().map_or_else(
|
||||||
.session()
|
|| Ok(uiaainfo.clone()),
|
||||||
.map(|session| self.db.get_uiaa_session(user_id, device_id, session))
|
|session| self.db.get_uiaa_session(user_id, device_id, session),
|
||||||
.unwrap_or_else(|| Ok(uiaainfo.clone()))?;
|
)?;
|
||||||
|
|
||||||
if uiaainfo.session.is_none() {
|
if uiaainfo.session.is_none() {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
|
@ -63,14 +63,11 @@ impl Service {
|
||||||
password,
|
password,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let username = match identifier {
|
let UserIdentifier::UserIdOrLocalpart(username) = identifier else {
|
||||||
UserIdentifier::UserIdOrLocalpart(username) => username,
|
|
||||||
_ => {
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Unrecognized,
|
ErrorKind::Unrecognized,
|
||||||
"Identifier type not recognized.",
|
"Identifier type not recognized.",
|
||||||
))
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_id = UserId::parse_with_server_name(
|
let user_id = UserId::parse_with_server_name(
|
||||||
|
|
|
@ -42,16 +42,16 @@ pub trait Data: Send + Sync {
|
||||||
/// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change.
|
/// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change.
|
||||||
fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()>;
|
fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()>;
|
||||||
|
|
||||||
/// Get the avatar_url of a user.
|
/// Get the `avatar_url` of a user.
|
||||||
fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>>;
|
fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>>;
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()>;
|
fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()>;
|
||||||
|
|
||||||
/// Get the blurhash of a user.
|
/// Get the blurhash of a user.
|
||||||
fn blurhash(&self, user_id: &UserId) -> Result<Option<String>>;
|
fn blurhash(&self, user_id: &UserId) -> Result<Option<String>>;
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()>;
|
fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()>;
|
||||||
|
|
||||||
/// Adds a new device to a user.
|
/// Adds a new device to a user.
|
||||||
|
|
|
@ -325,12 +325,12 @@ impl Service {
|
||||||
self.db.set_displayname(user_id, displayname)
|
self.db.set_displayname(user_id, displayname)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the avatar_url of a user.
|
/// Get the `avatar_url` of a user.
|
||||||
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>> {
|
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<OwnedMxcUri>> {
|
||||||
self.db.avatar_url(user_id)
|
self.db.avatar_url(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()> {
|
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<OwnedMxcUri>) -> Result<()> {
|
||||||
self.db.set_avatar_url(user_id, avatar_url)
|
self.db.set_avatar_url(user_id, avatar_url)
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@ impl Service {
|
||||||
self.db.blurhash(user_id)
|
self.db.blurhash(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
/// Sets a new `avatar_url` or removes it if `avatar_url` is None.
|
||||||
pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
|
pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) -> Result<()> {
|
||||||
self.db.set_blurhash(user_id, blurhash)
|
self.db.set_blurhash(user_id, blurhash)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
BadConfig(&'static str),
|
BadConfig(&'static str),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
/// Don't create this directly. Use Error::bad_database instead.
|
/// Don't create this directly. Use `Error::bad_database` instead.
|
||||||
BadDatabase(&'static str),
|
BadDatabase(&'static str),
|
||||||
#[error("uiaa")]
|
#[error("uiaa")]
|
||||||
Uiaa(UiaaInfo),
|
Uiaa(UiaaInfo),
|
||||||
|
@ -107,6 +107,9 @@ impl Error {
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
||||||
|
#[allow(clippy::enum_glob_use)]
|
||||||
|
use ErrorKind::*;
|
||||||
|
|
||||||
if let Self::Uiaa(uiaainfo) = self {
|
if let Self::Uiaa(uiaainfo) = self {
|
||||||
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
||||||
}
|
}
|
||||||
|
@ -122,20 +125,19 @@ impl Error {
|
||||||
|
|
||||||
let message = format!("{self}");
|
let message = format!("{self}");
|
||||||
|
|
||||||
use ErrorKind::*;
|
|
||||||
let (kind, status_code) = match self {
|
let (kind, status_code) = match self {
|
||||||
Self::BadRequest(kind, _) => (
|
Self::BadRequest(kind, _) => (
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
match kind {
|
match kind {
|
||||||
WrongRoomKeysVersion { .. }
|
Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED,
|
||||||
|
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
||||||
|
LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
||||||
|
UserDeactivated
|
||||||
|
| WrongRoomKeysVersion { .. }
|
||||||
| Forbidden
|
| Forbidden
|
||||||
| GuestAccessForbidden
|
| GuestAccessForbidden
|
||||||
| ThreepidAuthFailed
|
| ThreepidAuthFailed
|
||||||
| ThreepidDenied => StatusCode::FORBIDDEN,
|
| ThreepidDenied => StatusCode::FORBIDDEN,
|
||||||
Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED,
|
|
||||||
NotFound | Unrecognized => StatusCode::NOT_FOUND,
|
|
||||||
LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
|
||||||
UserDeactivated => StatusCode::FORBIDDEN,
|
|
||||||
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||||
_ => StatusCode::BAD_REQUEST,
|
_ => StatusCode::BAD_REQUEST,
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,8 +18,8 @@ pub fn millis_since_unix_epoch() -> u64 {
|
||||||
.as_millis() as u64
|
.as_millis() as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
pub fn increment(old: Option<&[u8]>) -> Vec<u8> {
|
||||||
let number = match old.map(|bytes| bytes.try_into()) {
|
let number = match old.map(TryInto::try_into) {
|
||||||
Some(Ok(bytes)) => {
|
Some(Ok(bytes)) => {
|
||||||
let number = u64::from_be_bytes(bytes);
|
let number = u64::from_be_bytes(bytes);
|
||||||
number + 1
|
number + 1
|
||||||
|
@ -27,7 +27,7 @@ pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
||||||
_ => 1, // Start at one. since 0 should return the first event in the db
|
_ => 1, // Start at one. since 0 should return the first event in the db
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(number.to_be_bytes().to_vec())
|
number.to_be_bytes().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_keypair() -> Vec<u8> {
|
pub fn generate_keypair() -> Vec<u8> {
|
||||||
|
@ -83,7 +83,7 @@ pub fn common_elements(
|
||||||
check_order: impl Fn(&[u8], &[u8]) -> Ordering,
|
check_order: impl Fn(&[u8], &[u8]) -> Ordering,
|
||||||
) -> Option<impl Iterator<Item = Vec<u8>>> {
|
) -> Option<impl Iterator<Item = Vec<u8>>> {
|
||||||
let first_iterator = iterators.next()?;
|
let first_iterator = iterators.next()?;
|
||||||
let mut other_iterators = iterators.map(|i| i.peekable()).collect::<Vec<_>>();
|
let mut other_iterators = iterators.map(Iterator::peekable).collect::<Vec<_>>();
|
||||||
|
|
||||||
Some(first_iterator.filter(move |target| {
|
Some(first_iterator.filter(move |target| {
|
||||||
other_iterators.iter_mut().all(|it| {
|
other_iterators.iter_mut().all(|it| {
|
||||||
|
|
Loading…
Reference in a new issue