apply new rustfmt.toml changes, fix some clippy lints
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
0317cc8cc5
commit
77e0b76408
296 changed files with 7147 additions and 4300 deletions
|
@ -21,3 +21,4 @@ indent_size = 2
|
||||||
|
|
||||||
[*.rs]
|
[*.rs]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
max_line_length = 98
|
||||||
|
|
|
@ -3,7 +3,7 @@ cognitive-complexity-threshold = 94 # TODO reduce me ALARA
|
||||||
excessive-nesting-threshold = 11 # TODO reduce me to 4 or 5
|
excessive-nesting-threshold = 11 # TODO reduce me to 4 or 5
|
||||||
future-size-threshold = 7745 # TODO reduce me ALARA
|
future-size-threshold = 7745 # TODO reduce me ALARA
|
||||||
stack-size-threshold = 196608 # reduce me ALARA
|
stack-size-threshold = 196608 # reduce me ALARA
|
||||||
too-many-lines-threshold = 700 # TODO reduce me to <= 100
|
too-many-lines-threshold = 780 # TODO reduce me to <= 100
|
||||||
type-complexity-threshold = 250 # reduce me to ~200
|
type-complexity-threshold = 250 # reduce me to ~200
|
||||||
|
|
||||||
disallowed-macros = [
|
disallowed-macros = [
|
||||||
|
@ -13,3 +13,7 @@ disallowed-macros = [
|
||||||
{ path = "log::debug", reason = "use conduwuit_core::debug" },
|
{ path = "log::debug", reason = "use conduwuit_core::debug" },
|
||||||
{ path = "log::trace", reason = "use conduwuit_core::trace" },
|
{ path = "log::trace", reason = "use conduwuit_core::trace" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "tokio::spawn", reason = "use and pass conduuwit_core::server::Server::runtime() to spawn from"},
|
||||||
|
]
|
||||||
|
|
12
rustfmt.toml
12
rustfmt.toml
|
@ -2,9 +2,8 @@ array_width = 80
|
||||||
chain_width = 60
|
chain_width = 60
|
||||||
comment_width = 80
|
comment_width = 80
|
||||||
condense_wildcard_suffixes = true
|
condense_wildcard_suffixes = true
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
fn_call_width = 80
|
fn_call_width = 80
|
||||||
fn_params_layout = "Compressed"
|
|
||||||
fn_single_line = true
|
fn_single_line = true
|
||||||
format_code_in_doc_comments = true
|
format_code_in_doc_comments = true
|
||||||
format_macro_bodies = true
|
format_macro_bodies = true
|
||||||
|
@ -14,13 +13,20 @@ group_imports = "StdExternalCrate"
|
||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
hex_literal_case = "Upper"
|
hex_literal_case = "Upper"
|
||||||
imports_granularity = "Crate"
|
imports_granularity = "Crate"
|
||||||
|
match_arm_blocks = false
|
||||||
|
match_arm_leading_pipes = "Always"
|
||||||
match_block_trailing_comma = true
|
match_block_trailing_comma = true
|
||||||
max_width = 120
|
max_width = 98
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
normalize_comments = false
|
normalize_comments = false
|
||||||
|
overflow_delimited_expr = true
|
||||||
reorder_impl_items = true
|
reorder_impl_items = true
|
||||||
reorder_imports = true
|
reorder_imports = true
|
||||||
|
single_line_if_else_max_width = 60
|
||||||
|
single_line_let_else_max_width = 80
|
||||||
|
struct_lit_width = 40
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
|
unstable_features = true
|
||||||
use_field_init_shorthand = true
|
use_field_init_shorthand = true
|
||||||
use_small_heuristics = "Off"
|
use_small_heuristics = "Off"
|
||||||
use_try_shorthand = true
|
use_try_shorthand = true
|
||||||
|
|
|
@ -3,9 +3,10 @@ use conduwuit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, command::Command, debug,
|
appservice, appservice::AppserviceCommand, check, check::CheckCommand, command::Command,
|
||||||
debug::DebugCommand, federation, federation::FederationCommand, media, media::MediaCommand, query,
|
debug, debug::DebugCommand, federation, federation::FederationCommand, media,
|
||||||
query::QueryCommand, room, room::RoomCommand, server, server::ServerCommand, user, user::UserCommand,
|
media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand, server,
|
||||||
|
server::ServerCommand, user, user::UserCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -49,18 +50,21 @@ pub(super) enum AdminCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, name = "command")]
|
#[tracing::instrument(skip_all, name = "command")]
|
||||||
pub(super) async fn process(command: AdminCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
command: AdminCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
use AdminCommand::*;
|
use AdminCommand::*;
|
||||||
|
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
Appservices(command) => appservice::process(command, context).await?,
|
| Appservices(command) => appservice::process(command, context).await?,
|
||||||
Media(command) => media::process(command, context).await?,
|
| Media(command) => media::process(command, context).await?,
|
||||||
Users(command) => user::process(command, context).await?,
|
| Users(command) => user::process(command, context).await?,
|
||||||
Rooms(command) => room::process(command, context).await?,
|
| Rooms(command) => room::process(command, context).await?,
|
||||||
Federation(command) => federation::process(command, context).await?,
|
| Federation(command) => federation::process(command, context).await?,
|
||||||
Server(command) => server::process(command, context).await?,
|
| Server(command) => server::process(command, context).await?,
|
||||||
Debug(command) => debug::process(command, context).await?,
|
| Debug(command) => debug::process(command, context).await?,
|
||||||
Query(command) => query::process(command, context).await?,
|
| Query(command) => query::process(command, context).await?,
|
||||||
Check(command) => check::process(command, context).await?,
|
| Check(command) => check::process(command, context).await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ use crate::{admin_command, Result};
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
|
pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -14,55 +16,63 @@ pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
|
||||||
let appservice_config_body = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
let appservice_config_body = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config_body);
|
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config_body);
|
||||||
match parsed_config {
|
match parsed_config {
|
||||||
Ok(registration) => match self
|
| Ok(registration) => match self
|
||||||
.services
|
.services
|
||||||
.appservice
|
.appservice
|
||||||
.register_appservice(®istration, &appservice_config_body)
|
.register_appservice(®istration, &appservice_config_body)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Appservice registered with ID: {}",
|
"Appservice registered with ID: {}",
|
||||||
registration.id
|
registration.id
|
||||||
))),
|
))),
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to register appservice: {e}"
|
"Failed to register appservice: {e}"
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Could not parse appservice config as YAML: {e}"
|
"Could not parse appservice config as YAML: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
pub(super) async fn unregister(
|
||||||
|
&self,
|
||||||
|
appservice_identifier: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
.appservice
|
.appservice
|
||||||
.unregister_appservice(&appservice_identifier)
|
.unregister_appservice(&appservice_identifier)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Appservice unregistered.")),
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain("Appservice unregistered.")),
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to unregister appservice: {e}"
|
"Failed to unregister appservice: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn show_appservice_config(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
pub(super) async fn show_appservice_config(
|
||||||
|
&self,
|
||||||
|
appservice_identifier: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
.appservice
|
.appservice
|
||||||
.get_registration(&appservice_identifier)
|
.get_registration(&appservice_identifier)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Some(config) => {
|
| Some(config) => {
|
||||||
let config_str = serde_yaml::to_string(&config).expect("config should've been validated on register");
|
let config_str = serde_yaml::to_string(&config)
|
||||||
let output = format!("Config for {appservice_identifier}:\n\n```yaml\n{config_str}\n```",);
|
.expect("config should've been validated on register");
|
||||||
|
let output =
|
||||||
|
format!("Config for {appservice_identifier}:\n\n```yaml\n{config_str}\n```",);
|
||||||
Ok(RoomMessageEventContent::notice_markdown(output))
|
Ok(RoomMessageEventContent::notice_markdown(output))
|
||||||
},
|
},
|
||||||
None => Ok(RoomMessageEventContent::text_plain("Appservice does not exist.")),
|
| None => Ok(RoomMessageEventContent::text_plain("Appservice does not exist.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,9 @@ pub(super) async fn check_all_users(&self) -> Result<RoomMessageEventContent> {
|
||||||
let ok_count = users.iter().filter(|_user| true).count();
|
let ok_count = users.iter().filter(|_user| true).count();
|
||||||
|
|
||||||
let message = format!(
|
let message = format!(
|
||||||
"Database query completed in {query_time:?}:\n\n```\nTotal entries: {total:?}\nFailure/Invalid user count: \
|
"Database query completed in {query_time:?}:\n\n```\nTotal entries: \
|
||||||
{err_count:?}\nSuccess/Valid user count: {ok_count:?}\n```"
|
{total:?}\nFailure/Invalid user count: {err_count:?}\nSuccess/Valid user count: \
|
||||||
|
{ok_count:?}\n```"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(message))
|
Ok(RoomMessageEventContent::notice_markdown(message))
|
||||||
|
|
|
@ -11,8 +11,11 @@ pub(super) enum CheckCommand {
|
||||||
AllUsers,
|
AllUsers,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: CheckCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
command: CheckCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
CheckCommand::AllUsers => context.check_all_users().await?,
|
| CheckCommand::AllUsers => context.check_all_users().await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ use std::{
|
||||||
time::{Instant, SystemTime},
|
time::{Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use conduwuit::{debug_error, err, info, trace, utils, utils::string::EMPTY, warn, Error, PduEvent, Result};
|
use conduwuit::{
|
||||||
|
debug_error, err, info, trace, utils, utils::string::EMPTY, warn, Error, PduEvent, Result,
|
||||||
|
};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{client::error::ErrorKind, federation::event::get_room_state},
|
api::{client::error::ErrorKind, federation::event::get_room_state},
|
||||||
|
@ -26,7 +28,10 @@ pub(super) async fn echo(&self, message: Vec<String>) -> Result<RoomMessageEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn get_auth_chain(
|
||||||
|
&self,
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let Ok(event) = self.services.rooms.timeline.get_pdu_json(&event_id).await else {
|
let Ok(event) = self.services.rooms.timeline.get_pdu_json(&event_id).await else {
|
||||||
return Ok(RoomMessageEventContent::notice_plain("Event not found."));
|
return Ok(RoomMessageEventContent::notice_plain("Event not found."));
|
||||||
};
|
};
|
||||||
|
@ -68,20 +73,26 @@ pub(super) async fn parse_pdu(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
|
||||||
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
|
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
match serde_json::from_str(&string) {
|
||||||
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
| Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||||
Ok(hash) => {
|
| Ok(hash) => {
|
||||||
let event_id = EventId::parse(format!("${hash}"));
|
let event_id = EventId::parse(format!("${hash}"));
|
||||||
|
|
||||||
match serde_json::from_value::<PduEvent>(serde_json::to_value(value).expect("value is json")) {
|
match serde_json::from_value::<PduEvent>(
|
||||||
Ok(pdu) => Ok(RoomMessageEventContent::text_plain(format!("EventId: {event_id:?}\n{pdu:#?}"))),
|
serde_json::to_value(value).expect("value is json"),
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
) {
|
||||||
|
| Ok(pdu) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"EventId: {event_id:?}\n{pdu:#?}"
|
||||||
|
))),
|
||||||
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"EventId: {event_id:?}\nCould not parse event: {e}"
|
"EventId: {event_id:?}\nCould not parse event: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Could not parse PDU JSON: {e:?}"))),
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Could not parse PDU JSON: {e:?}"
|
||||||
|
))),
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Invalid json in command body: {e}"
|
"Invalid json in command body: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
|
@ -103,8 +114,9 @@ pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
match pdu_json {
|
match pdu_json {
|
||||||
Ok(json) => {
|
| Ok(json) => {
|
||||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
let json_text =
|
||||||
|
serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{}\n```json\n{}\n```",
|
"{}\n```json\n{}\n```",
|
||||||
if outlier {
|
if outlier {
|
||||||
|
@ -115,13 +127,15 @@ pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessage
|
||||||
json_text
|
json_text
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Err(_) => Ok(RoomMessageEventContent::text_plain("PDU not found locally.")),
|
| Err(_) => Ok(RoomMessageEventContent::text_plain("PDU not found locally.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_pdu_list(
|
pub(super) async fn get_remote_pdu_list(
|
||||||
&self, server: Box<ServerName>, force: bool,
|
&self,
|
||||||
|
server: Box<ServerName>,
|
||||||
|
force: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self.services.globals.config.allow_federation {
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -131,8 +145,8 @@ pub(super) async fn get_remote_pdu_list(
|
||||||
|
|
||||||
if server == self.services.globals.server_name() {
|
if server == self.services.globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs from \
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for \
|
||||||
the database.",
|
fetching local PDUs from the database.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +198,9 @@ pub(super) async fn get_remote_pdu_list(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_pdu(
|
pub(super) async fn get_remote_pdu(
|
||||||
&self, event_id: Box<EventId>, server: Box<ServerName>,
|
&self,
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
server: Box<ServerName>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self.services.globals.config.allow_federation {
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -194,30 +210,32 @@ pub(super) async fn get_remote_pdu(
|
||||||
|
|
||||||
if server == self.services.globals.server_name() {
|
if server == self.services.globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for \
|
||||||
|
fetching local PDUs.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(&server, ruma::api::federation::event::get_event::v1::Request {
|
||||||
&server,
|
event_id: event_id.clone().into(),
|
||||||
ruma::api::federation::event::get_event::v1::Request {
|
include_unredacted_content: None,
|
||||||
event_id: event_id.clone().into(),
|
})
|
||||||
include_unredacted_content: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(response) => {
|
| Ok(response) => {
|
||||||
let json: CanonicalJsonObject = serde_json::from_str(response.pdu.get()).map_err(|e| {
|
let json: CanonicalJsonObject =
|
||||||
warn!(
|
serde_json::from_str(response.pdu.get()).map_err(|e| {
|
||||||
"Requested event ID {event_id} from server but failed to convert from RawValue to \
|
warn!(
|
||||||
CanonicalJsonObject (malformed event/response?): {e}"
|
"Requested event ID {event_id} from server but failed to convert from \
|
||||||
);
|
RawValue to CanonicalJsonObject (malformed event/response?): {e}"
|
||||||
Error::BadRequest(ErrorKind::Unknown, "Received response from server but failed to parse PDU")
|
);
|
||||||
})?;
|
Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Received response from server but failed to parse PDU",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
trace!("Attempting to parse PDU: {:?}", &response.pdu);
|
trace!("Attempting to parse PDU: {:?}", &response.pdu);
|
||||||
let _parsed_pdu = {
|
let _parsed_pdu = {
|
||||||
|
@ -229,8 +247,8 @@ pub(super) async fn get_remote_pdu(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (event_id, value, room_id) = match parsed_result {
|
let (event_id, value, room_id) = match parsed_result {
|
||||||
Ok(t) => t,
|
| Ok(t) => t,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
warn!("Failed to parse PDU: {e}");
|
warn!("Failed to parse PDU: {e}");
|
||||||
info!("Full PDU: {:?}", &response.pdu);
|
info!("Full PDU: {:?}", &response.pdu);
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -250,21 +268,27 @@ pub(super) async fn get_remote_pdu(
|
||||||
.boxed()
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
let json_text =
|
||||||
|
serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{}\n```json\n{}\n```",
|
"{}\n```json\n{}\n```",
|
||||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:", json_text
|
"Got PDU from specified server and handled as backfilled PDU successfully. \
|
||||||
|
Event body:",
|
||||||
|
json_text
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Remote server did not have PDU or failed sending request to remote server: {e}"
|
"Remote server did not have PDU or failed sending request to remote server: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
pub(super) async fn get_room_state(
|
||||||
|
&self,
|
||||||
|
room: OwnedRoomOrAliasId,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
||||||
let room_state: Vec<_> = self
|
let room_state: Vec<_> = self
|
||||||
.services
|
.services
|
||||||
|
@ -285,7 +309,8 @@ pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<Ro
|
||||||
let json = serde_json::to_string_pretty(&room_state).map_err(|e| {
|
let json = serde_json::to_string_pretty(&room_state).map_err(|e| {
|
||||||
warn!("Failed converting room state vector in our database to pretty JSON: {e}");
|
warn!("Failed converting room state vector in our database to pretty JSON: {e}");
|
||||||
Error::bad_database(
|
Error::bad_database(
|
||||||
"Failed to convert room state events to pretty JSON, possible invalid room state events in our database",
|
"Failed to convert room state events to pretty JSON, possible invalid room state \
|
||||||
|
events in our database",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -305,10 +330,13 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
|
.send_federation_request(
|
||||||
|
&server,
|
||||||
|
ruma::api::federation::discovery::get_server_version::v1::Request {},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(response) => {
|
| Ok(response) => {
|
||||||
let ping_time = timer.elapsed();
|
let ping_time = timer.elapsed();
|
||||||
|
|
||||||
let json_text_res = serde_json::to_string_pretty(&response.server);
|
let json_text_res = serde_json::to_string_pretty(&response.server);
|
||||||
|
@ -323,8 +351,11 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
|
||||||
"Got non-JSON response which took {ping_time:?} time:\n{response:?}"
|
"Got non-JSON response which took {ping_time:?} time:\n{response:?}"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
warn!("Failed sending federation request to specified server from ping debug command: {e}");
|
warn!(
|
||||||
|
"Failed sending federation request to specified server from ping debug command: \
|
||||||
|
{e}"
|
||||||
|
);
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed sending federation request to specified server:\n\n{e}",
|
"Failed sending federation request to specified server:\n\n{e}",
|
||||||
)))
|
)))
|
||||||
|
@ -347,13 +378,17 @@ pub(super) async fn force_device_list_updates(&self) -> Result<RoomMessageEventC
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool) -> Result<RoomMessageEventContent> {
|
pub(super) async fn change_log_level(
|
||||||
|
&self,
|
||||||
|
filter: Option<String>,
|
||||||
|
reset: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let handles = &["console"];
|
let handles = &["console"];
|
||||||
|
|
||||||
if reset {
|
if reset {
|
||||||
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
|
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
|
||||||
Ok(s) => s,
|
| Ok(s) => s,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Log level from config appears to be invalid now: {e}"
|
"Log level from config appears to be invalid now: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -367,13 +402,13 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||||
.reload
|
.reload
|
||||||
.reload(&old_filter_layer, Some(handles))
|
.reload(&old_filter_layer, Some(handles))
|
||||||
{
|
{
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Successfully changed log level back to config value {}",
|
"Successfully changed log level back to config value {}",
|
||||||
self.services.globals.config.log
|
self.services.globals.config.log
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to modify and reload the global tracing log level: {e}"
|
"Failed to modify and reload the global tracing log level: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -383,8 +418,8 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||||
|
|
||||||
if let Some(filter) = filter {
|
if let Some(filter) = filter {
|
||||||
let new_filter_layer = match EnvFilter::try_new(filter) {
|
let new_filter_layer = match EnvFilter::try_new(filter) {
|
||||||
Ok(s) => s,
|
| Ok(s) => s,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Invalid log level filter specified: {e}"
|
"Invalid log level filter specified: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -398,10 +433,10 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||||
.reload
|
.reload
|
||||||
.reload(&new_filter_layer, Some(handles))
|
.reload(&new_filter_layer, Some(handles))
|
||||||
{
|
{
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to modify and reload the global tracing log level: {e}"
|
"Failed to modify and reload the global tracing log level: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -414,7 +449,9 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
|
pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -423,21 +460,24 @@ pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
|
||||||
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
match serde_json::from_str(&string) {
|
||||||
Ok(mut value) => {
|
| Ok(mut value) => {
|
||||||
self.services
|
self.services
|
||||||
.server_keys
|
.server_keys
|
||||||
.sign_json(&mut value)
|
.sign_json(&mut value)
|
||||||
.expect("our request json is what ruma expects");
|
.expect("our request json is what ruma expects");
|
||||||
let json_text = serde_json::to_string_pretty(&value).expect("canonical json is valid json");
|
let json_text =
|
||||||
|
serde_json::to_string_pretty(&value).expect("canonical json is valid json");
|
||||||
Ok(RoomMessageEventContent::text_plain(json_text))
|
Ok(RoomMessageEventContent::text_plain(json_text))
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))),
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
|
pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -446,13 +486,13 @@ pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
|
||||||
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||||
match serde_json::from_str::<CanonicalJsonObject>(&string) {
|
match serde_json::from_str::<CanonicalJsonObject>(&string) {
|
||||||
Ok(value) => match self.services.server_keys.verify_json(&value, None).await {
|
| Ok(value) => match self.services.server_keys.verify_json(&value, None).await {
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Signature correct")),
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain("Signature correct")),
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Signature verification failed: {e}"
|
"Signature verification failed: {e}"
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))),
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,9 +502,10 @@ pub(super) async fn verify_pdu(&self, event_id: Box<EventId>) -> Result<RoomMess
|
||||||
|
|
||||||
event.remove("event_id");
|
event.remove("event_id");
|
||||||
let msg = match self.services.server_keys.verify_event(&event, None).await {
|
let msg = match self.services.server_keys.verify_event(&event, None).await {
|
||||||
Ok(ruma::signatures::Verified::Signatures) => "signatures OK, but content hash failed (redaction).",
|
| Ok(ruma::signatures::Verified::Signatures) =>
|
||||||
Ok(ruma::signatures::Verified::All) => "signatures and hashes OK.",
|
"signatures OK, but content hash failed (redaction).",
|
||||||
Err(e) => return Err(e),
|
| Ok(ruma::signatures::Verified::All) => "signatures and hashes OK.",
|
||||||
|
| Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain(msg))
|
Ok(RoomMessageEventContent::notice_plain(msg))
|
||||||
|
@ -472,7 +513,10 @@ pub(super) async fn verify_pdu(&self, event_id: Box<EventId>) -> Result<RoomMess
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn first_pdu_in_room(
|
||||||
|
&self,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self
|
if !self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -498,7 +542,10 @@ pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Roo
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn latest_pdu_in_room(
|
||||||
|
&self,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self
|
if !self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -525,7 +572,9 @@ pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Ro
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub(super) async fn force_set_room_state_from_server(
|
pub(super) async fn force_set_room_state_from_server(
|
||||||
&self, room_id: Box<RoomId>, server_name: Box<ServerName>,
|
&self,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
server_name: Box<ServerName>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self
|
if !self
|
||||||
.services
|
.services
|
||||||
|
@ -554,13 +603,10 @@ pub(super) async fn force_set_room_state_from_server(
|
||||||
let remote_state_response = self
|
let remote_state_response = self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(&server_name, get_room_state::v1::Request {
|
||||||
&server_name,
|
room_id: room_id.clone().into(),
|
||||||
get_room_state::v1::Request {
|
event_id: first_pdu.event_id.clone().into(),
|
||||||
room_id: room_id.clone().into(),
|
})
|
||||||
event_id: first_pdu.event_id.clone().into(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for pdu in remote_state_response.pdus.clone() {
|
for pdu in remote_state_response.pdus.clone() {
|
||||||
|
@ -571,8 +617,8 @@ pub(super) async fn force_set_room_state_from_server(
|
||||||
.parse_incoming_pdu(&pdu)
|
.parse_incoming_pdu(&pdu)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(t) => t,
|
| Ok(t) => t,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
warn!("Could not parse PDU, ignoring: {e}");
|
warn!("Could not parse PDU, ignoring: {e}");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
@ -654,8 +700,8 @@ pub(super) async fn force_set_room_state_from_server(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Updating joined counts for room just in case (e.g. we may have found a difference in the room's \
|
"Updating joined counts for room just in case (e.g. we may have found a difference in \
|
||||||
m.room.member state"
|
the room's m.room.member state"
|
||||||
);
|
);
|
||||||
self.services
|
self.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -672,9 +718,13 @@ pub(super) async fn force_set_room_state_from_server(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_signing_keys(
|
pub(super) async fn get_signing_keys(
|
||||||
&self, server_name: Option<Box<ServerName>>, notary: Option<Box<ServerName>>, query: bool,
|
&self,
|
||||||
|
server_name: Option<Box<ServerName>>,
|
||||||
|
notary: Option<Box<ServerName>>,
|
||||||
|
query: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
let server_name =
|
||||||
|
server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
||||||
|
|
||||||
if let Some(notary) = notary {
|
if let Some(notary) = notary {
|
||||||
let signing_keys = self
|
let signing_keys = self
|
||||||
|
@ -706,8 +756,12 @@ pub(super) async fn get_signing_keys(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_verify_keys(&self, server_name: Option<Box<ServerName>>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn get_verify_keys(
|
||||||
let server_name = server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
&self,
|
||||||
|
server_name: Option<Box<ServerName>>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let server_name =
|
||||||
|
server_name.unwrap_or_else(|| self.services.server.config.server_name.clone().into());
|
||||||
|
|
||||||
let keys = self
|
let keys = self
|
||||||
.services
|
.services
|
||||||
|
@ -727,7 +781,9 @@ pub(super) async fn get_verify_keys(&self, server_name: Option<Box<ServerName>>)
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn resolve_true_destination(
|
pub(super) async fn resolve_true_destination(
|
||||||
&self, server_name: Box<ServerName>, no_cache: bool,
|
&self,
|
||||||
|
server_name: Box<ServerName>,
|
||||||
|
no_cache: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !self.services.globals.config.allow_federation {
|
if !self.services.globals.config.allow_federation {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -737,7 +793,8 @@ pub(super) async fn resolve_true_destination(
|
||||||
|
|
||||||
if server_name == self.services.globals.config.server_name {
|
if server_name == self.services.globals.config.server_name {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for \
|
||||||
|
fetching local PDUs.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,7 +903,9 @@ pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageE
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn database_stats(
|
pub(super) async fn database_stats(
|
||||||
&self, property: Option<String>, map: Option<String>,
|
&self,
|
||||||
|
property: Option<String>,
|
||||||
|
map: Option<String>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let property = property.unwrap_or_else(|| "rocksdb.stats".to_owned());
|
let property = property.unwrap_or_else(|| "rocksdb.stats".to_owned());
|
||||||
let map_name = map.as_ref().map_or(EMPTY, String::as_str);
|
let map_name = map.as_ref().map_or(EMPTY, String::as_str);
|
||||||
|
|
|
@ -2,7 +2,9 @@ use std::fmt::Write;
|
||||||
|
|
||||||
use conduwuit::Result;
|
use conduwuit::Result;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
|
use ruma::{
|
||||||
|
events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{admin_command, get_room_info};
|
use crate::{admin_command, get_room_info};
|
||||||
|
|
||||||
|
@ -38,7 +40,10 @@ pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn fetch_support_well_known(
|
||||||
|
&self,
|
||||||
|
server_name: Box<ServerName>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let response = self
|
let response = self
|
||||||
.services
|
.services
|
||||||
.client
|
.client
|
||||||
|
@ -60,16 +65,20 @@ pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>
|
||||||
}
|
}
|
||||||
|
|
||||||
let json: serde_json::Value = match serde_json::from_str(&text) {
|
let json: serde_json::Value = match serde_json::from_str(&text) {
|
||||||
Ok(json) => json,
|
| Ok(json) => json,
|
||||||
Err(_) => {
|
| Err(_) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Response text/body is not valid JSON."));
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"Response text/body is not valid JSON.",
|
||||||
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let pretty_json: String = match serde_json::to_string_pretty(&json) {
|
let pretty_json: String = match serde_json::to_string_pretty(&json) {
|
||||||
Ok(json) => json,
|
| Ok(json) => json,
|
||||||
Err(_) => {
|
| Err(_) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Response text/body is not valid JSON."));
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"Response text/body is not valid JSON.",
|
||||||
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,10 +88,14 @@ pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn remote_user_in_rooms(&self, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn remote_user_in_rooms(
|
||||||
|
&self,
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
if user_id.server_name() == self.services.globals.config.server_name {
|
if user_id.server_name() == self.services.globals.config.server_name {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
|
"User belongs to our server, please use `list-joined-rooms` user admin command \
|
||||||
|
instead.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use conduwuit::{debug, debug_info, debug_warn, error, info, trace, utils::time::parse_timepoint_ago, Result};
|
use conduwuit::{
|
||||||
|
debug, debug_info, debug_warn, error, info, trace, utils::time::parse_timepoint_ago, Result,
|
||||||
|
};
|
||||||
use conduwuit_service::media::Dim;
|
use conduwuit_service::media::Dim;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::room::message::RoomMessageEventContent, EventId, Mxc, MxcUri, OwnedMxcUri, OwnedServerName, ServerName,
|
events::room::message::RoomMessageEventContent, EventId, Mxc, MxcUri, OwnedMxcUri,
|
||||||
|
OwnedServerName, ServerName,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{admin_command, utils::parse_local_user_id};
|
use crate::{admin_command, utils::parse_local_user_id};
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete(
|
pub(super) async fn delete(
|
||||||
&self, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
&self,
|
||||||
|
mxc: Option<Box<MxcUri>>,
|
||||||
|
event_id: Option<Box<EventId>>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if event_id.is_some() && mxc.is_some() {
|
if event_id.is_some() && mxc.is_some() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -52,7 +57,10 @@ pub(super) async fn delete(
|
||||||
let final_url = url.to_string().replace('"', "");
|
let final_url = url.to_string().replace('"', "");
|
||||||
mxc_urls.push(final_url);
|
mxc_urls.push(final_url);
|
||||||
} else {
|
} else {
|
||||||
info!("Found a URL in the event ID {event_id} but did not start with mxc://, ignoring");
|
info!(
|
||||||
|
"Found a URL in the event ID {event_id} but did not start with \
|
||||||
|
mxc://, ignoring"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,17 +75,24 @@ pub(super) async fn delete(
|
||||||
debug!("Found a thumbnail_url in info key: {thumbnail_url}");
|
debug!("Found a thumbnail_url in info key: {thumbnail_url}");
|
||||||
|
|
||||||
if thumbnail_url.to_string().starts_with("\"mxc://") {
|
if thumbnail_url.to_string().starts_with("\"mxc://") {
|
||||||
debug!("Pushing thumbnail URL {thumbnail_url} to list of MXCs to delete");
|
debug!(
|
||||||
let final_thumbnail_url = thumbnail_url.to_string().replace('"', "");
|
"Pushing thumbnail URL {thumbnail_url} to list of MXCs \
|
||||||
|
to delete"
|
||||||
|
);
|
||||||
|
let final_thumbnail_url =
|
||||||
|
thumbnail_url.to_string().replace('"', "");
|
||||||
mxc_urls.push(final_thumbnail_url);
|
mxc_urls.push(final_thumbnail_url);
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
"Found a thumbnail URL in the event ID {event_id} but did not start with \
|
"Found a thumbnail URL in the event ID {event_id} but \
|
||||||
mxc://, ignoring"
|
did not start with mxc://, ignoring"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info!("No \"thumbnail_url\" key in \"info\" key, assuming no thumbnails.");
|
info!(
|
||||||
|
"No \"thumbnail_url\" key in \"info\" key, assuming no \
|
||||||
|
thumbnails."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,8 +113,8 @@ pub(super) async fn delete(
|
||||||
mxc_urls.push(final_url);
|
mxc_urls.push(final_url);
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
"Found a URL in the event ID {event_id} but did not start with mxc://, \
|
"Found a URL in the event ID {event_id} but did not \
|
||||||
ignoring"
|
start with mxc://, ignoring"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,13 +124,14 @@ pub(super) async fn delete(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Event ID does not have a \"content\" key or failed parsing the event ID JSON.",
|
"Event ID does not have a \"content\" key or failed parsing the event \
|
||||||
|
ID JSON.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Event ID does not have a \"content\" key, this is not a message or an event type that contains \
|
"Event ID does not have a \"content\" key, this is not a message or an \
|
||||||
media.",
|
event type that contains media.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,7 +142,9 @@ pub(super) async fn delete(
|
||||||
|
|
||||||
if mxc_urls.is_empty() {
|
if mxc_urls.is_empty() {
|
||||||
info!("Parsed event ID {event_id} but did not contain any MXC URLs.");
|
info!("Parsed event ID {event_id} but did not contain any MXC URLs.");
|
||||||
return Ok(RoomMessageEventContent::text_plain("Parsed event ID but found no MXC URLs."));
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"Parsed event ID but found no MXC URLs.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mxc_deletion_count: usize = 0;
|
let mut mxc_deletion_count: usize = 0;
|
||||||
|
@ -138,11 +156,11 @@ pub(super) async fn delete(
|
||||||
.delete(&mxc_url.as_str().try_into()?)
|
.delete(&mxc_url.as_str().try_into()?)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
debug_info!("Successfully deleted {mxc_url} from filesystem and database");
|
debug_info!("Successfully deleted {mxc_url} from filesystem and database");
|
||||||
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Failed to delete {mxc_url}, ignoring error and skipping: {e}");
|
debug_warn!("Failed to delete {mxc_url}, ignoring error and skipping: {e}");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
@ -150,19 +168,22 @@ pub(super) async fn delete(
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Deleted {mxc_deletion_count} total MXCs from our database and the filesystem from event ID {event_id}."
|
"Deleted {mxc_deletion_count} total MXCs from our database and the filesystem from \
|
||||||
|
event ID {event_id}."
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(
|
Ok(RoomMessageEventContent::text_plain(
|
||||||
"Please specify either an MXC using --mxc or an event ID using --event-id of the message containing an image. \
|
"Please specify either an MXC using --mxc or an event ID using --event-id of the \
|
||||||
See --help for details.",
|
message containing an image. See --help for details.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -192,11 +213,11 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
||||||
for mxc in &mxc_list {
|
for mxc in &mxc_list {
|
||||||
trace!(%failed_parsed_mxcs, %mxc_deletion_count, "Deleting MXC {mxc} in bulk");
|
trace!(%failed_parsed_mxcs, %mxc_deletion_count, "Deleting MXC {mxc} in bulk");
|
||||||
match self.services.media.delete(mxc).await {
|
match self.services.media.delete(mxc).await {
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
debug_info!("Successfully deleted {mxc} from filesystem and database");
|
debug_info!("Successfully deleted {mxc} from filesystem and database");
|
||||||
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
|
debug_warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
@ -204,14 +225,18 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database and the filesystem. \
|
"Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database \
|
||||||
{failed_parsed_mxcs} MXCs failed to be parsed from the database.",
|
and the filesystem. {failed_parsed_mxcs} MXCs failed to be parsed from the database.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete_past_remote_media(
|
pub(super) async fn delete_past_remote_media(
|
||||||
&self, duration: String, before: bool, after: bool, yes_i_want_to_delete_local_media: bool,
|
&self,
|
||||||
|
duration: String,
|
||||||
|
before: bool,
|
||||||
|
after: bool,
|
||||||
|
yes_i_want_to_delete_local_media: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if before && after {
|
if before && after {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -224,7 +249,12 @@ pub(super) async fn delete_past_remote_media(
|
||||||
let deleted_count = self
|
let deleted_count = self
|
||||||
.services
|
.services
|
||||||
.media
|
.media
|
||||||
.delete_all_remote_media_at_after_time(duration, before, after, yes_i_want_to_delete_local_media)
|
.delete_all_remote_media_at_after_time(
|
||||||
|
duration,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
yes_i_want_to_delete_local_media,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -233,7 +263,10 @@ pub(super) async fn delete_past_remote_media(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete_all_from_user(&self, username: String) -> Result<RoomMessageEventContent> {
|
pub(super) async fn delete_all_from_user(
|
||||||
|
&self,
|
||||||
|
username: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_local_user_id(self.services, &username)?;
|
let user_id = parse_local_user_id(self.services, &username)?;
|
||||||
|
|
||||||
let deleted_count = self.services.media.delete_from_user(&user_id).await?;
|
let deleted_count = self.services.media.delete_from_user(&user_id).await?;
|
||||||
|
@ -245,7 +278,9 @@ pub(super) async fn delete_all_from_user(&self, username: String) -> Result<Room
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete_all_from_server(
|
pub(super) async fn delete_all_from_server(
|
||||||
&self, server_name: Box<ServerName>, yes_i_want_to_delete_local_media: bool,
|
&self,
|
||||||
|
server_name: Box<ServerName>,
|
||||||
|
yes_i_want_to_delete_local_media: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if server_name == self.services.globals.server_name() && !yes_i_want_to_delete_local_media {
|
if server_name == self.services.globals.server_name() && !yes_i_want_to_delete_local_media {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
@ -260,20 +295,26 @@ pub(super) async fn delete_all_from_server(
|
||||||
.await
|
.await
|
||||||
.inspect_err(|e| error!("Failed to get MXC URIs from our database: {e}"))
|
.inspect_err(|e| error!("Failed to get MXC URIs from our database: {e}"))
|
||||||
else {
|
else {
|
||||||
return Ok(RoomMessageEventContent::text_plain("Failed to get MXC URIs from our database"));
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"Failed to get MXC URIs from our database",
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut deleted_count: usize = 0;
|
let mut deleted_count: usize = 0;
|
||||||
|
|
||||||
for mxc in all_mxcs {
|
for mxc in all_mxcs {
|
||||||
let Ok(mxc_server_name) = mxc.server_name().inspect_err(|e| {
|
let Ok(mxc_server_name) = mxc.server_name().inspect_err(|e| {
|
||||||
debug_warn!("Failed to parse MXC {mxc} server name from database, ignoring error and skipping: {e}");
|
debug_warn!(
|
||||||
|
"Failed to parse MXC {mxc} server name from database, ignoring error and \
|
||||||
|
skipping: {e}"
|
||||||
|
);
|
||||||
}) else {
|
}) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if mxc_server_name != server_name
|
if mxc_server_name != server_name
|
||||||
|| (self.services.globals.server_is_ours(mxc_server_name) && !yes_i_want_to_delete_local_media)
|
|| (self.services.globals.server_is_ours(mxc_server_name)
|
||||||
|
&& !yes_i_want_to_delete_local_media)
|
||||||
{
|
{
|
||||||
trace!("skipping MXC URI {mxc}");
|
trace!("skipping MXC URI {mxc}");
|
||||||
continue;
|
continue;
|
||||||
|
@ -282,10 +323,10 @@ pub(super) async fn delete_all_from_server(
|
||||||
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
||||||
|
|
||||||
match self.services.media.delete(&mxc).await {
|
match self.services.media.delete(&mxc).await {
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
deleted_count = deleted_count.saturating_add(1);
|
deleted_count = deleted_count.saturating_add(1);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
|
debug_warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
@ -307,7 +348,10 @@ pub(super) async fn get_file_info(&self, mxc: OwnedMxcUri) -> Result<RoomMessage
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_file(
|
pub(super) async fn get_remote_file(
|
||||||
&self, mxc: OwnedMxcUri, server: Option<OwnedServerName>, timeout: u32,
|
&self,
|
||||||
|
mxc: OwnedMxcUri,
|
||||||
|
server: Option<OwnedServerName>,
|
||||||
|
timeout: u32,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
||||||
let timeout = Duration::from_millis(timeout.into());
|
let timeout = Duration::from_millis(timeout.into());
|
||||||
|
@ -327,7 +371,12 @@ pub(super) async fn get_remote_file(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_remote_thumbnail(
|
pub(super) async fn get_remote_thumbnail(
|
||||||
&self, mxc: OwnedMxcUri, server: Option<OwnedServerName>, timeout: u32, width: u32, height: u32,
|
&self,
|
||||||
|
mxc: OwnedMxcUri,
|
||||||
|
server: Option<OwnedServerName>,
|
||||||
|
timeout: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
||||||
let timeout = Duration::from_millis(timeout.into());
|
let timeout = Duration::from_millis(timeout.into());
|
||||||
|
|
|
@ -53,8 +53,8 @@ async fn handle_command(services: Arc<Services>, command: CommandInput) -> Proce
|
||||||
|
|
||||||
async fn process_command(services: Arc<Services>, input: &CommandInput) -> ProcessorResult {
|
async fn process_command(services: Arc<Services>, input: &CommandInput) -> ProcessorResult {
|
||||||
let (command, args, body) = match parse(&services, input) {
|
let (command, args, body) = match parse(&services, input) {
|
||||||
Err(error) => return Err(error),
|
| Err(error) => return Err(error),
|
||||||
Ok(parsed) => parsed,
|
| Ok(parsed) => parsed,
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = Command {
|
let context = Command {
|
||||||
|
@ -68,7 +68,8 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
|
fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
|
||||||
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
let link =
|
||||||
|
"Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
||||||
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||||
let content = RoomMessageEventContent::notice_markdown(msg);
|
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||||
error!("Panic while processing command: {error:?}");
|
error!("Panic while processing command: {error:?}");
|
||||||
|
@ -76,7 +77,11 @@ fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and process a message from the admin room
|
// Parse and process a message from the admin room
|
||||||
async fn process(context: &Command<'_>, command: AdminCommand, args: &[String]) -> ProcessorResult {
|
async fn process(
|
||||||
|
context: &Command<'_>,
|
||||||
|
command: AdminCommand,
|
||||||
|
args: &[String],
|
||||||
|
) -> ProcessorResult {
|
||||||
let (capture, logs) = capture_create(context);
|
let (capture, logs) = capture_create(context);
|
||||||
|
|
||||||
let capture_scope = capture.start();
|
let capture_scope = capture.start();
|
||||||
|
@ -100,11 +105,12 @@ async fn process(context: &Command<'_>, command: AdminCommand, args: &[String])
|
||||||
drop(logs);
|
drop(logs);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(content) => {
|
| Ok(content) => {
|
||||||
write!(&mut output, "{0}", content.body()).expect("failed to format command result to output buffer");
|
write!(&mut output, "{0}", content.body())
|
||||||
|
.expect("failed to format command result to output buffer");
|
||||||
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id)))
|
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id)))
|
||||||
},
|
},
|
||||||
Err(error) => {
|
| Err(error) => {
|
||||||
write!(&mut output, "Command failed with error:\n```\n{error:#?}\n```")
|
write!(&mut output, "Command failed with error:\n```\n{error:#?}\n```")
|
||||||
.expect("failed to format command result to output");
|
.expect("failed to format command result to output");
|
||||||
Err(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id))
|
Err(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id))
|
||||||
|
@ -128,8 +134,9 @@ fn capture_create(context: &Command<'_>) -> (Arc<Capture>, Arc<Mutex<String>>) {
|
||||||
.and_then(LevelFilter::into_level)
|
.and_then(LevelFilter::into_level)
|
||||||
.unwrap_or(Level::DEBUG);
|
.unwrap_or(Level::DEBUG);
|
||||||
|
|
||||||
let filter =
|
let filter = move |data: capture::Data<'_>| {
|
||||||
move |data: capture::Data<'_>| data.level() <= log_level && data.our_modules() && data.scope.contains(&"admin");
|
data.level() <= log_level && data.our_modules() && data.scope.contains(&"admin")
|
||||||
|
};
|
||||||
|
|
||||||
let logs = Arc::new(Mutex::new(
|
let logs = Arc::new(Mutex::new(
|
||||||
collect_stream(|s| markdown_table_head(s)).expect("markdown table header"),
|
collect_stream(|s| markdown_table_head(s)).expect("markdown table header"),
|
||||||
|
@ -146,14 +153,15 @@ fn capture_create(context: &Command<'_>) -> (Arc<Capture>, Arc<Mutex<String>>) {
|
||||||
|
|
||||||
// Parse chat messages from the admin room into an AdminCommand object
|
// Parse chat messages from the admin room into an AdminCommand object
|
||||||
fn parse<'a>(
|
fn parse<'a>(
|
||||||
services: &Arc<Services>, input: &'a CommandInput,
|
services: &Arc<Services>,
|
||||||
|
input: &'a CommandInput,
|
||||||
) -> Result<(AdminCommand, Vec<String>, Vec<&'a str>), CommandOutput> {
|
) -> Result<(AdminCommand, Vec<String>, Vec<&'a str>), CommandOutput> {
|
||||||
let lines = input.command.lines().filter(|line| !line.trim().is_empty());
|
let lines = input.command.lines().filter(|line| !line.trim().is_empty());
|
||||||
let command_line = lines.clone().next().expect("command missing first line");
|
let command_line = lines.clone().next().expect("command missing first line");
|
||||||
let body = lines.skip(1).collect();
|
let body = lines.skip(1).collect();
|
||||||
match parse_command(command_line) {
|
match parse_command(command_line) {
|
||||||
Ok((command, args)) => Ok((command, args, body)),
|
| Ok((command, args)) => Ok((command, args, body)),
|
||||||
Err(error) => {
|
| Err(error) => {
|
||||||
let message = error
|
let message = error
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("server.name", services.globals.server_name().as_str());
|
.replace("server.name", services.globals.server_name().as_str());
|
||||||
|
@ -255,11 +263,12 @@ fn parse_line(command_line: &str) -> Vec<String> {
|
||||||
argv
|
argv
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply(mut content: RoomMessageEventContent, reply_id: Option<&EventId>) -> RoomMessageEventContent {
|
fn reply(
|
||||||
|
mut content: RoomMessageEventContent,
|
||||||
|
reply_id: Option<&EventId>,
|
||||||
|
) -> RoomMessageEventContent {
|
||||||
content.relates_to = reply_id.map(|event_id| Reply {
|
content.relates_to = reply_id.map(|event_id| Reply {
|
||||||
in_reply_to: InReplyTo {
|
in_reply_to: InReplyTo { event_id: event_id.to_owned() },
|
||||||
event_id: event_id.to_owned(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
content
|
content
|
||||||
|
|
|
@ -30,15 +30,14 @@ pub(crate) enum AccountDataCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||||
pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: AccountDataCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
AccountDataCommand::ChangesSince {
|
| AccountDataCommand::ChangesSince { user_id, since, room_id } => {
|
||||||
user_id,
|
|
||||||
since,
|
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.account_data
|
.account_data
|
||||||
|
@ -51,11 +50,7 @@ pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
AccountDataCommand::Get {
|
| AccountDataCommand::Get { user_id, kind, room_id } => {
|
||||||
user_id,
|
|
||||||
kind,
|
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services
|
let results = services
|
||||||
.account_data
|
.account_data
|
||||||
|
|
|
@ -18,13 +18,14 @@ pub(crate) enum AppserviceCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||||
pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: AppserviceCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
AppserviceCommand::GetRegistration {
|
| AppserviceCommand::GetRegistration { appservice_id } => {
|
||||||
appservice_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.appservice.get_registration(&appservice_id).await;
|
let results = services.appservice.get_registration(&appservice_id).await;
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
AppserviceCommand::All => {
|
| AppserviceCommand::All => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.appservice.all().await;
|
let results = services.appservice.all().await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
|
@ -21,11 +21,14 @@ pub(crate) enum GlobalsCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||||
pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: GlobalsCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
GlobalsCommand::DatabaseVersion => {
|
| GlobalsCommand::DatabaseVersion => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.globals.db.database_version().await;
|
let results = services.globals.db.database_version().await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -34,7 +37,7 @@ pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
GlobalsCommand::CurrentCount => {
|
| GlobalsCommand::CurrentCount => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.globals.db.current_count();
|
let results = services.globals.db.current_count();
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -43,7 +46,7 @@ pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
GlobalsCommand::LastCheckForUpdatesId => {
|
| GlobalsCommand::LastCheckForUpdatesId => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.updates.last_check_for_updates_id().await;
|
let results = services.updates.last_check_for_updates_id().await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -52,9 +55,7 @@ pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
GlobalsCommand::SigningKeysFor {
|
| GlobalsCommand::SigningKeysFor { origin } => {
|
||||||
origin,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.server_keys.verify_keys_for(&origin).await;
|
let results = services.server_keys.verify_keys_for(&origin).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
|
@ -14,8 +14,9 @@ use conduwuit::Result;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
||||||
presence::PresenceCommand, pusher::PusherCommand, resolver::ResolverCommand, room_alias::RoomAliasCommand,
|
presence::PresenceCommand, pusher::PusherCommand, resolver::ResolverCommand,
|
||||||
room_state_cache::RoomStateCacheCommand, sending::SendingCommand, users::UsersCommand,
|
room_alias::RoomAliasCommand, room_state_cache::RoomStateCacheCommand,
|
||||||
|
sending::SendingCommand, users::UsersCommand,
|
||||||
};
|
};
|
||||||
use crate::admin_command_dispatch;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,14 @@ pub(crate) enum PresenceCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/presence.rs
|
/// All the getters and iterators in key_value/presence.rs
|
||||||
pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: PresenceCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
PresenceCommand::GetPresence {
|
| PresenceCommand::GetPresence { user_id } => {
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.presence.db.get_presence(&user_id).await;
|
let results = services.presence.db.get_presence(&user_id).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -38,9 +39,7 @@ pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>)
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
PresenceCommand::PresenceSince {
|
| PresenceCommand::PresenceSince { since } => {
|
||||||
since,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<(_, _, _)> = services
|
let results: Vec<(_, _, _)> = services
|
||||||
.presence
|
.presence
|
||||||
|
|
|
@ -13,13 +13,14 @@ pub(crate) enum PusherCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(subcommand: PusherCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: PusherCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
PusherCommand::GetPushers {
|
| PusherCommand::GetPushers { user_id } => {
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.pusher.get_pushers(&user_id).await;
|
let results = services.pusher.get_pushers(&user_id).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
|
@ -22,20 +22,16 @@ pub(crate) enum ResolverCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
|
async fn destinations_cache(
|
||||||
|
&self,
|
||||||
|
server_name: Option<OwnedServerName>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
use service::resolver::cache::CachedDest;
|
use service::resolver::cache::CachedDest;
|
||||||
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
||||||
writeln!(out, "| ----------- | ----------- | -------- | ------- |")?;
|
writeln!(out, "| ----------- | ----------- | -------- | ------- |")?;
|
||||||
let row = |(
|
let row = |(name, &CachedDest { ref dest, ref host, expire })| {
|
||||||
name,
|
|
||||||
&CachedDest {
|
|
||||||
ref dest,
|
|
||||||
ref host,
|
|
||||||
expire,
|
|
||||||
},
|
|
||||||
)| {
|
|
||||||
let expire = time::format(expire, "%+");
|
let expire = time::format(expire, "%+");
|
||||||
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
||||||
};
|
};
|
||||||
|
@ -64,14 +60,7 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessa
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
||||||
writeln!(out, "| ----------- | --- | ----:| ------- |")?;
|
writeln!(out, "| ----------- | --- | ----:| ------- |")?;
|
||||||
let row = |(
|
let row = |(name, &CachedOverride { ref ips, port, expire })| {
|
||||||
name,
|
|
||||||
&CachedOverride {
|
|
||||||
ref ips,
|
|
||||||
port,
|
|
||||||
expire,
|
|
||||||
},
|
|
||||||
)| {
|
|
||||||
let expire = time::format(expire, "%+");
|
let expire = time::format(expire, "%+");
|
||||||
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,13 +24,14 @@ pub(crate) enum RoomAliasCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
||||||
pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: RoomAliasCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
RoomAliasCommand::ResolveLocalAlias {
|
| RoomAliasCommand::ResolveLocalAlias { alias } => {
|
||||||
alias,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.rooms.alias.resolve_local_alias(&alias).await;
|
let results = services.rooms.alias.resolve_local_alias(&alias).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -39,9 +40,7 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomAliasCommand::LocalAliasesForRoom {
|
| RoomAliasCommand::LocalAliasesForRoom { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let aliases: Vec<_> = services
|
let aliases: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -56,7 +55,7 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomAliasCommand::AllLocalAliases => {
|
| RoomAliasCommand::AllLocalAliases => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let aliases = services
|
let aliases = services
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -77,15 +77,13 @@ pub(crate) enum RoomStateCacheCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(
|
pub(super) async fn process(
|
||||||
subcommand: RoomStateCacheCommand, context: &Command<'_>,
|
subcommand: RoomStateCacheCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
RoomStateCacheCommand::ServerInRoom {
|
| RoomStateCacheCommand::ServerInRoom { server, room_id } => {
|
||||||
server,
|
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = services
|
let result = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -98,9 +96,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomServers {
|
| RoomStateCacheCommand::RoomServers { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -115,9 +111,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::ServerRooms {
|
| RoomStateCacheCommand::ServerRooms { server } => {
|
||||||
server,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -132,9 +126,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomMembers {
|
| RoomStateCacheCommand::RoomMembers { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -149,9 +141,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::LocalUsersInRoom {
|
| RoomStateCacheCommand::LocalUsersInRoom { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -166,9 +156,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::ActiveLocalUsersInRoom {
|
| RoomStateCacheCommand::ActiveLocalUsersInRoom { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -183,9 +171,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomJoinedCount {
|
| RoomStateCacheCommand::RoomJoinedCount { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.rooms.state_cache.room_joined_count(&room_id).await;
|
let results = services.rooms.state_cache.room_joined_count(&room_id).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -194,9 +180,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomInvitedCount {
|
| RoomStateCacheCommand::RoomInvitedCount { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -209,9 +193,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomUserOnceJoined {
|
| RoomStateCacheCommand::RoomUserOnceJoined { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -226,9 +208,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomMembersInvited {
|
| RoomStateCacheCommand::RoomMembersInvited { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -243,10 +223,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::GetInviteCount {
|
| RoomStateCacheCommand::GetInviteCount { room_id, user_id } => {
|
||||||
room_id,
|
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -259,10 +236,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::GetLeftCount {
|
| RoomStateCacheCommand::GetLeftCount { room_id, user_id } => {
|
||||||
room_id,
|
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -275,9 +249,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomsJoined {
|
| RoomStateCacheCommand::RoomsJoined { user_id } => {
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -292,9 +264,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomsInvited {
|
| RoomStateCacheCommand::RoomsInvited { user_id } => {
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -308,9 +278,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::RoomsLeft {
|
| RoomStateCacheCommand::RoomsLeft { user_id } => {
|
||||||
user_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results: Vec<_> = services
|
let results: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -324,10 +292,7 @@ pub(super) async fn process(
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
RoomStateCacheCommand::InviteState {
|
| RoomStateCacheCommand::InviteState { user_id, room_id } => {
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services
|
let results = services
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -62,11 +62,14 @@ pub(crate) enum SendingCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the getters and iterators in key_value/sending.rs
|
/// All the getters and iterators in key_value/sending.rs
|
||||||
pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
subcommand: SendingCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
SendingCommand::ActiveRequests => {
|
| SendingCommand::ActiveRequests => {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.sending.db.active_requests();
|
let results = services.sending.db.active_requests();
|
||||||
let active_requests = results.collect::<Vec<_>>().await;
|
let active_requests = results.collect::<Vec<_>>().await;
|
||||||
|
@ -76,25 +79,29 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
SendingCommand::QueuedRequests {
|
| SendingCommand::QueuedRequests {
|
||||||
appservice_id,
|
appservice_id,
|
||||||
server_name,
|
server_name,
|
||||||
user_id,
|
user_id,
|
||||||
push_key,
|
push_key,
|
||||||
} => {
|
} => {
|
||||||
if appservice_id.is_none() && server_name.is_none() && user_id.is_none() && push_key.is_none() {
|
if appservice_id.is_none()
|
||||||
|
&& server_name.is_none()
|
||||||
|
&& user_id.is_none()
|
||||||
|
&& push_key.is_none()
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. See \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
--help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = match (appservice_id, server_name, user_id, push_key) {
|
let results = match (appservice_id, server_name, user_id, push_key) {
|
||||||
(Some(appservice_id), None, None, None) => {
|
| (Some(appservice_id), None, None, None) => {
|
||||||
if appservice_id.is_empty() {
|
if appservice_id.is_empty() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
arguments. See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,15 +110,15 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Appservice(appservice_id))
|
.queued_requests(&Destination::Appservice(appservice_id))
|
||||||
},
|
},
|
||||||
(None, Some(server_name), None, None) => services
|
| (None, Some(server_name), None, None) => services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Normal(server_name.into())),
|
.queued_requests(&Destination::Normal(server_name.into())),
|
||||||
(None, None, Some(user_id), Some(push_key)) => {
|
| (None, None, Some(user_id), Some(push_key)) => {
|
||||||
if push_key.is_empty() {
|
if push_key.is_empty() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
arguments. See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +127,16 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
.db
|
.db
|
||||||
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
||||||
},
|
},
|
||||||
(Some(_), Some(_), Some(_), Some(_)) => {
|
| (Some(_), Some(_), Some(_), Some(_)) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
Not all of them See --help for more details.",
|
specified via arguments. Not all of them See --help for more details.",
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
_ => {
|
| _ => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -141,26 +148,30 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
SendingCommand::ActiveRequestsFor {
|
| SendingCommand::ActiveRequestsFor {
|
||||||
appservice_id,
|
appservice_id,
|
||||||
server_name,
|
server_name,
|
||||||
user_id,
|
user_id,
|
||||||
push_key,
|
push_key,
|
||||||
} => {
|
} => {
|
||||||
if appservice_id.is_none() && server_name.is_none() && user_id.is_none() && push_key.is_none() {
|
if appservice_id.is_none()
|
||||||
|
&& server_name.is_none()
|
||||||
|
&& user_id.is_none()
|
||||||
|
&& push_key.is_none()
|
||||||
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. See \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
--help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = match (appservice_id, server_name, user_id, push_key) {
|
let results = match (appservice_id, server_name, user_id, push_key) {
|
||||||
(Some(appservice_id), None, None, None) => {
|
| (Some(appservice_id), None, None, None) => {
|
||||||
if appservice_id.is_empty() {
|
if appservice_id.is_empty() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
arguments. See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,15 +180,15 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Appservice(appservice_id))
|
.active_requests_for(&Destination::Appservice(appservice_id))
|
||||||
},
|
},
|
||||||
(None, Some(server_name), None, None) => services
|
| (None, Some(server_name), None, None) => services
|
||||||
.sending
|
.sending
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Normal(server_name.into())),
|
.active_requests_for(&Destination::Normal(server_name.into())),
|
||||||
(None, None, Some(user_id), Some(push_key)) => {
|
| (None, None, Some(user_id), Some(push_key)) => {
|
||||||
if push_key.is_empty() {
|
if push_key.is_empty() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
arguments. See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,16 +197,16 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
.db
|
.db
|
||||||
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
||||||
},
|
},
|
||||||
(Some(_), Some(_), Some(_), Some(_)) => {
|
| (Some(_), Some(_), Some(_), Some(_)) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
Not all of them See --help for more details.",
|
specified via arguments. Not all of them See --help for more details.",
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
_ => {
|
| _ => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"An appservice ID, server name, or a user ID with push key must be specified via arguments. \
|
"An appservice ID, server name, or a user ID with push key must be \
|
||||||
See --help for more details.",
|
specified via arguments. See --help for more details.",
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -207,9 +218,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
SendingCommand::GetLatestEduCount {
|
| SendingCommand::GetLatestEduCount { server_name } => {
|
||||||
server_name,
|
|
||||||
} => {
|
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let results = services.sending.db.get_latest_educount(&server_name).await;
|
let results = services.sending.db.get_latest_educount(&server_name).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduwuit::Result;
|
use conduwuit::Result;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedDeviceId, OwnedRoomId, OwnedUserId};
|
use ruma::{
|
||||||
|
events::room::message::RoomMessageEventContent, OwnedDeviceId, OwnedRoomId, OwnedUserId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{admin_command, admin_command_dispatch};
|
use crate::{admin_command, admin_command_dispatch};
|
||||||
|
|
||||||
|
@ -91,7 +93,11 @@ pub(crate) enum UsersCommand {
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_backup_session(
|
async fn get_backup_session(
|
||||||
&self, user_id: OwnedUserId, version: String, room_id: OwnedRoomId, session_id: String,
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
version: String,
|
||||||
|
room_id: OwnedRoomId,
|
||||||
|
session_id: String,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
|
@ -108,7 +114,10 @@ async fn get_backup_session(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_room_backups(
|
async fn get_room_backups(
|
||||||
&self, user_id: OwnedUserId, version: String, room_id: OwnedRoomId,
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
version: String,
|
||||||
|
room_id: OwnedRoomId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
|
@ -124,7 +133,11 @@ async fn get_room_backups(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_all_backups(&self, user_id: OwnedUserId, version: String) -> Result<RoomMessageEventContent> {
|
async fn get_all_backups(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
version: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self.services.key_backups.get_all(&user_id, &version).await;
|
let result = self.services.key_backups.get_all(&user_id, &version).await;
|
||||||
let query_time = timer.elapsed();
|
let query_time = timer.elapsed();
|
||||||
|
@ -135,7 +148,11 @@ async fn get_all_backups(&self, user_id: OwnedUserId, version: String) -> Result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_backup_algorithm(&self, user_id: OwnedUserId, version: String) -> Result<RoomMessageEventContent> {
|
async fn get_backup_algorithm(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
version: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
.services
|
.services
|
||||||
|
@ -150,7 +167,10 @@ async fn get_backup_algorithm(&self, user_id: OwnedUserId, version: String) -> R
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_latest_backup_version(&self, user_id: OwnedUserId) -> Result<RoomMessageEventContent> {
|
async fn get_latest_backup_version(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
.services
|
.services
|
||||||
|
@ -244,7 +264,11 @@ async fn list_devices_metadata(&self, user_id: OwnedUserId) -> Result<RoomMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_device_metadata(&self, user_id: OwnedUserId, device_id: OwnedDeviceId) -> Result<RoomMessageEventContent> {
|
async fn get_device_metadata(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
device_id: OwnedDeviceId,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let device = self
|
let device = self
|
||||||
.services
|
.services
|
||||||
|
@ -270,7 +294,11 @@ async fn get_devices_version(&self, user_id: OwnedUserId) -> Result<RoomMessageE
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn count_one_time_keys(&self, user_id: OwnedUserId, device_id: OwnedDeviceId) -> Result<RoomMessageEventContent> {
|
async fn count_one_time_keys(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
device_id: OwnedDeviceId,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
.services
|
.services
|
||||||
|
@ -285,7 +313,11 @@ async fn count_one_time_keys(&self, user_id: OwnedUserId, device_id: OwnedDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_device_keys(&self, user_id: OwnedUserId, device_id: OwnedDeviceId) -> Result<RoomMessageEventContent> {
|
async fn get_device_keys(
|
||||||
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
device_id: OwnedDeviceId,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
.services
|
.services
|
||||||
|
@ -327,7 +359,9 @@ async fn get_master_key(&self, user_id: OwnedUserId) -> Result<RoomMessageEventC
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn get_to_device_events(
|
async fn get_to_device_events(
|
||||||
&self, user_id: OwnedUserId, device_id: OwnedDeviceId,
|
&self,
|
||||||
|
user_id: OwnedUserId,
|
||||||
|
device_id: OwnedDeviceId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let timer = tokio::time::Instant::now();
|
let timer = tokio::time::Instant::now();
|
||||||
let result = self
|
let result = self
|
||||||
|
|
|
@ -3,7 +3,10 @@ use std::fmt::Write;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduwuit::Result;
|
use conduwuit::Result;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId};
|
use ruma::{
|
||||||
|
events::room::message::RoomMessageEventContent, OwnedRoomAliasId, OwnedRoomId, RoomAliasId,
|
||||||
|
RoomId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{escape_html, Command};
|
use crate::{escape_html, Command};
|
||||||
|
|
||||||
|
@ -42,82 +45,92 @@ pub(crate) enum RoomAliasCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
command: RoomAliasCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
let server_user = &services.globals.server_user;
|
let server_user = &services.globals.server_user;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
RoomAliasCommand::Set {
|
| RoomAliasCommand::Set { ref room_alias_localpart, .. }
|
||||||
ref room_alias_localpart,
|
| RoomAliasCommand::Remove { ref room_alias_localpart }
|
||||||
..
|
| RoomAliasCommand::Which { ref room_alias_localpart } => {
|
||||||
}
|
let room_alias_str =
|
||||||
| RoomAliasCommand::Remove {
|
format!("#{}:{}", room_alias_localpart, services.globals.server_name());
|
||||||
ref room_alias_localpart,
|
|
||||||
}
|
|
||||||
| RoomAliasCommand::Which {
|
|
||||||
ref room_alias_localpart,
|
|
||||||
} => {
|
|
||||||
let room_alias_str = format!("#{}:{}", room_alias_localpart, services.globals.server_name());
|
|
||||||
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
|
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
|
||||||
Ok(alias) => alias,
|
| Ok(alias) => alias,
|
||||||
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {err}"))),
|
| Err(err) =>
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to parse alias: {err}"
|
||||||
|
))),
|
||||||
};
|
};
|
||||||
match command {
|
match command {
|
||||||
RoomAliasCommand::Set {
|
| RoomAliasCommand::Set { force, room_id, .. } =>
|
||||||
force,
|
match (force, services.rooms.alias.resolve_local_alias(&room_alias).await) {
|
||||||
room_id,
|
| (true, Ok(id)) => {
|
||||||
..
|
match services.rooms.alias.set_alias(
|
||||||
} => match (force, services.rooms.alias.resolve_local_alias(&room_alias).await) {
|
&room_alias,
|
||||||
(true, Ok(id)) => match services
|
&room_id,
|
||||||
.rooms
|
server_user,
|
||||||
.alias
|
) {
|
||||||
.set_alias(&room_alias, &room_id, server_user)
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
{
|
"Successfully overwrote alias (formerly {id})"
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
))),
|
||||||
"Successfully overwrote alias (formerly {id})"
|
| Err(err) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to remove alias: {err}"
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| (false, Ok(id)) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Refusing to overwrite in use alias for {id}, use -f or --force to \
|
||||||
|
overwrite"
|
||||||
))),
|
))),
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Failed to remove alias: {err}"))),
|
| (_, Err(_)) => {
|
||||||
|
match services.rooms.alias.set_alias(
|
||||||
|
&room_alias,
|
||||||
|
&room_id,
|
||||||
|
server_user,
|
||||||
|
) {
|
||||||
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"Successfully set alias",
|
||||||
|
)),
|
||||||
|
| Err(err) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to remove alias: {err}"
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
(false, Ok(id)) => Ok(RoomMessageEventContent::text_plain(format!(
|
| RoomAliasCommand::Remove { .. } =>
|
||||||
"Refusing to overwrite in use alias for {id}, use -f or --force to overwrite"
|
match services.rooms.alias.resolve_local_alias(&room_alias).await {
|
||||||
))),
|
| Ok(id) => match services
|
||||||
(_, Err(_)) => match services
|
.rooms
|
||||||
.rooms
|
.alias
|
||||||
.alias
|
.remove_alias(&room_alias, server_user)
|
||||||
.set_alias(&room_alias, &room_id, server_user)
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Successfully set alias")),
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Failed to remove alias: {err}"))),
|
"Removed alias from {id}"
|
||||||
|
))),
|
||||||
|
| Err(err) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to remove alias: {err}"
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
| Err(_) =>
|
||||||
|
Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
||||||
},
|
},
|
||||||
},
|
| RoomAliasCommand::Which { .. } =>
|
||||||
RoomAliasCommand::Remove {
|
match services.rooms.alias.resolve_local_alias(&room_alias).await {
|
||||||
..
|
| Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
} => match services.rooms.alias.resolve_local_alias(&room_alias).await {
|
"Alias resolves to {id}"
|
||||||
Ok(id) => match services
|
))),
|
||||||
.rooms
|
| Err(_) =>
|
||||||
.alias
|
Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
||||||
.remove_alias(&room_alias, server_user)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain(format!("Removed alias from {id}"))),
|
|
||||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Failed to remove alias: {err}"))),
|
|
||||||
},
|
},
|
||||||
Err(_) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
| RoomAliasCommand::List { .. } => unreachable!(),
|
||||||
},
|
|
||||||
RoomAliasCommand::Which {
|
|
||||||
..
|
|
||||||
} => match services.rooms.alias.resolve_local_alias(&room_alias).await {
|
|
||||||
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!("Alias resolves to {id}"))),
|
|
||||||
Err(_) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
|
||||||
},
|
|
||||||
RoomAliasCommand::List {
|
|
||||||
..
|
|
||||||
} => unreachable!(),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RoomAliasCommand::List {
|
| RoomAliasCommand::List { room_id } =>
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
if let Some(room_id) = room_id {
|
if let Some(room_id) = room_id {
|
||||||
let aliases: Vec<OwnedRoomAliasId> = services
|
let aliases: Vec<OwnedRoomAliasId> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -128,7 +141,8 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let plain_list = aliases.iter().fold(String::new(), |mut output, alias| {
|
let plain_list = aliases.iter().fold(String::new(), |mut output, alias| {
|
||||||
writeln!(output, "- {alias}").expect("should be able to write to string buffer");
|
writeln!(output, "- {alias}")
|
||||||
|
.expect("should be able to write to string buffer");
|
||||||
output
|
output
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -176,7 +190,6 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||||
let plain = format!("Aliases:\n{plain_list}");
|
let plain = format!("Aliases:\n{plain_list}");
|
||||||
let html = format!("Aliases:\n<ul>{html_list}</ul>");
|
let html = format!("Aliases:\n<ul>{html_list}</ul>");
|
||||||
Ok(RoomMessageEventContent::text_html(plain, html))
|
Ok(RoomMessageEventContent::text_html(plain, html))
|
||||||
}
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ use crate::{admin_command, get_room_info, PAGE_SIZE};
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn list_rooms(
|
pub(super) async fn list_rooms(
|
||||||
&self, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool, no_details: bool,
|
&self,
|
||||||
|
page: Option<usize>,
|
||||||
|
exclude_disabled: bool,
|
||||||
|
exclude_banned: bool,
|
||||||
|
no_details: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||||
let page = page.unwrap_or(1);
|
let page = page.unwrap_or(1);
|
||||||
|
@ -16,10 +20,12 @@ pub(super) async fn list_rooms(
|
||||||
.metadata
|
.metadata
|
||||||
.iter_ids()
|
.iter_ids()
|
||||||
.filter_map(|room_id| async move {
|
.filter_map(|room_id| async move {
|
||||||
(!exclude_disabled || !self.services.rooms.metadata.is_disabled(room_id).await).then_some(room_id)
|
(!exclude_disabled || !self.services.rooms.metadata.is_disabled(room_id).await)
|
||||||
|
.then_some(room_id)
|
||||||
})
|
})
|
||||||
.filter_map(|room_id| async move {
|
.filter_map(|room_id| async move {
|
||||||
(!exclude_banned || !self.services.rooms.metadata.is_banned(room_id).await).then_some(room_id)
|
(!exclude_banned || !self.services.rooms.metadata.is_banned(room_id).await)
|
||||||
|
.then_some(room_id)
|
||||||
})
|
})
|
||||||
.then(|room_id| get_room_info(self.services, room_id))
|
.then(|room_id| get_room_info(self.services, room_id))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|
|
@ -25,24 +25,21 @@ pub(crate) enum RoomDirectoryCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn process(
|
||||||
|
command: RoomDirectoryCommand,
|
||||||
|
context: &Command<'_>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let services = context.services;
|
let services = context.services;
|
||||||
match command {
|
match command {
|
||||||
RoomDirectoryCommand::Publish {
|
| RoomDirectoryCommand::Publish { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
services.rooms.directory.set_public(&room_id);
|
services.rooms.directory.set_public(&room_id);
|
||||||
Ok(RoomMessageEventContent::notice_plain("Room published"))
|
Ok(RoomMessageEventContent::notice_plain("Room published"))
|
||||||
},
|
},
|
||||||
RoomDirectoryCommand::Unpublish {
|
| RoomDirectoryCommand::Unpublish { room_id } => {
|
||||||
room_id,
|
|
||||||
} => {
|
|
||||||
services.rooms.directory.set_not_public(&room_id);
|
services.rooms.directory.set_not_public(&room_id);
|
||||||
Ok(RoomMessageEventContent::notice_plain("Room unpublished"))
|
Ok(RoomMessageEventContent::notice_plain("Room unpublished"))
|
||||||
},
|
},
|
||||||
RoomDirectoryCommand::List {
|
| RoomDirectoryCommand::List { page } => {
|
||||||
page,
|
|
||||||
} => {
|
|
||||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||||
let page = page.unwrap_or(1);
|
let page = page.unwrap_or(1);
|
||||||
let mut rooms: Vec<_> = services
|
let mut rooms: Vec<_> = services
|
||||||
|
@ -70,7 +67,9 @@ pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>
|
||||||
"Rooms (page {page}):\n```\n{}\n```",
|
"Rooms (page {page}):\n```\n{}\n```",
|
||||||
rooms
|
rooms
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, members, name)| format!("{id} | Members: {members} | Name: {name}"))
|
.map(|(id, members, name)| format!(
|
||||||
|
"{id} | Members: {members} | Name: {name}"
|
||||||
|
))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,7 +27,11 @@ pub(crate) enum RoomInfoCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn list_joined_members(&self, room_id: Box<RoomId>, local_only: bool) -> Result<RoomMessageEventContent> {
|
async fn list_joined_members(
|
||||||
|
&self,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
local_only: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let room_name = self
|
let room_name = self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -9,7 +9,8 @@ use conduwuit::Result;
|
||||||
use ruma::OwnedRoomId;
|
use ruma::OwnedRoomId;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand, moderation::RoomModerationCommand,
|
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand,
|
||||||
|
moderation::RoomModerationCommand,
|
||||||
};
|
};
|
||||||
use crate::admin_command_dispatch;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@ use conduwuit::{
|
||||||
warn, Result,
|
warn, Result,
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId, RoomOrAliasId};
|
use ruma::{
|
||||||
|
events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId,
|
||||||
|
RoomOrAliasId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{admin_command, admin_command_dispatch, get_room_info};
|
use crate::{admin_command, admin_command_dispatch, get_room_info};
|
||||||
|
|
||||||
|
@ -75,7 +78,10 @@ pub(crate) enum RoomModerationCommand {
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn ban_room(
|
async fn ban_room(
|
||||||
&self, force: bool, disable_federation: bool, room: Box<RoomOrAliasId>,
|
&self,
|
||||||
|
force: bool,
|
||||||
|
disable_federation: bool,
|
||||||
|
room: Box<RoomOrAliasId>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
debug!("Got room alias or ID: {}", room);
|
debug!("Got room alias or ID: {}", room);
|
||||||
|
|
||||||
|
@ -89,13 +95,13 @@ async fn ban_room(
|
||||||
|
|
||||||
let room_id = if room.is_room_id() {
|
let room_id = if room.is_room_id() {
|
||||||
let room_id = match RoomId::parse(&room) {
|
let room_id = match RoomId::parse(&room) {
|
||||||
Ok(room_id) => room_id,
|
| Ok(room_id) => room_id,
|
||||||
Err(e) => {
|
| Err(e) =>
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to parse room ID {room}. Please note that this requires a full room ID \
|
"Failed to parse room ID {room}. Please note that this requires a full room \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`): {e}"
|
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
)))
|
(`#roomalias:example.com`): {e}"
|
||||||
},
|
))),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Room specified is a room ID, banning room ID");
|
debug!("Room specified is a room ID, banning room ID");
|
||||||
|
@ -105,18 +111,18 @@ async fn ban_room(
|
||||||
room_id
|
room_id
|
||||||
} else if room.is_room_alias_id() {
|
} else if room.is_room_alias_id() {
|
||||||
let room_alias = match RoomAliasId::parse(&room) {
|
let room_alias = match RoomAliasId::parse(&room) {
|
||||||
Ok(room_alias) => room_alias,
|
| Ok(room_alias) => room_alias,
|
||||||
Err(e) => {
|
| Err(e) =>
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to parse room ID {room}. Please note that this requires a full room ID \
|
"Failed to parse room ID {room}. Please note that this requires a full room \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`): {e}"
|
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
)))
|
(`#roomalias:example.com`): {e}"
|
||||||
},
|
))),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Room specified is not a room ID, attempting to resolve room alias to a room ID locally, if not using \
|
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
|
||||||
get_alias_helper to fetch room ID remotely"
|
locally, if not using get_alias_helper to fetch room ID remotely"
|
||||||
);
|
);
|
||||||
|
|
||||||
let room_id = if let Ok(room_id) = self
|
let room_id = if let Ok(room_id) = self
|
||||||
|
@ -128,7 +134,10 @@ async fn ban_room(
|
||||||
{
|
{
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
debug!(
|
||||||
|
"We don't have this room alias to a room ID locally, attempting to fetch room \
|
||||||
|
ID over federation"
|
||||||
|
);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
|
@ -137,11 +146,15 @@ async fn ban_room(
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
debug!(?room_id, ?servers, "Got federation response fetching room ID for {room}");
|
debug!(
|
||||||
|
?room_id,
|
||||||
|
?servers,
|
||||||
|
"Got federation response fetching room ID for {room}"
|
||||||
|
);
|
||||||
room_id
|
room_id
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::notice_plain(format!(
|
return Ok(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Failed to resolve room alias {room} to a room ID: {e}"
|
"Failed to resolve room alias {room} to a room ID: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -154,8 +167,9 @@ async fn ban_room(
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room specified is not a room ID or room alias. Please note that this requires a full room ID \
|
"Room specified is not a room ID or room alias. Please note that this requires a \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`)",
|
full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
|
(`#roomalias:example.com`)",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,8 +185,8 @@ async fn ban_room(
|
||||||
|
|
||||||
while let Some(local_user) = users.next().await {
|
while let Some(local_user) = users.next().await {
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting leave for user {local_user} in room {room_id} (forced, ignoring all errors, evicting \
|
"Attempting leave for user {local_user} in room {room_id} (forced, ignoring all \
|
||||||
admins too)",
|
errors, evicting admins too)",
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
||||||
|
@ -196,12 +210,14 @@ async fn ban_room(
|
||||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||||
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
||||||
error!(
|
error!(
|
||||||
"Error attempting to make local user {} leave room {} during room banning: {}",
|
"Error attempting to make local user {} leave room {} during room banning: \
|
||||||
|
{}",
|
||||||
&local_user, &room_id, e
|
&local_user, &room_id, e
|
||||||
);
|
);
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Error attempting to make local user {} leave room {} during room banning (room is still banned \
|
"Error attempting to make local user {} leave room {} during room banning \
|
||||||
but not removing any more users): {}\nIf you would like to ignore errors, use --force",
|
(room is still banned but not removing any more users): {}\nIf you would \
|
||||||
|
like to ignore errors, use --force",
|
||||||
&local_user, &room_id, e
|
&local_user, &room_id, e
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -232,19 +248,26 @@ async fn ban_room(
|
||||||
if disable_federation {
|
if disable_federation {
|
||||||
self.services.rooms.metadata.disable_room(&room_id, true);
|
self.services.rooms.metadata.disable_room(&room_id, true);
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room banned, removed all our local users, and disabled incoming federation with room.",
|
"Room banned, removed all our local users, and disabled incoming federation with \
|
||||||
|
room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(
|
Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room banned and removed all our local users, use `!admin federation disable-room` to stop receiving new \
|
"Room banned and removed all our local users, use `!admin federation disable-room` to \
|
||||||
inbound federation events as well if needed.",
|
stop receiving new inbound federation events as well if needed.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
|
async fn ban_list_of_rooms(
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
&self,
|
||||||
|
force: bool,
|
||||||
|
disable_federation: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -264,9 +287,10 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
for &room in &rooms_s {
|
for &room in &rooms_s {
|
||||||
match <&RoomOrAliasId>::try_from(room) {
|
match <&RoomOrAliasId>::try_from(room) {
|
||||||
Ok(room_alias_or_id) => {
|
| Ok(room_alias_or_id) => {
|
||||||
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
|
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
|
||||||
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias) {
|
if room.to_owned().eq(&admin_room_id) || room.to_owned().eq(admin_room_alias)
|
||||||
|
{
|
||||||
info!("User specified admin room in bulk ban list, ignoring");
|
info!("User specified admin room in bulk ban list, ignoring");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -274,19 +298,20 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
if room_alias_or_id.is_room_id() {
|
if room_alias_or_id.is_room_id() {
|
||||||
let room_id = match RoomId::parse(room_alias_or_id) {
|
let room_id = match RoomId::parse(room_alias_or_id) {
|
||||||
Ok(room_id) => room_id,
|
| Ok(room_id) => room_id,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
if force {
|
if force {
|
||||||
// ignore rooms we failed to parse if we're force banning
|
// ignore rooms we failed to parse if we're force banning
|
||||||
warn!(
|
warn!(
|
||||||
"Error parsing room \"{room}\" during bulk room banning, ignoring error and \
|
"Error parsing room \"{room}\" during bulk room banning, \
|
||||||
logging here: {e}"
|
ignoring error and logging here: {e}"
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"{room} is not a valid room ID or room alias, please fix the list and try again: {e}"
|
"{room} is not a valid room ID or room alias, please fix the \
|
||||||
|
list and try again: {e}"
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -296,7 +321,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
if room_alias_or_id.is_room_alias_id() {
|
if room_alias_or_id.is_room_alias_id() {
|
||||||
match RoomAliasId::parse(room_alias_or_id) {
|
match RoomAliasId::parse(room_alias_or_id) {
|
||||||
Ok(room_alias) => {
|
| Ok(room_alias) => {
|
||||||
let room_id = if let Ok(room_id) = self
|
let room_id = if let Ok(room_id) = self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -307,8 +332,8 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"We don't have this room alias to a room ID locally, attempting to fetch room ID \
|
"We don't have this room alias to a room ID locally, \
|
||||||
over federation"
|
attempting to fetch room ID over federation"
|
||||||
);
|
);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
|
@ -318,7 +343,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
debug!(
|
debug!(
|
||||||
?room_id,
|
?room_id,
|
||||||
?servers,
|
?servers,
|
||||||
|
@ -326,15 +351,19 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
);
|
);
|
||||||
room_id
|
room_id
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
// don't fail if force blocking
|
// don't fail if force blocking
|
||||||
if force {
|
if force {
|
||||||
warn!("Failed to resolve room alias {room} to a room ID: {e}");
|
warn!(
|
||||||
|
"Failed to resolve room alias {room} to a room \
|
||||||
|
ID: {e}"
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to resolve room alias {room} to a room ID: {e}"
|
"Failed to resolve room alias {room} to a room ID: \
|
||||||
|
{e}"
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -342,34 +371,37 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
room_ids.push(room_id);
|
room_ids.push(room_id);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
if force {
|
if force {
|
||||||
// ignore rooms we failed to parse if we're force deleting
|
// ignore rooms we failed to parse if we're force deleting
|
||||||
error!(
|
error!(
|
||||||
"Error parsing room \"{room}\" during bulk room banning, ignoring error and \
|
"Error parsing room \"{room}\" during bulk room banning, \
|
||||||
logging here: {e}"
|
ignoring error and logging here: {e}"
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"{room} is not a valid room ID or room alias, please fix the list and try again: {e}"
|
"{room} is not a valid room ID or room alias, please fix the \
|
||||||
|
list and try again: {e}"
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
if force {
|
if force {
|
||||||
// ignore rooms we failed to parse if we're force deleting
|
// ignore rooms we failed to parse if we're force deleting
|
||||||
error!(
|
error!(
|
||||||
"Error parsing room \"{room}\" during bulk room banning, ignoring error and logging here: {e}"
|
"Error parsing room \"{room}\" during bulk room banning, ignoring error \
|
||||||
|
and logging here: {e}"
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"{room} is not a valid room ID or room alias, please fix the list and try again: {e}"
|
"{room} is not a valid room ID or room alias, please fix the list and try \
|
||||||
|
again: {e}"
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -393,8 +425,8 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
while let Some(local_user) = users.next().await {
|
while let Some(local_user) = users.next().await {
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting leave for user {local_user} in room {room_id} (forced, ignoring all errors, evicting \
|
"Attempting leave for user {local_user} in room {room_id} (forced, ignoring \
|
||||||
admins too)",
|
all errors, evicting admins too)",
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
||||||
|
@ -418,14 +450,15 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
debug!("Attempting leave for user {local_user} in room {room_id}");
|
debug!("Attempting leave for user {local_user} in room {room_id}");
|
||||||
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
if let Err(e) = leave_room(self.services, local_user, &room_id, None).await {
|
||||||
error!(
|
error!(
|
||||||
"Error attempting to make local user {local_user} leave room {room_id} during bulk room \
|
"Error attempting to make local user {local_user} leave room {room_id} \
|
||||||
banning: {e}",
|
during bulk room banning: {e}",
|
||||||
);
|
);
|
||||||
|
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Error attempting to make local user {} leave room {} during room banning (room is still \
|
"Error attempting to make local user {} leave room {} during room \
|
||||||
banned but not removing any more users and not banning any more rooms): {}\nIf you would \
|
banning (room is still banned but not removing any more users and not \
|
||||||
like to ignore errors, use --force",
|
banning any more rooms): {}\nIf you would like to ignore errors, use \
|
||||||
|
--force",
|
||||||
&local_user, &room_id, e
|
&local_user, &room_id, e
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -458,8 +491,8 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
|
|
||||||
if disable_federation {
|
if disable_federation {
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, and disabled incoming \
|
"Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, \
|
||||||
federation with the room."
|
and disabled incoming federation with the room."
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -469,16 +502,20 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) -> Result<RoomMessageEventContent> {
|
async fn unban_room(
|
||||||
|
&self,
|
||||||
|
enable_federation: bool,
|
||||||
|
room: Box<RoomOrAliasId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let room_id = if room.is_room_id() {
|
let room_id = if room.is_room_id() {
|
||||||
let room_id = match RoomId::parse(&room) {
|
let room_id = match RoomId::parse(&room) {
|
||||||
Ok(room_id) => room_id,
|
| Ok(room_id) => room_id,
|
||||||
Err(e) => {
|
| Err(e) =>
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to parse room ID {room}. Please note that this requires a full room ID \
|
"Failed to parse room ID {room}. Please note that this requires a full room \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`): {e}"
|
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
)))
|
(`#roomalias:example.com`): {e}"
|
||||||
},
|
))),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Room specified is a room ID, unbanning room ID");
|
debug!("Room specified is a room ID, unbanning room ID");
|
||||||
|
@ -488,18 +525,18 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||||
room_id
|
room_id
|
||||||
} else if room.is_room_alias_id() {
|
} else if room.is_room_alias_id() {
|
||||||
let room_alias = match RoomAliasId::parse(&room) {
|
let room_alias = match RoomAliasId::parse(&room) {
|
||||||
Ok(room_alias) => room_alias,
|
| Ok(room_alias) => room_alias,
|
||||||
Err(e) => {
|
| Err(e) =>
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to parse room ID {room}. Please note that this requires a full room ID \
|
"Failed to parse room ID {room}. Please note that this requires a full room \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`): {e}"
|
ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
)))
|
(`#roomalias:example.com`): {e}"
|
||||||
},
|
))),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Room specified is not a room ID, attempting to resolve room alias to a room ID locally, if not using \
|
"Room specified is not a room ID, attempting to resolve room alias to a room ID \
|
||||||
get_alias_helper to fetch room ID remotely"
|
locally, if not using get_alias_helper to fetch room ID remotely"
|
||||||
);
|
);
|
||||||
|
|
||||||
let room_id = if let Ok(room_id) = self
|
let room_id = if let Ok(room_id) = self
|
||||||
|
@ -511,7 +548,10 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||||
{
|
{
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
debug!(
|
||||||
|
"We don't have this room alias to a room ID locally, attempting to fetch room \
|
||||||
|
ID over federation"
|
||||||
|
);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
|
@ -520,11 +560,15 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||||
.resolve_alias(&room_alias, None)
|
.resolve_alias(&room_alias, None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
debug!(?room_id, ?servers, "Got federation response fetching room ID for room {room}");
|
debug!(
|
||||||
|
?room_id,
|
||||||
|
?servers,
|
||||||
|
"Got federation response fetching room ID for room {room}"
|
||||||
|
);
|
||||||
room_id
|
room_id
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to resolve room alias {room} to a room ID: {e}"
|
"Failed to resolve room alias {room} to a room ID: {e}"
|
||||||
)));
|
)));
|
||||||
|
@ -537,8 +581,9 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||||
room_id
|
room_id
|
||||||
} else {
|
} else {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room specified is not a room ID or room alias. Please note that this requires a full room ID \
|
"Room specified is not a room ID or room alias. Please note that this requires a \
|
||||||
(`!awIh6gGInaS5wLQJwa:example.com`) or a room alias (`#roomalias:example.com`)",
|
full room ID (`!awIh6gGInaS5wLQJwa:example.com`) or a room alias \
|
||||||
|
(`#roomalias:example.com`)",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -548,8 +593,8 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(
|
Ok(RoomMessageEventContent::text_plain(
|
||||||
"Room unbanned, you may need to re-enable federation with the room using enable-room if this is a remote room \
|
"Room unbanned, you may need to re-enable federation with the room using enable-room if \
|
||||||
to make it fully functional.",
|
this is a remote room to make it fully functional.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,12 @@ pub(super) async fn show_config(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn list_features(
|
pub(super) async fn list_features(
|
||||||
&self, available: bool, enabled: bool, comma: bool,
|
&self,
|
||||||
|
available: bool,
|
||||||
|
enabled: bool,
|
||||||
|
comma: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let delim = if comma {
|
let delim = if comma { "," } else { " " };
|
||||||
","
|
|
||||||
} else {
|
|
||||||
" "
|
|
||||||
};
|
|
||||||
if enabled && !available {
|
if enabled && !available {
|
||||||
let features = info::rustc::features().join(delim);
|
let features = info::rustc::features().join(delim);
|
||||||
let out = format!("`\n{features}\n`");
|
let out = format!("`\n{features}\n`");
|
||||||
|
@ -53,16 +52,8 @@ pub(super) async fn list_features(
|
||||||
let available = info::cargo::features();
|
let available = info::cargo::features();
|
||||||
for feature in available {
|
for feature in available {
|
||||||
let active = enabled.contains(&feature.as_str());
|
let active = enabled.contains(&feature.as_str());
|
||||||
let emoji = if active {
|
let emoji = if active { "✅" } else { "❌" };
|
||||||
"✅"
|
let remark = if active { "[enabled]" } else { "" };
|
||||||
} else {
|
|
||||||
"❌"
|
|
||||||
};
|
|
||||||
let remark = if active {
|
|
||||||
"[enabled]"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
writeln!(features, "{emoji} {feature} {remark}")?;
|
writeln!(features, "{emoji} {feature} {remark}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +64,8 @@ pub(super) async fn list_features(
|
||||||
pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
|
pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
|
||||||
let services_usage = self.services.memory_usage().await?;
|
let services_usage = self.services.memory_usage().await?;
|
||||||
let database_usage = self.services.db.db.memory_usage()?;
|
let database_usage = self.services.db.db.memory_usage()?;
|
||||||
let allocator_usage = conduwuit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
let allocator_usage =
|
||||||
|
conduwuit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Services:\n{services_usage}\nDatabase:\n{database_usage}{allocator_usage}",
|
"Services:\n{services_usage}\nDatabase:\n{database_usage}{allocator_usage}",
|
||||||
|
@ -106,8 +98,8 @@ pub(super) async fn backup_database(&self) -> Result<RoomMessageEventContent> {
|
||||||
.server
|
.server
|
||||||
.runtime()
|
.runtime()
|
||||||
.spawn_blocking(move || match globals.db.backup() {
|
.spawn_blocking(move || match globals.db.backup() {
|
||||||
Ok(()) => String::new(),
|
| Ok(()) => String::new(),
|
||||||
Err(e) => e.to_string(),
|
| Err(e) => e.to_string(),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -147,8 +139,8 @@ pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventConten
|
||||||
|
|
||||||
if !force && current_exe_deleted() {
|
if !force && current_exe_deleted() {
|
||||||
return Err!(
|
return Err!(
|
||||||
"The server cannot be restarted because the executable changed. If this is expected use --force to \
|
"The server cannot be restarted because the executable changed. If this is expected \
|
||||||
override."
|
use --force to override."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,12 +47,18 @@ pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn create_user(
|
||||||
|
&self,
|
||||||
|
username: String,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(self.services, &username)?;
|
let user_id = parse_local_user_id(self.services, &username)?;
|
||||||
|
|
||||||
if self.services.users.exists(&user_id).await {
|
if self.services.users.exists(&user_id).await {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!("Userid {user_id} already exists")));
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Userid {user_id} already exists"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_id.is_historical() {
|
if user_id.is_historical() {
|
||||||
|
@ -120,7 +126,9 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
warn!(
|
||||||
|
"Skipping room {room} to automatically join as we have never joined before."
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,25 +138,31 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||||
&user_id,
|
&user_id,
|
||||||
&room_id,
|
&room_id,
|
||||||
Some("Automatically joining this room upon registration".to_owned()),
|
Some("Automatically joining this room upon registration".to_owned()),
|
||||||
&[self.services.globals.server_name().to_owned(), room_server_name.to_owned()],
|
&[
|
||||||
|
self.services.globals.server_name().to_owned(),
|
||||||
|
room_server_name.to_owned(),
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_response) => {
|
| Ok(_response) => {
|
||||||
info!("Automatically joined room {room} for user {user_id}");
|
info!("Automatically joined room {room} for user {user_id}");
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
self.services
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
"Failed to automatically join room {room} for user {user_id}: \
|
||||||
|
{e}"
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
// don't return this error so we don't fail registrations
|
// don't return this error so we don't fail registrations
|
||||||
error!("Failed to automatically join room {room} for user {user_id}: {e}");
|
error!(
|
||||||
|
"Failed to automatically join room {room} for user {user_id}: {e}"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -181,7 +195,11 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) -> Result<RoomMessageEventContent> {
|
pub(super) async fn deactivate(
|
||||||
|
&self,
|
||||||
|
no_leave_rooms: bool,
|
||||||
|
user_id: String,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
|
||||||
|
@ -229,7 +247,8 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
|
||||||
|
|
||||||
if user_id == self.services.globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Not allowed to set the password for the server account. Please use the emergency password config option.",
|
"Not allowed to set the password for the server account. Please use the emergency \
|
||||||
|
password config option.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,18 +259,24 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
|
||||||
.users
|
.users
|
||||||
.set_password(&user_id, Some(new_password.as_str()))
|
.set_password(&user_id, Some(new_password.as_str()))
|
||||||
{
|
{
|
||||||
Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Ok(()) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Successfully reset the password for user {user_id}: `{new_password}`"
|
"Successfully reset the password for user {user_id}: `{new_password}`"
|
||||||
))),
|
))),
|
||||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
| Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Couldn't reset the password for user {user_id}: {e}"
|
"Couldn't reset the password for user {user_id}: {e}"
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result<RoomMessageEventContent> {
|
pub(super) async fn deactivate_all(
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
&self,
|
||||||
|
no_leave_rooms: bool,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -269,7 +294,7 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||||
|
|
||||||
for username in usernames {
|
for username in usernames {
|
||||||
match parse_active_local_user_id(self.services, username).await {
|
match parse_active_local_user_id(self.services, username).await {
|
||||||
Ok(user_id) => {
|
| Ok(user_id) => {
|
||||||
if self.services.users.is_admin(&user_id).await && !force {
|
if self.services.users.is_admin(&user_id).await && !force {
|
||||||
self.services
|
self.services
|
||||||
.admin
|
.admin
|
||||||
|
@ -296,7 +321,7 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||||
|
|
||||||
user_ids.push(user_id);
|
user_ids.push(user_id);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
self.services
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -313,7 +338,7 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||||
|
|
||||||
for user_id in user_ids {
|
for user_id in user_ids {
|
||||||
match self.services.users.deactivate_account(&user_id).await {
|
match self.services.users.deactivate_account(&user_id).await {
|
||||||
Ok(()) => {
|
| Ok(()) => {
|
||||||
deactivation_count = deactivation_count.saturating_add(1);
|
deactivation_count = deactivation_count.saturating_add(1);
|
||||||
if !no_leave_rooms {
|
if !no_leave_rooms {
|
||||||
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
||||||
|
@ -328,14 +353,17 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||||
|
|
||||||
full_user_deactivate(self.services, &user_id, &all_joined_rooms).await?;
|
full_user_deactivate(self.services, &user_id, &all_joined_rooms).await?;
|
||||||
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
||||||
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms).await;
|
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms)
|
||||||
|
.await;
|
||||||
leave_all_rooms(self.services, &user_id).await;
|
leave_all_rooms(self.services, &user_id).await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
self.services
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!("Failed deactivating user: {e}")))
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed deactivating user: {e}"
|
||||||
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
},
|
},
|
||||||
|
@ -348,8 +376,8 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Deactivated {deactivation_count} accounts.\nSkipped admin accounts: {}. Use --force to deactivate admin \
|
"Deactivated {deactivation_count} accounts.\nSkipped admin accounts: {}. Use \
|
||||||
accounts",
|
--force to deactivate admin accounts",
|
||||||
admins.join(", ")
|
admins.join(", ")
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
@ -391,9 +419,13 @@ pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMess
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn force_join_list_of_local_users(
|
pub(super) async fn force_join_list_of_local_users(
|
||||||
&self, room_id: OwnedRoomOrAliasId, yes_i_want_to_do_this: bool,
|
&self,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
|
yes_i_want_to_do_this: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
if self.body.len() < 2
|
||||||
|
|| !self.body[0].trim().starts_with("```")
|
||||||
|
|| self.body.last().unwrap_or(&"").trim() != "```"
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
@ -402,8 +434,8 @@ pub(super) async fn force_join_list_of_local_users(
|
||||||
|
|
||||||
if !yes_i_want_to_do_this {
|
if !yes_i_want_to_do_this {
|
||||||
return Ok(RoomMessageEventContent::notice_markdown(
|
return Ok(RoomMessageEventContent::notice_markdown(
|
||||||
"You must pass the --yes-i-want-to-do-this-flag to ensure you really want to force bulk join all \
|
"You must pass the --yes-i-want-to-do-this-flag to ensure you really want to force \
|
||||||
specified local users.",
|
bulk join all specified local users.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +494,7 @@ pub(super) async fn force_join_list_of_local_users(
|
||||||
|
|
||||||
for username in usernames {
|
for username in usernames {
|
||||||
match parse_active_local_user_id(self.services, username).await {
|
match parse_active_local_user_id(self.services, username).await {
|
||||||
Ok(user_id) => {
|
| Ok(user_id) => {
|
||||||
// don't make the server service account join
|
// don't make the server service account join
|
||||||
if user_id == self.services.globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
self.services
|
self.services
|
||||||
|
@ -477,7 +509,7 @@ pub(super) async fn force_join_list_of_local_users(
|
||||||
|
|
||||||
user_ids.push(user_id);
|
user_ids.push(user_id);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
self.services
|
self.services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -505,10 +537,10 @@ pub(super) async fn force_join_list_of_local_users(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_res) => {
|
| Ok(_res) => {
|
||||||
successful_joins = successful_joins.saturating_add(1);
|
successful_joins = successful_joins.saturating_add(1);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Failed force joining {user_id} to {room_id} during bulk join: {e}");
|
debug_warn!("Failed force joining {user_id} to {room_id} during bulk join: {e}");
|
||||||
failed_joins = failed_joins.saturating_add(1);
|
failed_joins = failed_joins.saturating_add(1);
|
||||||
},
|
},
|
||||||
|
@ -516,18 +548,21 @@ pub(super) async fn force_join_list_of_local_users(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins failed.",
|
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins \
|
||||||
|
failed.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn force_join_all_local_users(
|
pub(super) async fn force_join_all_local_users(
|
||||||
&self, room_id: OwnedRoomOrAliasId, yes_i_want_to_do_this: bool,
|
&self,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
|
yes_i_want_to_do_this: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
if !yes_i_want_to_do_this {
|
if !yes_i_want_to_do_this {
|
||||||
return Ok(RoomMessageEventContent::notice_markdown(
|
return Ok(RoomMessageEventContent::notice_markdown(
|
||||||
"You must pass the --yes-i-want-to-do-this-flag to ensure you really want to force bulk join all local \
|
"You must pass the --yes-i-want-to-do-this-flag to ensure you really want to force \
|
||||||
users.",
|
bulk join all local users.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,10 +633,10 @@ pub(super) async fn force_join_all_local_users(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_res) => {
|
| Ok(_res) => {
|
||||||
successful_joins = successful_joins.saturating_add(1);
|
successful_joins = successful_joins.saturating_add(1);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Failed force joining {user_id} to {room_id} during bulk join: {e}");
|
debug_warn!("Failed force joining {user_id} to {room_id} during bulk join: {e}");
|
||||||
failed_joins = failed_joins.saturating_add(1);
|
failed_joins = failed_joins.saturating_add(1);
|
||||||
},
|
},
|
||||||
|
@ -609,13 +644,16 @@ pub(super) async fn force_join_all_local_users(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins failed.",
|
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins \
|
||||||
|
failed.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn force_join_room(
|
pub(super) async fn force_join_room(
|
||||||
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
let (room_id, servers) = self
|
let (room_id, servers) = self
|
||||||
|
@ -629,7 +667,8 @@ pub(super) async fn force_join_room(
|
||||||
self.services.globals.user_is_local(&user_id),
|
self.services.globals.user_is_local(&user_id),
|
||||||
"Parsed user_id must be a local user"
|
"Parsed user_id must be a local user"
|
||||||
);
|
);
|
||||||
join_room_by_id_helper(self.services, &user_id, &room_id, None, &servers, None, &None).await?;
|
join_room_by_id_helper(self.services, &user_id, &room_id, None, &servers, None, &None)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{user_id} has been joined to {room_id}.",
|
"{user_id} has been joined to {room_id}.",
|
||||||
|
@ -638,7 +677,9 @@ pub(super) async fn force_join_room(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn force_leave_room(
|
pub(super) async fn force_leave_room(
|
||||||
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||||
|
@ -656,7 +697,9 @@ pub(super) async fn force_leave_room(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn force_demote(
|
pub(super) async fn force_demote(
|
||||||
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: OwnedRoomOrAliasId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||||
|
@ -672,14 +715,19 @@ pub(super) async fn force_demote(
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomPowerLevelsEventContent>(&room_id, &StateEventType::RoomPowerLevels, "")
|
.room_state_get_content::<RoomPowerLevelsEventContent>(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomPowerLevels,
|
||||||
|
"",
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let user_can_demote_self = room_power_levels
|
let user_can_demote_self = room_power_levels
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|power_levels_content| {
|
.is_some_and(|power_levels_content| {
|
||||||
RoomPowerLevels::from(power_levels_content.clone()).user_can_change_user_power_level(&user_id, &user_id)
|
RoomPowerLevels::from(power_levels_content.clone())
|
||||||
|
.user_can_change_user_power_level(&user_id, &user_id)
|
||||||
}) || self
|
}) || self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -710,7 +758,8 @@ pub(super) async fn force_demote(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"User {user_id} demoted themselves to the room default power level in {room_id} - {event_id}"
|
"User {user_id} demoted themselves to the room default power level in {room_id} - \
|
||||||
|
{event_id}"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,7 +780,10 @@ pub(super) async fn make_user_admin(&self, user_id: String) -> Result<RoomMessag
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn put_room_tag(
|
pub(super) async fn put_room_tag(
|
||||||
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
tag: String,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
||||||
|
|
||||||
|
@ -741,9 +793,7 @@ pub(super) async fn put_room_tag(
|
||||||
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tags_event
|
tags_event
|
||||||
|
@ -768,7 +818,10 @@ pub(super) async fn put_room_tag(
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn delete_room_tag(
|
pub(super) async fn delete_room_tag(
|
||||||
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
tag: String,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
||||||
|
|
||||||
|
@ -778,9 +831,7 @@ pub(super) async fn delete_room_tag(
|
||||||
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tags_event.content.tags.remove(&tag.clone().into());
|
tags_event.content.tags.remove(&tag.clone().into());
|
||||||
|
@ -796,12 +847,17 @@ pub(super) async fn delete_room_tag(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
"Successfully updated room account data for {user_id} and room {room_id}, deleting room tag {tag}"
|
"Successfully updated room account data for {user_id} and room {room_id}, deleting room \
|
||||||
|
tag {tag}"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn get_room_tags(
|
||||||
|
&self,
|
||||||
|
user_id: String,
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
let user_id = parse_active_local_user_id(self.services, &user_id).await?;
|
||||||
|
|
||||||
let tags_event = self
|
let tags_event = self
|
||||||
|
@ -810,9 +866,7 @@ pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>)
|
||||||
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
.get_room(&room_id, &user_id, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
|
@ -822,7 +876,10 @@ pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn redact_event(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn redact_event(
|
||||||
|
&self,
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
let Ok(event) = self
|
let Ok(event) = self
|
||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -841,7 +898,9 @@ pub(super) async fn redact_event(&self, event_id: Box<EventId>) -> Result<RoomMe
|
||||||
let sender_user = event.sender;
|
let sender_user = event.sender;
|
||||||
|
|
||||||
if !self.services.globals.user_is_local(&sender_user) {
|
if !self.services.globals.user_is_local(&sender_user) {
|
||||||
return Ok(RoomMessageEventContent::text_plain("This command only works on local users."));
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
|
"This command only works on local users.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let reason = format!(
|
let reason = format!(
|
||||||
|
|
|
@ -8,7 +8,10 @@ pub(crate) fn escape_html(s: &str) -> String {
|
||||||
.replace('>', ">")
|
.replace('>', ">")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_room_info(services: &Services, room_id: &RoomId) -> (OwnedRoomId, u64, String) {
|
pub(crate) async fn get_room_info(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
) -> (OwnedRoomId, u64, String) {
|
||||||
(
|
(
|
||||||
room_id.into(),
|
room_id.into(),
|
||||||
services
|
services
|
||||||
|
@ -44,7 +47,10 @@ pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses user ID that is an active (not guest or deactivated) local user
|
/// Parses user ID that is an active (not guest or deactivated) local user
|
||||||
pub(crate) async fn parse_active_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
pub(crate) async fn parse_active_local_user_id(
|
||||||
|
services: &Services,
|
||||||
|
user_id: &str,
|
||||||
|
) -> Result<OwnedUserId> {
|
||||||
let user_id = parse_local_user_id(services, user_id)?;
|
let user_id = parse_local_user_id(services, user_id)?;
|
||||||
|
|
||||||
if !services.users.exists(&user_id).await {
|
if !services.users.exists(&user_id).await {
|
||||||
|
|
|
@ -2,16 +2,19 @@ use std::fmt::Write;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{debug_info, error, info, is_equal_to, utils, utils::ReadyExt, warn, Error, PduBuilder, Result};
|
use conduwuit::{
|
||||||
|
debug_info, error, info, is_equal_to, utils, utils::ReadyExt, warn, Error, PduBuilder, Result,
|
||||||
|
};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use register::RegistrationKind;
|
use register::RegistrationKind;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
account::{
|
account::{
|
||||||
change_password, check_registration_token_validity, deactivate, get_3pids, get_username_availability,
|
change_password, check_registration_token_validity, deactivate, get_3pids,
|
||||||
|
get_username_availability,
|
||||||
register::{self, LoginType},
|
register::{self, LoginType},
|
||||||
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, whoami,
|
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn,
|
||||||
ThirdPartyIdRemovalStatus,
|
whoami, ThirdPartyIdRemovalStatus,
|
||||||
},
|
},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||||
|
@ -45,7 +48,8 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||||
/// invalid when trying to register
|
/// invalid when trying to register
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
||||||
pub(crate) async fn get_register_available_route(
|
pub(crate) async fn get_register_available_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_username_availability::v3::Request>,
|
body: Ruma<get_username_availability::v3::Request>,
|
||||||
) -> Result<get_username_availability::v3::Response> {
|
) -> Result<get_username_availability::v3::Response> {
|
||||||
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
||||||
|
@ -66,7 +70,8 @@ pub(crate) async fn get_register_available_route(
|
||||||
let user_id = UserId::parse_with_server_name(body_username, services.globals.server_name())
|
let user_id = UserId::parse_with_server_name(body_username, services.globals.server_name())
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
(!user_id.is_historical() || is_matrix_appservice_irc) && services.globals.user_is_local(user_id)
|
(!user_id.is_historical() || is_matrix_appservice_irc)
|
||||||
|
&& services.globals.user_is_local(user_id)
|
||||||
})
|
})
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
|
|
||||||
|
@ -86,9 +91,7 @@ pub(crate) async fn get_register_available_route(
|
||||||
// TODO add check for appservice namespaces
|
// TODO add check for appservice namespaces
|
||||||
|
|
||||||
// If no if check is true we have an username that's available to be used.
|
// If no if check is true we have an username that's available to be used.
|
||||||
Ok(get_username_availability::v3::Response {
|
Ok(get_username_availability::v3::Response { available: true })
|
||||||
available: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/register`
|
/// # `POST /_matrix/client/v3/register`
|
||||||
|
@ -111,12 +114,14 @@ pub(crate) async fn get_register_available_route(
|
||||||
#[allow(clippy::doc_markdown)]
|
#[allow(clippy::doc_markdown)]
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
||||||
pub(crate) async fn register_route(
|
pub(crate) async fn register_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<register::v3::Request>,
|
||||||
) -> Result<register::v3::Response> {
|
) -> Result<register::v3::Response> {
|
||||||
if !services.globals.allow_registration() && body.appservice_info.is_none() {
|
if !services.globals.allow_registration() && body.appservice_info.is_none() {
|
||||||
info!(
|
info!(
|
||||||
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
|
"Registration disabled and request not from known appservice, rejecting \
|
||||||
\"{}\"",
|
registration attempt for username \"{}\"",
|
||||||
body.username.as_deref().unwrap_or("")
|
body.username.as_deref().unwrap_or("")
|
||||||
);
|
);
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Registration has been disabled."));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Registration has been disabled."));
|
||||||
|
@ -126,11 +131,12 @@ pub(crate) async fn register_route(
|
||||||
|
|
||||||
if is_guest
|
if is_guest
|
||||||
&& (!services.globals.allow_guest_registration()
|
&& (!services.globals.allow_guest_registration()
|
||||||
|| (services.globals.allow_registration() && services.globals.registration_token.is_some()))
|
|| (services.globals.allow_registration()
|
||||||
|
&& services.globals.registration_token.is_some()))
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
|
"Guest registration disabled / registration enabled with token configured, \
|
||||||
attempt, initial device name: \"{}\"",
|
rejecting guest registration attempt, initial device name: \"{}\"",
|
||||||
body.initial_device_display_name.as_deref().unwrap_or("")
|
body.initial_device_display_name.as_deref().unwrap_or("")
|
||||||
);
|
);
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -143,21 +149,25 @@ pub(crate) async fn register_route(
|
||||||
// generic user error.
|
// generic user error.
|
||||||
if is_guest && services.users.count().await < 2 {
|
if is_guest && services.users.count().await < 2 {
|
||||||
warn!(
|
warn!(
|
||||||
"Guest account attempted to register before a real admin user has been registered, rejecting \
|
"Guest account attempted to register before a real admin user has been registered, \
|
||||||
registration. Guest's initial device name: \"{}\"",
|
rejecting registration. Guest's initial device name: \"{}\"",
|
||||||
body.initial_device_display_name.as_deref().unwrap_or("")
|
body.initial_device_display_name.as_deref().unwrap_or("")
|
||||||
);
|
);
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Registration temporarily disabled."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"Registration temporarily disabled.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_id = match (&body.username, is_guest) {
|
let user_id = match (&body.username, is_guest) {
|
||||||
(Some(username), false) => {
|
| (Some(username), false) => {
|
||||||
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
||||||
let is_matrix_appservice_irc = body.appservice_info.as_ref().is_some_and(|appservice| {
|
let is_matrix_appservice_irc =
|
||||||
appservice.registration.id == "irc"
|
body.appservice_info.as_ref().is_some_and(|appservice| {
|
||||||
|| appservice.registration.id.contains("matrix-appservice-irc")
|
appservice.registration.id == "irc"
|
||||||
|| appservice.registration.id.contains("matrix_appservice_irc")
|
|| appservice.registration.id.contains("matrix-appservice-irc")
|
||||||
});
|
|| appservice.registration.id.contains("matrix_appservice_irc")
|
||||||
|
});
|
||||||
|
|
||||||
// don't force the username lowercase if it's from matrix-appservice-irc
|
// don't force the username lowercase if it's from matrix-appservice-irc
|
||||||
let body_username = if is_matrix_appservice_irc {
|
let body_username = if is_matrix_appservice_irc {
|
||||||
|
@ -166,15 +176,23 @@ pub(crate) async fn register_route(
|
||||||
username.to_lowercase()
|
username.to_lowercase()
|
||||||
};
|
};
|
||||||
|
|
||||||
let proposed_user_id = UserId::parse_with_server_name(body_username, services.globals.server_name())
|
let proposed_user_id =
|
||||||
.ok()
|
UserId::parse_with_server_name(body_username, services.globals.server_name())
|
||||||
.filter(|user_id| {
|
.ok()
|
||||||
(!user_id.is_historical() || is_matrix_appservice_irc) && services.globals.user_is_local(user_id)
|
.filter(|user_id| {
|
||||||
})
|
(!user_id.is_historical() || is_matrix_appservice_irc)
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
&& services.globals.user_is_local(user_id)
|
||||||
|
})
|
||||||
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidUsername,
|
||||||
|
"Username is invalid.",
|
||||||
|
))?;
|
||||||
|
|
||||||
if services.users.exists(&proposed_user_id).await {
|
if services.users.exists(&proposed_user_id).await {
|
||||||
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::UserInUse,
|
||||||
|
"Desired user ID is already taken.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services
|
if services
|
||||||
|
@ -187,7 +205,7 @@ pub(crate) async fn register_route(
|
||||||
|
|
||||||
proposed_user_id
|
proposed_user_id
|
||||||
},
|
},
|
||||||
_ => loop {
|
| _ => loop {
|
||||||
let proposed_user_id = UserId::parse_with_server_name(
|
let proposed_user_id = UserId::parse_with_server_name(
|
||||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||||
services.globals.server_name(),
|
services.globals.server_name(),
|
||||||
|
@ -228,9 +246,7 @@ pub(crate) async fn register_route(
|
||||||
} else {
|
} else {
|
||||||
// No registration token necessary, but clients must still go through the flow
|
// No registration token necessary, but clients must still go through the flow
|
||||||
uiaainfo = UiaaInfo {
|
uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||||
stages: vec![AuthType::Dummy],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
@ -244,7 +260,8 @@ pub(crate) async fn register_route(
|
||||||
let (worked, uiaainfo) = services
|
let (worked, uiaainfo) = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.try_auth(
|
.try_auth(
|
||||||
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
&UserId::parse_with_server_name("", services.globals.server_name())
|
||||||
|
.expect("we know this is valid"),
|
||||||
"".into(),
|
"".into(),
|
||||||
auth,
|
auth,
|
||||||
&uiaainfo,
|
&uiaainfo,
|
||||||
|
@ -257,7 +274,8 @@ pub(crate) async fn register_route(
|
||||||
} else if let Some(json) = body.json_body {
|
} else if let Some(json) = body.json_body {
|
||||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||||
services.uiaa.create(
|
services.uiaa.create(
|
||||||
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
&UserId::parse_with_server_name("", services.globals.server_name())
|
||||||
|
.expect("we know this is valid"),
|
||||||
"".into(),
|
"".into(),
|
||||||
&uiaainfo,
|
&uiaainfo,
|
||||||
&json,
|
&json,
|
||||||
|
@ -268,11 +286,7 @@ pub(crate) async fn register_route(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let password = if is_guest {
|
let password = if is_guest { None } else { body.password.as_deref() };
|
||||||
None
|
|
||||||
} else {
|
|
||||||
body.password.as_deref()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
services.users.create(&user_id, password)?;
|
services.users.create(&user_id, password)?;
|
||||||
|
@ -282,7 +296,9 @@ pub(crate) async fn register_route(
|
||||||
|
|
||||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||||
// content is set to the user's display name with a space before it
|
// content is set to the user's display name with a space before it
|
||||||
if !services.globals.new_user_displayname_suffix().is_empty() && body.appservice_info.is_none() {
|
if !services.globals.new_user_displayname_suffix().is_empty()
|
||||||
|
&& body.appservice_info.is_none()
|
||||||
|
{
|
||||||
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
|
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
|
||||||
.expect("should be able to write to string buffer");
|
.expect("should be able to write to string buffer");
|
||||||
}
|
}
|
||||||
|
@ -319,12 +335,8 @@ pub(crate) async fn register_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new device id if the user didn't specify one
|
// Generate new device id if the user didn't specify one
|
||||||
let device_id = if is_guest {
|
let device_id = if is_guest { None } else { body.device_id.clone() }
|
||||||
None
|
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||||
} else {
|
|
||||||
body.device_id.clone()
|
|
||||||
}
|
|
||||||
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
|
||||||
|
|
||||||
// Generate new token for the device
|
// Generate new token for the device
|
||||||
let token = utils::random_string(TOKEN_LENGTH);
|
let token = utils::random_string(TOKEN_LENGTH);
|
||||||
|
@ -349,15 +361,16 @@ pub(crate) async fn register_route(
|
||||||
if body.appservice_info.is_none() && !is_guest {
|
if body.appservice_info.is_none() && !is_guest {
|
||||||
if !device_display_name.is_empty() {
|
if !device_display_name.is_empty() {
|
||||||
info!(
|
info!(
|
||||||
"New user \"{user_id}\" registered on this server with device display name: \"{device_display_name}\""
|
"New user \"{user_id}\" registered on this server with device display name: \
|
||||||
|
\"{device_display_name}\""
|
||||||
);
|
);
|
||||||
|
|
||||||
if services.globals.config.admin_room_notices {
|
if services.globals.config.admin_room_notices {
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"New user \"{user_id}\" registered on this server from IP {client} and device display name \
|
"New user \"{user_id}\" registered on this server from IP {client} and \
|
||||||
\"{device_display_name}\""
|
device display name \"{device_display_name}\""
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -386,8 +399,8 @@ pub(crate) async fn register_route(
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Guest user \"{user_id}\" with device display name \"{device_display_name}\" registered on \
|
"Guest user \"{user_id}\" with device display name \
|
||||||
this server from IP {client}"
|
\"{device_display_name}\" registered on this server from IP {client}"
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -398,8 +411,8 @@ pub(crate) async fn register_route(
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
"Guest user \"{user_id}\" with no device display name registered on \
|
||||||
{client}",
|
this server from IP {client}",
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -430,7 +443,10 @@ pub(crate) async fn register_route(
|
||||||
{
|
{
|
||||||
for room in &services.globals.config.auto_join_rooms {
|
for room in &services.globals.config.auto_join_rooms {
|
||||||
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
||||||
error!("Failed to resolve room alias to room ID when attempting to auto join {room}, skipping");
|
error!(
|
||||||
|
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||||
|
{room}, skipping"
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -440,7 +456,9 @@ pub(crate) async fn register_route(
|
||||||
.server_in_room(services.globals.server_name(), &room_id)
|
.server_in_room(services.globals.server_name(), &room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
warn!("Skipping room {room} to automatically join as we have never joined before.");
|
warn!(
|
||||||
|
"Skipping room {room} to automatically join as we have never joined before."
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +512,8 @@ pub(crate) async fn register_route(
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
||||||
pub(crate) async fn change_password_route(
|
pub(crate) async fn change_password_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<change_password::v3::Request>,
|
body: Ruma<change_password::v3::Request>,
|
||||||
) -> Result<change_password::v3::Response> {
|
) -> Result<change_password::v3::Response> {
|
||||||
// Authentication for this endpoint was made optional, but we need
|
// Authentication for this endpoint was made optional, but we need
|
||||||
|
@ -506,9 +525,7 @@ pub(crate) async fn change_password_route(
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||||
stages: vec![AuthType::Password],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
@ -572,7 +589,8 @@ pub(crate) async fn change_password_route(
|
||||||
///
|
///
|
||||||
/// Note: Also works for Application Services
|
/// Note: Also works for Application Services
|
||||||
pub(crate) async fn whoami_route(
|
pub(crate) async fn whoami_route(
|
||||||
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<whoami::v3::Request>,
|
||||||
) -> Result<whoami::v3::Response> {
|
) -> 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.clone();
|
let device_id = body.sender_device.clone();
|
||||||
|
@ -580,7 +598,8 @@ pub(crate) async fn whoami_route(
|
||||||
Ok(whoami::v3::Response {
|
Ok(whoami::v3::Response {
|
||||||
user_id: sender_user.clone(),
|
user_id: sender_user.clone(),
|
||||||
device_id,
|
device_id,
|
||||||
is_guest: services.users.is_deactivated(sender_user).await? && body.appservice_info.is_none(),
|
is_guest: services.users.is_deactivated(sender_user).await?
|
||||||
|
&& body.appservice_info.is_none(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +616,8 @@ pub(crate) async fn whoami_route(
|
||||||
/// - Removes ability to log in again
|
/// - Removes ability to log in again
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
||||||
pub(crate) async fn deactivate_route(
|
pub(crate) async fn deactivate_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<deactivate::v3::Request>,
|
body: Ruma<deactivate::v3::Request>,
|
||||||
) -> Result<deactivate::v3::Response> {
|
) -> Result<deactivate::v3::Response> {
|
||||||
// Authentication for this endpoint was made optional, but we need
|
// Authentication for this endpoint was made optional, but we need
|
||||||
|
@ -609,9 +629,7 @@ pub(crate) async fn deactivate_route(
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||||
stages: vec![AuthType::Password],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
@ -675,7 +693,9 @@ pub(crate) async fn deactivate_route(
|
||||||
/// Get a list of third party identifiers associated with this account.
|
/// Get a list of third party identifiers associated with this account.
|
||||||
///
|
///
|
||||||
/// - Currently always returns empty list
|
/// - Currently always returns empty list
|
||||||
pub(crate) async fn third_party_route(body: Ruma<get_3pids::v3::Request>) -> Result<get_3pids::v3::Response> {
|
pub(crate) async fn third_party_route(
|
||||||
|
body: Ruma<get_3pids::v3::Request>,
|
||||||
|
) -> Result<get_3pids::v3::Response> {
|
||||||
let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
Ok(get_3pids::v3::Response::new(Vec::new()))
|
Ok(get_3pids::v3::Response::new(Vec::new()))
|
||||||
|
@ -720,7 +740,8 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
||||||
/// Currently does not have any ratelimiting, and this isn't very practical as
|
/// Currently does not have any ratelimiting, and this isn't very practical as
|
||||||
/// there is only one registration token allowed.
|
/// there is only one registration token allowed.
|
||||||
pub(crate) async fn check_registration_token_validity(
|
pub(crate) async fn check_registration_token_validity(
|
||||||
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<check_registration_token_validity::v1::Request>,
|
||||||
) -> Result<check_registration_token_validity::v1::Response> {
|
) -> Result<check_registration_token_validity::v1::Response> {
|
||||||
let Some(reg_token) = services.globals.registration_token.clone() else {
|
let Some(reg_token) = services.globals.registration_token.clone() else {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -729,9 +750,7 @@ pub(crate) async fn check_registration_token_validity(
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(check_registration_token_validity::v1::Response {
|
Ok(check_registration_token_validity::v1::Response { valid: reg_token == body.token })
|
||||||
valid: reg_token == body.token,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs through all the deactivation steps:
|
/// Runs through all the deactivation steps:
|
||||||
|
@ -742,7 +761,9 @@ pub(crate) async fn check_registration_token_validity(
|
||||||
/// - Removing all profile data
|
/// - Removing all profile data
|
||||||
/// - Leaving all rooms (and forgets all of them)
|
/// - Leaving all rooms (and forgets all of them)
|
||||||
pub async fn full_user_deactivate(
|
pub async fn full_user_deactivate(
|
||||||
services: &Services, user_id: &UserId, all_joined_rooms: &[OwnedRoomId],
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
all_joined_rooms: &[OwnedRoomId],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
services.users.deactivate_account(user_id).await.ok();
|
services.users.deactivate_account(user_id).await.ok();
|
||||||
super::update_displayname(services, user_id, None, all_joined_rooms).await;
|
super::update_displayname(services, user_id, None, all_joined_rooms).await;
|
||||||
|
@ -751,7 +772,9 @@ pub async fn full_user_deactivate(
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.all_profile_keys(user_id)
|
.all_profile_keys(user_id)
|
||||||
.ready_for_each(|(profile_key, _)| services.users.set_profile_key(user_id, &profile_key, None))
|
.ready_for_each(|(profile_key, _)| {
|
||||||
|
services.users.set_profile_key(user_id, &profile_key, None);
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
for room_id in all_joined_rooms {
|
for room_id in all_joined_rooms {
|
||||||
|
@ -760,20 +783,26 @@ pub async fn full_user_deactivate(
|
||||||
let room_power_levels = services
|
let room_power_levels = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomPowerLevelsEventContent>(room_id, &StateEventType::RoomPowerLevels, "")
|
.room_state_get_content::<RoomPowerLevelsEventContent>(
|
||||||
|
room_id,
|
||||||
|
&StateEventType::RoomPowerLevels,
|
||||||
|
"",
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let user_can_demote_self = room_power_levels
|
let user_can_demote_self =
|
||||||
.as_ref()
|
room_power_levels
|
||||||
.is_some_and(|power_levels_content| {
|
.as_ref()
|
||||||
RoomPowerLevels::from(power_levels_content.clone()).user_can_change_user_power_level(user_id, user_id)
|
.is_some_and(|power_levels_content| {
|
||||||
}) || services
|
RoomPowerLevels::from(power_levels_content.clone())
|
||||||
.rooms
|
.user_can_change_user_power_level(user_id, user_id)
|
||||||
.state_accessor
|
}) || services
|
||||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
.rooms
|
||||||
.await
|
.state_accessor
|
||||||
.is_ok_and(|event| event.sender == user_id);
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||||
|
.await
|
||||||
|
.is_ok_and(|event| event.sender == user_id);
|
||||||
|
|
||||||
if user_can_demote_self {
|
if user_can_demote_self {
|
||||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||||
|
|
|
@ -2,11 +2,12 @@ use axum::extract::State;
|
||||||
use conduwuit::{err, Err};
|
use conduwuit::{err, Err};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::config::{
|
api::client::config::{
|
||||||
get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data,
|
get_global_account_data, get_room_account_data, set_global_account_data,
|
||||||
|
set_room_account_data,
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent, GlobalAccountDataEventType,
|
AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent,
|
||||||
RoomAccountDataEventType,
|
GlobalAccountDataEventType, RoomAccountDataEventType,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
RoomId, UserId,
|
RoomId, UserId,
|
||||||
|
@ -20,7 +21,8 @@ use crate::{service::Services, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Sets some account data for the sender user.
|
/// Sets some account data for the sender user.
|
||||||
pub(crate) async fn set_global_account_data_route(
|
pub(crate) async fn set_global_account_data_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_global_account_data::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_global_account_data::v3::Request>,
|
||||||
) -> Result<set_global_account_data::v3::Response> {
|
) -> Result<set_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -28,7 +30,14 @@ pub(crate) async fn set_global_account_data_route(
|
||||||
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_account_data(&services, None, &body.user_id, &body.event_type.to_string(), body.data.json()).await?;
|
set_account_data(
|
||||||
|
&services,
|
||||||
|
None,
|
||||||
|
&body.user_id,
|
||||||
|
&body.event_type.to_string(),
|
||||||
|
body.data.json(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(set_global_account_data::v3::Response {})
|
Ok(set_global_account_data::v3::Response {})
|
||||||
}
|
}
|
||||||
|
@ -37,7 +46,8 @@ pub(crate) async fn set_global_account_data_route(
|
||||||
///
|
///
|
||||||
/// Sets some room account data for the sender user.
|
/// Sets some room account data for the sender user.
|
||||||
pub(crate) async fn set_room_account_data_route(
|
pub(crate) async fn set_room_account_data_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_room_account_data::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_room_account_data::v3::Request>,
|
||||||
) -> Result<set_room_account_data::v3::Response> {
|
) -> Result<set_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -61,7 +71,8 @@ pub(crate) async fn set_room_account_data_route(
|
||||||
///
|
///
|
||||||
/// Gets some account data for the sender user.
|
/// Gets some account data for the sender user.
|
||||||
pub(crate) async fn get_global_account_data_route(
|
pub(crate) async fn get_global_account_data_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_global_account_data::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_global_account_data::v3::Request>,
|
||||||
) -> Result<get_global_account_data::v3::Response> {
|
) -> Result<get_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -75,16 +86,15 @@ pub(crate) async fn get_global_account_data_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
|
||||||
|
|
||||||
Ok(get_global_account_data::v3::Response {
|
Ok(get_global_account_data::v3::Response { account_data: account_data.content })
|
||||||
account_data: account_data.content,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
|
/// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
|
||||||
///
|
///
|
||||||
/// Gets some room account data for the sender user.
|
/// Gets some room account data for the sender user.
|
||||||
pub(crate) async fn get_room_account_data_route(
|
pub(crate) async fn get_room_account_data_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_room_account_data::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_room_account_data::v3::Request>,
|
||||||
) -> Result<get_room_account_data::v3::Response> {
|
) -> Result<get_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -98,17 +108,20 @@ pub(crate) async fn get_room_account_data_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Data not found."))))?;
|
||||||
|
|
||||||
Ok(get_room_account_data::v3::Response {
|
Ok(get_room_account_data::v3::Response { account_data: account_data.content })
|
||||||
account_data: account_data.content,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_account_data(
|
async fn set_account_data(
|
||||||
services: &Services, room_id: Option<&RoomId>, sender_user: &UserId, event_type_s: &str, data: &RawJsonValue,
|
services: &Services,
|
||||||
|
room_id: Option<&RoomId>,
|
||||||
|
sender_user: &UserId,
|
||||||
|
event_type_s: &str,
|
||||||
|
data: &RawJsonValue,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if event_type_s == RoomAccountDataEventType::FullyRead.to_cow_str() {
|
if event_type_s == RoomAccountDataEventType::FullyRead.to_cow_str() {
|
||||||
return Err!(Request(BadJson(
|
return Err!(Request(BadJson(
|
||||||
"This endpoint cannot be used for marking a room as fully read (setting m.fully_read)"
|
"This endpoint cannot be used for marking a room as fully read (setting \
|
||||||
|
m.fully_read)"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +131,8 @@ async fn set_account_data(
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let data: serde_json::Value =
|
let data: serde_json::Value = serde_json::from_str(data.get())
|
||||||
serde_json::from_str(data.get()).map_err(|e| err!(Request(BadJson(warn!("Invalid JSON provided: {e}")))))?;
|
.map_err(|e| err!(Request(BadJson(warn!("Invalid JSON provided: {e}")))))?;
|
||||||
|
|
||||||
services
|
services
|
||||||
.account_data
|
.account_data
|
||||||
|
|
|
@ -14,7 +14,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// Creates a new room alias on this server.
|
/// Creates a new room alias on this server.
|
||||||
pub(crate) async fn create_alias_route(
|
pub(crate) async fn create_alias_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_alias::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_alias::v3::Request>,
|
||||||
) -> Result<create_alias::v3::Response> {
|
) -> Result<create_alias::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");
|
||||||
|
|
||||||
|
@ -58,7 +59,8 @@ pub(crate) async fn create_alias_route(
|
||||||
///
|
///
|
||||||
/// - TODO: Update canonical alias event
|
/// - TODO: Update canonical alias event
|
||||||
pub(crate) async fn delete_alias_route(
|
pub(crate) async fn delete_alias_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_alias::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_alias::v3::Request>,
|
||||||
) -> Result<delete_alias::v3::Response> {
|
) -> Result<delete_alias::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");
|
||||||
|
|
||||||
|
@ -83,11 +85,13 @@ pub(crate) async fn delete_alias_route(
|
||||||
///
|
///
|
||||||
/// Resolve an alias locally or over federation.
|
/// Resolve an alias locally or over federation.
|
||||||
pub(crate) async fn get_alias_route(
|
pub(crate) async fn get_alias_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_alias::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_alias::v3::Request>,
|
||||||
) -> Result<get_alias::v3::Response> {
|
) -> Result<get_alias::v3::Response> {
|
||||||
let room_alias = body.body.room_alias;
|
let room_alias = body.body.room_alias;
|
||||||
|
|
||||||
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias, None).await else {
|
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias, None).await
|
||||||
|
else {
|
||||||
return Err!(Request(NotFound("Room with alias not found.")));
|
return Err!(Request(NotFound("Room with alias not found.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,7 +102,10 @@ pub(crate) async fn get_alias_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn room_available_servers(
|
async fn room_available_servers(
|
||||||
services: &Services, room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: Vec<OwnedServerName>,
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_alias: &RoomAliasId,
|
||||||
|
pre_servers: Vec<OwnedServerName>,
|
||||||
) -> Vec<OwnedServerName> {
|
) -> Vec<OwnedServerName> {
|
||||||
// find active servers in room state cache to suggest
|
// find active servers in room state cache to suggest
|
||||||
let mut servers: Vec<OwnedServerName> = services
|
let mut servers: Vec<OwnedServerName> = services
|
||||||
|
|
|
@ -9,12 +9,12 @@ use crate::Ruma;
|
||||||
/// Ask the homeserver to ping the application service to ensure the connection
|
/// Ask the homeserver to ping the application service to ensure the connection
|
||||||
/// works.
|
/// works.
|
||||||
pub(crate) async fn appservice_ping(
|
pub(crate) async fn appservice_ping(
|
||||||
State(services): State<crate::State>, body: Ruma<request_ping::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<request_ping::v1::Request>,
|
||||||
) -> Result<request_ping::v1::Response> {
|
) -> Result<request_ping::v1::Response> {
|
||||||
let appservice_info = body
|
let appservice_info = body.appservice_info.as_ref().ok_or_else(|| {
|
||||||
.appservice_info
|
err!(Request(Forbidden("This endpoint can only be called by appservices.")))
|
||||||
.as_ref()
|
})?;
|
||||||
.ok_or_else(|| err!(Request(Forbidden("This endpoint can only be called by appservices."))))?;
|
|
||||||
|
|
||||||
if body.appservice_id != appservice_info.registration.id {
|
if body.appservice_id != appservice_info.registration.id {
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
|
@ -41,7 +41,5 @@ pub(crate) async fn appservice_ping(
|
||||||
.await?
|
.await?
|
||||||
.expect("We already validated if an appservice URL exists above");
|
.expect("We already validated if an appservice URL exists above");
|
||||||
|
|
||||||
Ok(request_ping::v1::Response {
|
Ok(request_ping::v1::Response { duration: timer.elapsed() })
|
||||||
duration: timer.elapsed(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ use axum::extract::State;
|
||||||
use conduwuit::{err, Err};
|
use conduwuit::{err, Err};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::backup::{
|
api::client::backup::{
|
||||||
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session, create_backup_version,
|
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session,
|
||||||
delete_backup_keys, delete_backup_keys_for_room, delete_backup_keys_for_session, delete_backup_version,
|
create_backup_version, delete_backup_keys, delete_backup_keys_for_room,
|
||||||
get_backup_info, get_backup_keys, get_backup_keys_for_room, get_backup_keys_for_session,
|
delete_backup_keys_for_session, delete_backup_version, get_backup_info, get_backup_keys,
|
||||||
get_latest_backup_info, update_backup_version,
|
get_backup_keys_for_room, get_backup_keys_for_session, get_latest_backup_info,
|
||||||
|
update_backup_version,
|
||||||
},
|
},
|
||||||
UInt,
|
UInt,
|
||||||
};
|
};
|
||||||
|
@ -16,15 +17,14 @@ use crate::{Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Creates a new backup.
|
/// Creates a new backup.
|
||||||
pub(crate) async fn create_backup_version_route(
|
pub(crate) async fn create_backup_version_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_backup_version::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_backup_version::v3::Request>,
|
||||||
) -> Result<create_backup_version::v3::Response> {
|
) -> Result<create_backup_version::v3::Response> {
|
||||||
let version = services
|
let version = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.create_backup(body.sender_user(), &body.algorithm)?;
|
.create_backup(body.sender_user(), &body.algorithm)?;
|
||||||
|
|
||||||
Ok(create_backup_version::v3::Response {
|
Ok(create_backup_version::v3::Response { version })
|
||||||
version,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/room_keys/version/{version}`
|
/// # `PUT /_matrix/client/r0/room_keys/version/{version}`
|
||||||
|
@ -32,7 +32,8 @@ pub(crate) async fn create_backup_version_route(
|
||||||
/// Update information about an existing backup. Only `auth_data` can be
|
/// Update information about an existing backup. Only `auth_data` can be
|
||||||
/// modified.
|
/// modified.
|
||||||
pub(crate) async fn update_backup_version_route(
|
pub(crate) async fn update_backup_version_route(
|
||||||
State(services): State<crate::State>, body: Ruma<update_backup_version::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<update_backup_version::v3::Request>,
|
||||||
) -> Result<update_backup_version::v3::Response> {
|
) -> Result<update_backup_version::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -46,7 +47,8 @@ pub(crate) async fn update_backup_version_route(
|
||||||
///
|
///
|
||||||
/// Get information about the latest backup version.
|
/// Get information about the latest backup version.
|
||||||
pub(crate) async fn get_latest_backup_info_route(
|
pub(crate) async fn get_latest_backup_info_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_latest_backup_info::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_latest_backup_info::v3::Request>,
|
||||||
) -> Result<get_latest_backup_info::v3::Response> {
|
) -> Result<get_latest_backup_info::v3::Response> {
|
||||||
let (version, algorithm) = services
|
let (version, algorithm) = services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -75,13 +77,16 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||||
///
|
///
|
||||||
/// Get information about an existing backup.
|
/// Get information about an existing backup.
|
||||||
pub(crate) async fn get_backup_info_route(
|
pub(crate) async fn get_backup_info_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_backup_info::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_backup_info::v3::Request>,
|
||||||
) -> Result<get_backup_info::v3::Response> {
|
) -> Result<get_backup_info::v3::Response> {
|
||||||
let algorithm = services
|
let algorithm = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_backup(body.sender_user(), &body.version)
|
.get_backup(body.sender_user(), &body.version)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Key backup does not exist at version {:?}", body.version))))?;
|
.map_err(|_| {
|
||||||
|
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(get_backup_info::v3::Response {
|
Ok(get_backup_info::v3::Response {
|
||||||
algorithm,
|
algorithm,
|
||||||
|
@ -105,7 +110,8 @@ pub(crate) async fn get_backup_info_route(
|
||||||
/// - Deletes both information about the backup, as well as all key data related
|
/// - Deletes both information about the backup, as well as all key data related
|
||||||
/// to the backup
|
/// to the backup
|
||||||
pub(crate) async fn delete_backup_version_route(
|
pub(crate) async fn delete_backup_version_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_backup_version::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_backup_version::v3::Request>,
|
||||||
) -> Result<delete_backup_version::v3::Response> {
|
) -> Result<delete_backup_version::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -124,7 +130,8 @@ pub(crate) async fn delete_backup_version_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_route(
|
pub(crate) async fn add_backup_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<add_backup_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<add_backup_keys::v3::Request>,
|
||||||
) -> Result<add_backup_keys::v3::Response> {
|
) -> Result<add_backup_keys::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -168,7 +175,8 @@ pub(crate) async fn add_backup_keys_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_for_room_route(
|
pub(crate) async fn add_backup_keys_for_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<add_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -210,7 +218,8 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub(crate) async fn add_backup_keys_for_session_route(
|
pub(crate) async fn add_backup_keys_for_session_route(
|
||||||
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<add_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -251,56 +260,56 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup.
|
/// Retrieves all keys from the backup.
|
||||||
pub(crate) async fn get_backup_keys_route(
|
pub(crate) async fn get_backup_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_backup_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_backup_keys::v3::Request>,
|
||||||
) -> Result<get_backup_keys::v3::Response> {
|
) -> Result<get_backup_keys::v3::Response> {
|
||||||
let rooms = services
|
let rooms = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_all(body.sender_user(), &body.version)
|
.get_all(body.sender_user(), &body.version)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_backup_keys::v3::Response {
|
Ok(get_backup_keys::v3::Response { rooms })
|
||||||
rooms,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
|
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup for a given room.
|
/// Retrieves all keys from the backup for a given room.
|
||||||
pub(crate) async fn get_backup_keys_for_room_route(
|
pub(crate) async fn get_backup_keys_for_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||||
let sessions = services
|
let sessions = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_room(body.sender_user(), &body.version, &body.room_id)
|
.get_room(body.sender_user(), &body.version, &body.room_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_backup_keys_for_room::v3::Response {
|
Ok(get_backup_keys_for_room::v3::Response { sessions })
|
||||||
sessions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||||
///
|
///
|
||||||
/// Retrieves a key from the backup.
|
/// Retrieves a key from the backup.
|
||||||
pub(crate) async fn get_backup_keys_for_session_route(
|
pub(crate) async fn get_backup_keys_for_session_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||||
let key_data = services
|
let key_data = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound(debug_error!("Backup key not found for this user's session.")))))?;
|
.map_err(|_| {
|
||||||
|
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(get_backup_keys_for_session::v3::Response {
|
Ok(get_backup_keys_for_session::v3::Response { key_data })
|
||||||
key_data,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `DELETE /_matrix/client/r0/room_keys/keys`
|
/// # `DELETE /_matrix/client/r0/room_keys/keys`
|
||||||
///
|
///
|
||||||
/// Delete the keys from the backup.
|
/// Delete the keys from the backup.
|
||||||
pub(crate) async fn delete_backup_keys_route(
|
pub(crate) async fn delete_backup_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_backup_keys::v3::Request>,
|
||||||
) -> Result<delete_backup_keys::v3::Response> {
|
) -> Result<delete_backup_keys::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -324,7 +333,8 @@ pub(crate) async fn delete_backup_keys_route(
|
||||||
///
|
///
|
||||||
/// Delete the keys from the backup for a given room.
|
/// Delete the keys from the backup for a given room.
|
||||||
pub(crate) async fn delete_backup_keys_for_room_route(
|
pub(crate) async fn delete_backup_keys_for_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
||||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
@ -348,7 +358,8 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||||
///
|
///
|
||||||
/// Delete a key from the backup.
|
/// Delete a key from the backup.
|
||||||
pub(crate) async fn delete_backup_keys_for_session_route(
|
pub(crate) async fn delete_backup_keys_for_session_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
||||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::collections::BTreeMap;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::discovery::get_capabilities::{
|
api::client::discovery::get_capabilities::{
|
||||||
self, Capabilities, GetLoginTokenCapability, RoomVersionStability, RoomVersionsCapability,
|
self, Capabilities, GetLoginTokenCapability, RoomVersionStability,
|
||||||
ThirdPartyIdChangesCapability,
|
RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
||||||
},
|
},
|
||||||
RoomVersionId,
|
RoomVersionId,
|
||||||
};
|
};
|
||||||
|
@ -17,9 +17,11 @@ use crate::{Result, Ruma};
|
||||||
/// Get information on the supported feature set and other relevent capabilities
|
/// Get information on the supported feature set and other relevent capabilities
|
||||||
/// of this server.
|
/// of this server.
|
||||||
pub(crate) async fn get_capabilities_route(
|
pub(crate) async fn get_capabilities_route(
|
||||||
State(services): State<crate::State>, _body: Ruma<get_capabilities::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<get_capabilities::v3::Request>,
|
||||||
) -> Result<get_capabilities::v3::Response> {
|
) -> Result<get_capabilities::v3::Response> {
|
||||||
let available: BTreeMap<RoomVersionId, RoomVersionStability> = services.server.available_room_versions().collect();
|
let available: BTreeMap<RoomVersionId, RoomVersionStability> =
|
||||||
|
services.server.available_room_versions().collect();
|
||||||
|
|
||||||
let mut capabilities = Capabilities::default();
|
let mut capabilities = Capabilities::default();
|
||||||
capabilities.room_versions = RoomVersionsCapability {
|
capabilities.room_versions = RoomVersionsCapability {
|
||||||
|
@ -28,21 +30,15 @@ pub(crate) async fn get_capabilities_route(
|
||||||
};
|
};
|
||||||
|
|
||||||
// we do not implement 3PID stuff
|
// we do not implement 3PID stuff
|
||||||
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability {
|
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability { enabled: false };
|
||||||
enabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// we dont support generating tokens yet
|
// we dont support generating tokens yet
|
||||||
capabilities.get_login_token = GetLoginTokenCapability {
|
capabilities.get_login_token = GetLoginTokenCapability { enabled: false };
|
||||||
enabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// MSC4133 capability
|
// MSC4133 capability
|
||||||
capabilities
|
capabilities
|
||||||
.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))
|
.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))
|
||||||
.expect("this is valid JSON we created");
|
.expect("this is valid JSON we created");
|
||||||
|
|
||||||
Ok(get_capabilities::v3::Response {
|
Ok(get_capabilities::v3::Response { capabilities })
|
||||||
capabilities,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,8 @@ const LIMIT_DEFAULT: usize = 10;
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||||
/// if the user was joined, depending on history_visibility)
|
/// if the user was joined, depending on history_visibility)
|
||||||
pub(crate) async fn get_context_route(
|
pub(crate) async fn get_context_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_context::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_context::v3::Request>,
|
||||||
) -> Result<get_context::v3::Response> {
|
) -> Result<get_context::v3::Response> {
|
||||||
let filter = &body.filter;
|
let filter = &body.filter;
|
||||||
let sender = body.sender();
|
let sender = body.sender();
|
||||||
|
@ -50,9 +51,8 @@ pub(crate) async fn get_context_route(
|
||||||
// members for "inline" profiles on the timeline to work properly
|
// members for "inline" profiles on the timeline to work properly
|
||||||
let lazy_load_enabled = matches!(filter.lazy_load_options, LazyLoadOptions::Enabled { .. });
|
let lazy_load_enabled = matches!(filter.lazy_load_options, LazyLoadOptions::Enabled { .. });
|
||||||
|
|
||||||
let lazy_load_redundant = if let LazyLoadOptions::Enabled {
|
let lazy_load_redundant = if let LazyLoadOptions::Enabled { include_redundant_members } =
|
||||||
include_redundant_members,
|
filter.lazy_load_options
|
||||||
} = filter.lazy_load_options
|
|
||||||
{
|
{
|
||||||
include_redundant_members
|
include_redundant_members
|
||||||
} else {
|
} else {
|
||||||
|
@ -91,10 +91,11 @@ pub(crate) async fn get_context_route(
|
||||||
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let events_before = services
|
let events_before =
|
||||||
.rooms
|
services
|
||||||
.timeline
|
.rooms
|
||||||
.pdus_rev(Some(sender_user), room_id, Some(base_token));
|
.timeline
|
||||||
|
.pdus_rev(Some(sender_user), room_id, Some(base_token));
|
||||||
|
|
||||||
let events_after = services
|
let events_after = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -166,7 +167,9 @@ pub(crate) async fn get_context_route(
|
||||||
.filter(|&user_id: &&UserId| lazy.contains(user_id))
|
.filter(|&user_id: &&UserId| lazy.contains(user_id))
|
||||||
.map(|_| event_id)
|
.map(|_| event_id)
|
||||||
})
|
})
|
||||||
.broad_filter_map(|event_id: &OwnedEventId| services.rooms.timeline.get_pdu(event_id).ok())
|
.broad_filter_map(|event_id: &OwnedEventId| {
|
||||||
|
services.rooms.timeline.get_pdu(event_id).ok()
|
||||||
|
})
|
||||||
.map(|pdu| pdu.to_state_event())
|
.map(|pdu| pdu.to_state_event())
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
|
@ -18,7 +18,8 @@ use crate::{utils, Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Get metadata on all devices of the sender user.
|
/// Get metadata on all devices of the sender user.
|
||||||
pub(crate) async fn get_devices_route(
|
pub(crate) async fn get_devices_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_devices::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_devices::v3::Request>,
|
||||||
) -> Result<get_devices::v3::Response> {
|
) -> Result<get_devices::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");
|
||||||
|
|
||||||
|
@ -28,16 +29,15 @@ pub(crate) async fn get_devices_route(
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_devices::v3::Response {
|
Ok(get_devices::v3::Response { devices })
|
||||||
devices,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
||||||
///
|
///
|
||||||
/// Get metadata on a single device of the sender user.
|
/// Get metadata on a single device of the sender user.
|
||||||
pub(crate) async fn get_device_route(
|
pub(crate) async fn get_device_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_device::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_device::v3::Request>,
|
||||||
) -> Result<get_device::v3::Response> {
|
) -> Result<get_device::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");
|
||||||
|
|
||||||
|
@ -47,9 +47,7 @@ pub(crate) async fn get_device_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
||||||
|
|
||||||
Ok(get_device::v3::Response {
|
Ok(get_device::v3::Response { device })
|
||||||
device,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||||
|
@ -57,7 +55,8 @@ pub(crate) async fn get_device_route(
|
||||||
/// Updates the metadata on a given device of the sender user.
|
/// Updates the metadata on a given device of the sender user.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
|
#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
|
||||||
pub(crate) async fn update_device_route(
|
pub(crate) async fn update_device_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<update_device::v3::Request>,
|
body: Ruma<update_device::v3::Request>,
|
||||||
) -> Result<update_device::v3::Response> {
|
) -> Result<update_device::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");
|
||||||
|
@ -93,16 +92,15 @@ pub(crate) async fn update_device_route(
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub(crate) async fn delete_device_route(
|
pub(crate) async fn delete_device_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_device::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_device::v3::Request>,
|
||||||
) -> Result<delete_device::v3::Response> {
|
) -> Result<delete_device::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 sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||||
stages: vec![AuthType::Password],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
@ -151,16 +149,15 @@ pub(crate) async fn delete_device_route(
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub(crate) async fn delete_devices_route(
|
pub(crate) async fn delete_devices_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_devices::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_devices::v3::Request>,
|
||||||
) -> Result<delete_devices::v3::Response> {
|
) -> Result<delete_devices::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 sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||||
stages: vec![AuthType::Password],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
|
|
@ -5,7 +5,10 @@ use futures::{StreamExt, TryFutureExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
directory::{get_public_rooms, get_public_rooms_filtered, get_room_visibility, set_room_visibility},
|
directory::{
|
||||||
|
get_public_rooms, get_public_rooms_filtered, get_room_visibility,
|
||||||
|
set_room_visibility,
|
||||||
|
},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
room,
|
room,
|
||||||
},
|
},
|
||||||
|
@ -32,7 +35,8 @@ use crate::Ruma;
|
||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_filtered_route(
|
pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
if let Some(server) = &body.server {
|
if let Some(server) = &body.server {
|
||||||
|
@ -57,7 +61,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
warn!(?body.server, "Failed to return /publicRooms: {e}");
|
warn!(?body.server, "Failed to return /publicRooms: {e}");
|
||||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return the requested server's public room list.")
|
Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Failed to return the requested server's public room list.",
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
|
@ -70,7 +77,8 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_route(
|
pub(crate) async fn get_public_rooms_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_public_rooms::v3::Request>,
|
body: Ruma<get_public_rooms::v3::Request>,
|
||||||
) -> Result<get_public_rooms::v3::Response> {
|
) -> Result<get_public_rooms::v3::Response> {
|
||||||
if let Some(server) = &body.server {
|
if let Some(server) = &body.server {
|
||||||
|
@ -95,7 +103,10 @@ pub(crate) async fn get_public_rooms_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
warn!(?body.server, "Failed to return /publicRooms: {e}");
|
warn!(?body.server, "Failed to return /publicRooms: {e}");
|
||||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return the requested server's public room list.")
|
Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Failed to return the requested server's public room list.",
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(get_public_rooms::v3::Response {
|
Ok(get_public_rooms::v3::Response {
|
||||||
|
@ -111,7 +122,8 @@ pub(crate) async fn get_public_rooms_route(
|
||||||
/// Sets the visibility of a given room in the room directory.
|
/// Sets the visibility of a given room in the room directory.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
||||||
pub(crate) async fn set_room_visibility_route(
|
pub(crate) async fn set_room_visibility_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<set_room_visibility::v3::Request>,
|
body: Ruma<set_room_visibility::v3::Request>,
|
||||||
) -> Result<set_room_visibility::v3::Response> {
|
) -> Result<set_room_visibility::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");
|
||||||
|
@ -139,14 +151,14 @@ pub(crate) async fn set_room_visibility_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
match &body.visibility {
|
match &body.visibility {
|
||||||
room::Visibility::Public => {
|
| room::Visibility::Public => {
|
||||||
if services.globals.config.lockdown_public_room_directory
|
if services.globals.config.lockdown_public_room_directory
|
||||||
&& !services.users.is_admin(sender_user).await
|
&& !services.users.is_admin(sender_user).await
|
||||||
&& body.appservice_info.is_none()
|
&& body.appservice_info.is_none()
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
||||||
\"lockdown_public_room_directory\" is enabled",
|
while \"lockdown_public_room_directory\" is enabled",
|
||||||
body.room_id
|
body.room_id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -154,8 +166,8 @@ pub(crate) async fn set_room_visibility_route(
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_text(&format!(
|
.send_text(&format!(
|
||||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
"Non-admin user {sender_user} tried to publish {0} to the room \
|
||||||
\"lockdown_public_room_directory\" is enabled",
|
directory while \"lockdown_public_room_directory\" is enabled",
|
||||||
body.room_id
|
body.room_id
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -172,13 +184,16 @@ pub(crate) async fn set_room_visibility_route(
|
||||||
if services.globals.config.admin_room_notices {
|
if services.globals.config.admin_room_notices {
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_text(&format!("{sender_user} made {} public to the room directory", body.room_id))
|
.send_text(&format!(
|
||||||
|
"{sender_user} made {} public to the room directory",
|
||||||
|
body.room_id
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
info!("{sender_user} made {0} public to the room directory", body.room_id);
|
info!("{sender_user} made {0} public to the room directory", body.room_id);
|
||||||
},
|
},
|
||||||
room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id),
|
| room::Visibility::Private => services.rooms.directory.set_not_public(&body.room_id),
|
||||||
_ => {
|
| _ => {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Room visibility type is not supported.",
|
"Room visibility type is not supported.",
|
||||||
|
@ -193,7 +208,8 @@ pub(crate) async fn set_room_visibility_route(
|
||||||
///
|
///
|
||||||
/// Gets the visibility of a given room in the room directory.
|
/// Gets the visibility of a given room in the room directory.
|
||||||
pub(crate) async fn get_room_visibility_route(
|
pub(crate) async fn get_room_visibility_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_room_visibility::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_room_visibility::v3::Request>,
|
||||||
) -> Result<get_room_visibility::v3::Response> {
|
) -> Result<get_room_visibility::v3::Response> {
|
||||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||||
// Return 404 if the room doesn't exist
|
// Return 404 if the room doesn't exist
|
||||||
|
@ -210,10 +226,16 @@ pub(crate) async fn get_room_visibility_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_public_rooms_filtered_helper(
|
pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
services: &Services, server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter,
|
services: &Services,
|
||||||
|
server: Option<&ServerName>,
|
||||||
|
limit: Option<UInt>,
|
||||||
|
since: Option<&str>,
|
||||||
|
filter: &Filter,
|
||||||
_network: &RoomNetwork,
|
_network: &RoomNetwork,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
if let Some(other_server) = server.filter(|server_name| !services.globals.server_is_ours(server_name)) {
|
if let Some(other_server) =
|
||||||
|
server.filter(|server_name| !services.globals.server_is_ours(server_name))
|
||||||
|
{
|
||||||
let response = services
|
let response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
|
@ -245,9 +267,10 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
if let Some(s) = &since {
|
if let Some(s) = &since {
|
||||||
let mut characters = s.chars();
|
let mut characters = s.chars();
|
||||||
let backwards = match characters.next() {
|
let backwards = match characters.next() {
|
||||||
Some('n') => false,
|
| Some('n') => false,
|
||||||
Some('p') => true,
|
| Some('p') => true,
|
||||||
_ => return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token")),
|
| _ =>
|
||||||
|
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token")),
|
||||||
};
|
};
|
||||||
|
|
||||||
num_since = characters
|
num_since = characters
|
||||||
|
@ -337,7 +360,11 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
|
|
||||||
/// Check whether the user can publish to the room directory via power levels of
|
/// Check whether the user can publish to the room directory via power levels of
|
||||||
/// room history visibility event or room creator
|
/// room history visibility event or room creator
|
||||||
async fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
async fn user_can_publish_room(
|
||||||
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
) -> Result<bool> {
|
||||||
if let Ok(event) = services
|
if let Ok(event) = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
|
@ -347,7 +374,8 @@ async fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &
|
||||||
serde_json::from_str(event.content.get())
|
serde_json::from_str(event.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
||||||
.map(|content: RoomPowerLevelsEventContent| {
|
.map(|content: RoomPowerLevelsEventContent| {
|
||||||
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
RoomPowerLevels::from(content)
|
||||||
|
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||||
})
|
})
|
||||||
} else if let Ok(event) = services
|
} else if let Ok(event) = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -406,10 +434,10 @@ async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> Public
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content(&room_id, &StateEventType::RoomJoinRules, "")
|
.room_state_get_content(&room_id, &StateEventType::RoomJoinRules, "")
|
||||||
.map_ok(|c: RoomJoinRulesEventContent| match c.join_rule {
|
.map_ok(|c: RoomJoinRulesEventContent| match c.join_rule {
|
||||||
JoinRule::Public => PublicRoomJoinRule::Public,
|
| JoinRule::Public => PublicRoomJoinRule::Public,
|
||||||
JoinRule::Knock => "knock".into(),
|
| JoinRule::Knock => "knock".into(),
|
||||||
JoinRule::KnockRestricted(_) => "knock_restricted".into(),
|
| JoinRule::KnockRestricted(_) => "knock_restricted".into(),
|
||||||
_ => "invite".into(),
|
| _ => "invite".into(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
|
|
@ -10,7 +10,8 @@ use crate::{Result, Ruma};
|
||||||
///
|
///
|
||||||
/// - A user can only access their own filters
|
/// - A user can only access their own filters
|
||||||
pub(crate) async fn get_filter_route(
|
pub(crate) async fn get_filter_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_filter::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_filter::v3::Request>,
|
||||||
) -> Result<get_filter::v3::Response> {
|
) -> 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");
|
||||||
|
|
||||||
|
@ -26,7 +27,8 @@ pub(crate) async fn get_filter_route(
|
||||||
///
|
///
|
||||||
/// Creates a new filter to be used by other endpoints.
|
/// Creates a new filter to be used by other endpoints.
|
||||||
pub(crate) async fn create_filter_route(
|
pub(crate) async fn create_filter_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_filter::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_filter::v3::Request>,
|
||||||
) -> Result<create_filter::v3::Response> {
|
) -> Result<create_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");
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,10 @@ use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
keys::{claim_keys, get_key_changes, get_keys, upload_keys, upload_signatures, upload_signing_keys},
|
keys::{
|
||||||
|
claim_keys, get_key_changes, get_keys, upload_keys, upload_signatures,
|
||||||
|
upload_signing_keys,
|
||||||
|
},
|
||||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
|
@ -31,7 +34,8 @@ use crate::{
|
||||||
/// - If there are no device keys yet: Adds device keys (TODO: merge with
|
/// - If there are no device keys yet: Adds device keys (TODO: merge with
|
||||||
/// existing keys?)
|
/// existing keys?)
|
||||||
pub(crate) async fn upload_keys_route(
|
pub(crate) async fn upload_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<upload_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<upload_keys::v3::Request>,
|
||||||
) -> Result<upload_keys::v3::Response> {
|
) -> Result<upload_keys::v3::Response> {
|
||||||
let (sender_user, sender_device) = body.sender();
|
let (sender_user, sender_device) = body.sender();
|
||||||
|
|
||||||
|
@ -75,7 +79,8 @@ pub(crate) async fn upload_keys_route(
|
||||||
/// - The master and self-signing keys contain signatures that the user is
|
/// - The master and self-signing keys contain signatures that the user is
|
||||||
/// allowed to see
|
/// allowed to see
|
||||||
pub(crate) async fn get_keys_route(
|
pub(crate) async fn get_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_keys::v3::Request>,
|
||||||
) -> Result<get_keys::v3::Response> {
|
) -> Result<get_keys::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");
|
||||||
|
|
||||||
|
@ -93,7 +98,8 @@ pub(crate) async fn get_keys_route(
|
||||||
///
|
///
|
||||||
/// Claims one-time keys
|
/// Claims one-time keys
|
||||||
pub(crate) async fn claim_keys_route(
|
pub(crate) async fn claim_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<claim_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<claim_keys::v3::Request>,
|
||||||
) -> Result<claim_keys::v3::Response> {
|
) -> Result<claim_keys::v3::Response> {
|
||||||
claim_keys_helper(&services, &body.one_time_keys).await
|
claim_keys_helper(&services, &body.one_time_keys).await
|
||||||
}
|
}
|
||||||
|
@ -104,16 +110,15 @@ pub(crate) async fn claim_keys_route(
|
||||||
///
|
///
|
||||||
/// - Requires UIAA to verify password
|
/// - Requires UIAA to verify password
|
||||||
pub(crate) async fn upload_signing_keys_route(
|
pub(crate) async fn upload_signing_keys_route(
|
||||||
State(services): State<crate::State>, body: Ruma<upload_signing_keys::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<upload_signing_keys::v3::Request>,
|
||||||
) -> Result<upload_signing_keys::v3::Response> {
|
) -> Result<upload_signing_keys::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 sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||||
stages: vec![AuthType::Password],
|
|
||||||
}],
|
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Box::default(),
|
params: Box::default(),
|
||||||
session: None,
|
session: None,
|
||||||
|
@ -161,7 +166,8 @@ pub(crate) async fn upload_signing_keys_route(
|
||||||
///
|
///
|
||||||
/// Uploads end-to-end key signatures from the sender user.
|
/// Uploads end-to-end key signatures from the sender user.
|
||||||
pub(crate) async fn upload_signatures_route(
|
pub(crate) async fn upload_signatures_route(
|
||||||
State(services): State<crate::State>, body: Ruma<upload_signatures::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<upload_signatures::v3::Request>,
|
||||||
) -> Result<upload_signatures::v3::Response> {
|
) -> Result<upload_signatures::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");
|
||||||
|
|
||||||
|
@ -174,7 +180,10 @@ pub(crate) async fn upload_signatures_route(
|
||||||
.get("signatures")
|
.get("signatures")
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Missing signatures field."))?
|
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Missing signatures field."))?
|
||||||
.get(sender_user.to_string())
|
.get(sender_user.to_string())
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid user in signatures field."))?
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Invalid user in signatures field.",
|
||||||
|
))?
|
||||||
.as_object()
|
.as_object()
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature."))?
|
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature."))?
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -185,7 +194,10 @@ pub(crate) async fn upload_signatures_route(
|
||||||
signature
|
signature
|
||||||
.1
|
.1
|
||||||
.as_str()
|
.as_str()
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Invalid signature value.",
|
||||||
|
))?
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -209,7 +221,8 @@ pub(crate) async fn upload_signatures_route(
|
||||||
///
|
///
|
||||||
/// - TODO: left users
|
/// - TODO: left users
|
||||||
pub(crate) async fn get_key_changes_route(
|
pub(crate) async fn get_key_changes_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_key_changes::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_key_changes::v3::Request>,
|
||||||
) -> Result<get_key_changes::v3::Response> {
|
) -> Result<get_key_changes::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");
|
||||||
|
|
||||||
|
@ -255,8 +268,11 @@ pub(crate) async fn get_key_changes_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_keys_helper<F>(
|
pub(crate) async fn get_keys_helper<F>(
|
||||||
services: &Services, sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
|
services: &Services,
|
||||||
allowed_signatures: F, include_display_names: bool,
|
sender_user: Option<&UserId>,
|
||||||
|
device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
|
||||||
|
allowed_signatures: F,
|
||||||
|
include_display_names: bool,
|
||||||
) -> Result<get_keys::v3::Response>
|
) -> Result<get_keys::v3::Response>
|
||||||
where
|
where
|
||||||
F: Fn(&UserId) -> bool + Send + Sync,
|
F: Fn(&UserId) -> bool + Send + Sync,
|
||||||
|
@ -289,7 +305,9 @@ where
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(user_id, device_id)
|
.get_device_metadata(user_id, device_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Database("all_device_keys contained nonexistent device.")))?;
|
.map_err(|_| {
|
||||||
|
err!(Database("all_device_keys contained nonexistent device."))
|
||||||
|
})?;
|
||||||
|
|
||||||
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
|
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
|
||||||
.map_err(|_| err!(Database("invalid device keys in database")))?;
|
.map_err(|_| err!(Database("invalid device keys in database")))?;
|
||||||
|
@ -307,7 +325,11 @@ where
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(user_id, device_id)
|
.get_device_metadata(user_id, device_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(InvalidParam("Tried to get keys for nonexistent device."))))?;
|
.map_err(|_| {
|
||||||
|
err!(Request(InvalidParam(
|
||||||
|
"Tried to get keys for nonexistent device."
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
|
||||||
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
|
add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
|
||||||
.map_err(|_| err!(Database("invalid device keys in database")))?;
|
.map_err(|_| err!(Database("invalid device keys in database")))?;
|
||||||
|
@ -350,9 +372,8 @@ where
|
||||||
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = federation::keys::get_keys::v1::Request {
|
let request =
|
||||||
device_keys: device_keys_input_fed,
|
federation::keys::get_keys::v1::Request { device_keys: device_keys_input_fed };
|
||||||
};
|
|
||||||
|
|
||||||
let response = services
|
let response = services
|
||||||
.sending
|
.sending
|
||||||
|
@ -382,8 +403,8 @@ where
|
||||||
.users
|
.users
|
||||||
.add_cross_signing_keys(
|
.add_cross_signing_keys(
|
||||||
&user, &raw, &None, &None,
|
&user, &raw, &None, &None,
|
||||||
false, /* Dont notify. A notification would trigger another key request resulting in an
|
false, /* Dont notify. A notification would trigger another key request
|
||||||
* endless loop */
|
* resulting in an endless loop */
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
master_keys.insert(user.clone(), raw);
|
master_keys.insert(user.clone(), raw);
|
||||||
|
@ -406,7 +427,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_unsigned_device_display_name(
|
fn add_unsigned_device_display_name(
|
||||||
keys: &mut Raw<ruma::encryption::DeviceKeys>, metadata: ruma::api::client::device::Device,
|
keys: &mut Raw<ruma::encryption::DeviceKeys>,
|
||||||
|
metadata: ruma::api::client::device::Device,
|
||||||
include_display_names: bool,
|
include_display_names: bool,
|
||||||
) -> serde_json::Result<()> {
|
) -> serde_json::Result<()> {
|
||||||
if let Some(display_name) = metadata.display_name {
|
if let Some(display_name) = metadata.display_name {
|
||||||
|
@ -431,7 +453,8 @@ fn add_unsigned_device_display_name(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn claim_keys_helper(
|
pub(crate) async fn claim_keys_helper(
|
||||||
services: &Services, one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, OneTimeKeyAlgorithm>>,
|
services: &Services,
|
||||||
|
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, OneTimeKeyAlgorithm>>,
|
||||||
) -> Result<claim_keys::v3::Response> {
|
) -> Result<claim_keys::v3::Response> {
|
||||||
let mut one_time_keys = BTreeMap::new();
|
let mut one_time_keys = BTreeMap::new();
|
||||||
|
|
||||||
|
@ -473,12 +496,9 @@ pub(crate) async fn claim_keys_helper(
|
||||||
server,
|
server,
|
||||||
services
|
services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(server, federation::keys::claim_keys::v1::Request {
|
||||||
server,
|
one_time_keys: one_time_keys_input_fed,
|
||||||
federation::keys::claim_keys::v1::Request {
|
})
|
||||||
one_time_keys: one_time_keys_input_fed,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await,
|
.await,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -486,17 +506,14 @@ pub(crate) async fn claim_keys_helper(
|
||||||
|
|
||||||
while let Some((server, response)) = futures.next().await {
|
while let Some((server, response)) = futures.next().await {
|
||||||
match response {
|
match response {
|
||||||
Ok(keys) => {
|
| Ok(keys) => {
|
||||||
one_time_keys.extend(keys.one_time_keys);
|
one_time_keys.extend(keys.one_time_keys);
|
||||||
},
|
},
|
||||||
Err(_e) => {
|
| Err(_e) => {
|
||||||
failures.insert(server.to_string(), json!({}));
|
failures.insert(server.to_string(), json!({}));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(claim_keys::v3::Response {
|
Ok(claim_keys::v3::Response { failures, one_time_keys })
|
||||||
failures,
|
|
||||||
one_time_keys,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@ use reqwest::Url;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
authenticated_media::{
|
authenticated_media::{
|
||||||
get_content, get_content_as_filename, get_content_thumbnail, get_media_config, get_media_preview,
|
get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
|
||||||
|
get_media_preview,
|
||||||
},
|
},
|
||||||
media::create_content,
|
media::create_content,
|
||||||
},
|
},
|
||||||
|
@ -26,7 +27,8 @@ use crate::Ruma;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v1/media/config`
|
/// # `GET /_matrix/client/v1/media/config`
|
||||||
pub(crate) async fn get_media_config_route(
|
pub(crate) async fn get_media_config_route(
|
||||||
State(services): State<crate::State>, _body: Ruma<get_media_config::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<get_media_config::v1::Request>,
|
||||||
) -> Result<get_media_config::v1::Response> {
|
) -> Result<get_media_config::v1::Response> {
|
||||||
Ok(get_media_config::v1::Response {
|
Ok(get_media_config::v1::Response {
|
||||||
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||||
|
@ -46,7 +48,8 @@ pub(crate) async fn get_media_config_route(
|
||||||
fields(%client),
|
fields(%client),
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn create_content_route(
|
pub(crate) async fn create_content_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<create_content::v3::Response> {
|
) -> Result<create_content::v3::Response> {
|
||||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
@ -79,7 +82,8 @@ pub(crate) async fn create_content_route(
|
||||||
fields(%client),
|
fields(%client),
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn get_content_thumbnail_route(
|
pub(crate) async fn get_content_thumbnail_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||||
) -> Result<get_content_thumbnail::v1::Response> {
|
) -> Result<get_content_thumbnail::v1::Response> {
|
||||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
@ -115,7 +119,8 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||||
fields(%client),
|
fields(%client),
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn get_content_route(
|
pub(crate) async fn get_content_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content::v1::Request>,
|
body: Ruma<get_content::v1::Request>,
|
||||||
) -> Result<get_content::v1::Response> {
|
) -> Result<get_content::v1::Response> {
|
||||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
@ -150,7 +155,8 @@ pub(crate) async fn get_content_route(
|
||||||
fields(%client),
|
fields(%client),
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn get_content_as_filename_route(
|
pub(crate) async fn get_content_as_filename_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_as_filename::v1::Request>,
|
body: Ruma<get_content_as_filename::v1::Request>,
|
||||||
) -> Result<get_content_as_filename::v1::Response> {
|
) -> Result<get_content_as_filename::v1::Response> {
|
||||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
@ -185,7 +191,8 @@ pub(crate) async fn get_content_as_filename_route(
|
||||||
fields(%client),
|
fields(%client),
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn get_media_preview_route(
|
pub(crate) async fn get_media_preview_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_media_preview::v1::Request>,
|
body: Ruma<get_media_preview::v1::Request>,
|
||||||
) -> Result<get_media_preview::v1::Response> {
|
) -> Result<get_media_preview::v1::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");
|
||||||
|
@ -223,7 +230,11 @@ pub(crate) async fn get_media_preview_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_thumbnail(
|
async fn fetch_thumbnail(
|
||||||
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, dim: &Dim,
|
services: &Services,
|
||||||
|
mxc: &Mxc<'_>,
|
||||||
|
user: &UserId,
|
||||||
|
timeout_ms: Duration,
|
||||||
|
dim: &Dim,
|
||||||
) -> Result<FileMeta> {
|
) -> Result<FileMeta> {
|
||||||
let FileMeta {
|
let FileMeta {
|
||||||
content,
|
content,
|
||||||
|
@ -245,7 +256,11 @@ async fn fetch_thumbnail(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_file(
|
async fn fetch_file(
|
||||||
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, filename: Option<&str>,
|
services: &Services,
|
||||||
|
mxc: &Mxc<'_>,
|
||||||
|
user: &UserId,
|
||||||
|
timeout_ms: Duration,
|
||||||
|
filename: Option<&str>,
|
||||||
) -> Result<FileMeta> {
|
) -> Result<FileMeta> {
|
||||||
let FileMeta {
|
let FileMeta {
|
||||||
content,
|
content,
|
||||||
|
@ -267,7 +282,11 @@ async fn fetch_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_thumbnail_meta(
|
async fn fetch_thumbnail_meta(
|
||||||
services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration, dim: &Dim,
|
services: &Services,
|
||||||
|
mxc: &Mxc<'_>,
|
||||||
|
user: &UserId,
|
||||||
|
timeout_ms: Duration,
|
||||||
|
dim: &Dim,
|
||||||
) -> Result<FileMeta> {
|
) -> Result<FileMeta> {
|
||||||
if let Some(filemeta) = services.media.get_thumbnail(mxc, dim).await? {
|
if let Some(filemeta) = services.media.get_thumbnail(mxc, dim).await? {
|
||||||
return Ok(filemeta);
|
return Ok(filemeta);
|
||||||
|
@ -283,7 +302,12 @@ async fn fetch_thumbnail_meta(
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_file_meta(services: &Services, mxc: &Mxc<'_>, user: &UserId, timeout_ms: Duration) -> Result<FileMeta> {
|
async fn fetch_file_meta(
|
||||||
|
services: &Services,
|
||||||
|
mxc: &Mxc<'_>,
|
||||||
|
user: &UserId,
|
||||||
|
timeout_ms: Duration,
|
||||||
|
) -> Result<FileMeta> {
|
||||||
if let Some(filemeta) = services.media.get(mxc).await? {
|
if let Some(filemeta) = services.media.get(mxc).await? {
|
||||||
return Ok(filemeta);
|
return Ok(filemeta);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ use conduwuit_service::media::{Dim, FileMeta, CACHE_CONTROL_IMMUTABLE, CORP_CROS
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::media::{
|
api::client::media::{
|
||||||
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
|
create_content, get_content, get_content_as_filename, get_content_thumbnail,
|
||||||
get_media_preview,
|
get_media_config, get_media_preview,
|
||||||
},
|
},
|
||||||
Mxc,
|
Mxc,
|
||||||
};
|
};
|
||||||
|
@ -23,7 +23,8 @@ use crate::{client::create_content_route, Ruma, RumaResponse};
|
||||||
///
|
///
|
||||||
/// Returns max upload size.
|
/// Returns max upload size.
|
||||||
pub(crate) async fn get_media_config_legacy_route(
|
pub(crate) async fn get_media_config_legacy_route(
|
||||||
State(services): State<crate::State>, _body: Ruma<get_media_config::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<get_media_config::v3::Request>,
|
||||||
) -> Result<get_media_config::v3::Response> {
|
) -> Result<get_media_config::v3::Response> {
|
||||||
Ok(get_media_config::v3::Response {
|
Ok(get_media_config::v3::Response {
|
||||||
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||||
|
@ -38,7 +39,8 @@ pub(crate) async fn get_media_config_legacy_route(
|
||||||
///
|
///
|
||||||
/// Returns max upload size.
|
/// Returns max upload size.
|
||||||
pub(crate) async fn get_media_config_legacy_legacy_route(
|
pub(crate) async fn get_media_config_legacy_legacy_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_media_config::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_media_config::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
||||||
get_media_config_legacy_route(State(services), body)
|
get_media_config_legacy_route(State(services), body)
|
||||||
.await
|
.await
|
||||||
|
@ -50,7 +52,8 @@ pub(crate) async fn get_media_config_legacy_legacy_route(
|
||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy")]
|
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy")]
|
||||||
pub(crate) async fn get_media_preview_legacy_route(
|
pub(crate) async fn get_media_preview_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_media_preview::v3::Request>,
|
body: Ruma<get_media_preview::v3::Request>,
|
||||||
) -> Result<get_media_preview::v3::Response> {
|
) -> Result<get_media_preview::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");
|
||||||
|
@ -91,7 +94,8 @@ pub(crate) async fn get_media_preview_legacy_route(
|
||||||
///
|
///
|
||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
pub(crate) async fn get_media_preview_legacy_legacy_route(
|
pub(crate) async fn get_media_preview_legacy_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_media_preview::v3::Request>,
|
body: Ruma<get_media_preview::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||||
get_media_preview_legacy_route(State(services), InsecureClientIp(client), body)
|
get_media_preview_legacy_route(State(services), InsecureClientIp(client), body)
|
||||||
|
@ -110,7 +114,8 @@ pub(crate) async fn get_media_preview_legacy_legacy_route(
|
||||||
/// - Some metadata will be saved in the database
|
/// - Some metadata will be saved in the database
|
||||||
/// - Media will be saved in the media/ directory
|
/// - Media will be saved in the media/ directory
|
||||||
pub(crate) async fn create_content_legacy_route(
|
pub(crate) async fn create_content_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||||
create_content_route(State(services), InsecureClientIp(client), body)
|
create_content_route(State(services), InsecureClientIp(client), body)
|
||||||
|
@ -128,7 +133,8 @@ pub(crate) async fn create_content_legacy_route(
|
||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||||
pub(crate) async fn get_content_legacy_route(
|
pub(crate) async fn get_content_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content::v3::Request>,
|
body: Ruma<get_content::v3::Request>,
|
||||||
) -> Result<get_content::v3::Response> {
|
) -> Result<get_content::v3::Response> {
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
|
@ -142,7 +148,8 @@ pub(crate) async fn get_content_legacy_route(
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services.media.get(&mxc).await?
|
}) = services.media.get(&mxc).await?
|
||||||
{
|
{
|
||||||
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
let content_disposition =
|
||||||
|
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
||||||
|
|
||||||
Ok(get_content::v3::Response {
|
Ok(get_content::v3::Response {
|
||||||
file: content.expect("entire file contents"),
|
file: content.expect("entire file contents"),
|
||||||
|
@ -156,10 +163,15 @@ pub(crate) async fn get_content_legacy_route(
|
||||||
.media
|
.media
|
||||||
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
|
.map_err(|e| {
|
||||||
|
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
let content_disposition =
|
let content_disposition = make_content_disposition(
|
||||||
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
|
response.content_disposition.as_ref(),
|
||||||
|
response.content_type.as_deref(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(get_content::v3::Response {
|
Ok(get_content::v3::Response {
|
||||||
file: response.file,
|
file: response.file,
|
||||||
|
@ -187,7 +199,8 @@ pub(crate) async fn get_content_legacy_route(
|
||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||||
pub(crate) async fn get_content_legacy_legacy_route(
|
pub(crate) async fn get_content_legacy_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content::v3::Request>,
|
body: Ruma<get_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||||
get_content_legacy_route(State(services), InsecureClientIp(client), body)
|
get_content_legacy_route(State(services), InsecureClientIp(client), body)
|
||||||
|
@ -205,7 +218,8 @@ pub(crate) async fn get_content_legacy_legacy_route(
|
||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||||
pub(crate) async fn get_content_as_filename_legacy_route(
|
pub(crate) async fn get_content_as_filename_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_as_filename::v3::Request>,
|
body: Ruma<get_content_as_filename::v3::Request>,
|
||||||
) -> Result<get_content_as_filename::v3::Response> {
|
) -> Result<get_content_as_filename::v3::Response> {
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
|
@ -219,8 +233,11 @@ pub(crate) async fn get_content_as_filename_legacy_route(
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services.media.get(&mxc).await?
|
}) = services.media.get(&mxc).await?
|
||||||
{
|
{
|
||||||
let content_disposition =
|
let content_disposition = make_content_disposition(
|
||||||
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), Some(&body.filename));
|
content_disposition.as_ref(),
|
||||||
|
content_type.as_deref(),
|
||||||
|
Some(&body.filename),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(get_content_as_filename::v3::Response {
|
Ok(get_content_as_filename::v3::Response {
|
||||||
file: content.expect("entire file contents"),
|
file: content.expect("entire file contents"),
|
||||||
|
@ -234,10 +251,15 @@ pub(crate) async fn get_content_as_filename_legacy_route(
|
||||||
.media
|
.media
|
||||||
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
.fetch_remote_content_legacy(&mxc, body.allow_redirect, body.timeout_ms)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
|
.map_err(|e| {
|
||||||
|
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
let content_disposition =
|
let content_disposition = make_content_disposition(
|
||||||
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
|
response.content_disposition.as_ref(),
|
||||||
|
response.content_type.as_deref(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(get_content_as_filename::v3::Response {
|
Ok(get_content_as_filename::v3::Response {
|
||||||
content_disposition: Some(content_disposition),
|
content_disposition: Some(content_disposition),
|
||||||
|
@ -264,7 +286,8 @@ pub(crate) async fn get_content_as_filename_legacy_route(
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_as_filename::v3::Request>,
|
body: Ruma<get_content_as_filename::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||||
get_content_as_filename_legacy_route(State(services), InsecureClientIp(client), body)
|
get_content_as_filename_legacy_route(State(services), InsecureClientIp(client), body)
|
||||||
|
@ -282,7 +305,8 @@ pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy")]
|
||||||
pub(crate) async fn get_content_thumbnail_legacy_route(
|
pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||||
) -> Result<get_content_thumbnail::v3::Response> {
|
) -> Result<get_content_thumbnail::v3::Response> {
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
|
@ -297,7 +321,8 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||||
content_disposition,
|
content_disposition,
|
||||||
}) = services.media.get_thumbnail(&mxc, &dim).await?
|
}) = services.media.get_thumbnail(&mxc, &dim).await?
|
||||||
{
|
{
|
||||||
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
let content_disposition =
|
||||||
|
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
||||||
|
|
||||||
Ok(get_content_thumbnail::v3::Response {
|
Ok(get_content_thumbnail::v3::Response {
|
||||||
file: content.expect("entire file contents"),
|
file: content.expect("entire file contents"),
|
||||||
|
@ -311,10 +336,15 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||||
.media
|
.media
|
||||||
.fetch_remote_thumbnail_legacy(&body)
|
.fetch_remote_thumbnail_legacy(&body)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}")))))?;
|
.map_err(|e| {
|
||||||
|
err!(Request(NotFound(debug_warn!(%mxc, "Fetching media failed: {e:?}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
let content_disposition =
|
let content_disposition = make_content_disposition(
|
||||||
make_content_disposition(response.content_disposition.as_ref(), response.content_type.as_deref(), None);
|
response.content_disposition.as_ref(),
|
||||||
|
response.content_type.as_deref(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(get_content_thumbnail::v3::Response {
|
Ok(get_content_thumbnail::v3::Response {
|
||||||
file: response.file,
|
file: response.file,
|
||||||
|
@ -341,7 +371,8 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||||
/// seconds
|
/// seconds
|
||||||
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
|
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||||
get_content_thumbnail_legacy_route(State(services), InsecureClientIp(client), body)
|
get_content_thumbnail_legacy_route(State(services), InsecureClientIp(client), body)
|
||||||
|
|
|
@ -20,7 +20,8 @@ use ruma::{
|
||||||
client::{
|
client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
membership::{
|
membership::{
|
||||||
ban_user, forget_room, get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias,
|
ban_user, forget_room, get_member_events, invite_user, join_room_by_id,
|
||||||
|
join_room_by_id_or_alias,
|
||||||
joined_members::{self, v3::RoomMember},
|
joined_members::{self, v3::RoomMember},
|
||||||
joined_rooms, kick_user, leave_room, unban_user, ThirdPartySigned,
|
joined_rooms, kick_user, leave_room, unban_user, ThirdPartySigned,
|
||||||
},
|
},
|
||||||
|
@ -36,8 +37,8 @@ use ruma::{
|
||||||
},
|
},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
},
|
},
|
||||||
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
|
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName,
|
||||||
RoomVersionId, ServerName, UserId,
|
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use service::{
|
use service::{
|
||||||
appservice::RegistrationInfo,
|
appservice::RegistrationInfo,
|
||||||
|
@ -54,7 +55,10 @@ use crate::{client::full_user_deactivate, Ruma};
|
||||||
/// enabled
|
/// enabled
|
||||||
#[tracing::instrument(skip(services))]
|
#[tracing::instrument(skip(services))]
|
||||||
async fn banned_room_check(
|
async fn banned_room_check(
|
||||||
services: &Services, user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>,
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: Option<&RoomId>,
|
||||||
|
server_name: Option<&ServerName>,
|
||||||
client_ip: IpAddr,
|
client_ip: IpAddr,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if services.users.is_admin(user_id).await {
|
if services.users.is_admin(user_id).await {
|
||||||
|
@ -70,19 +74,21 @@ async fn banned_room_check(
|
||||||
.contains(&room_id.server_name().unwrap().to_owned())
|
.contains(&room_id.server_name().unwrap().to_owned())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
|
"User {user_id} who is not an admin attempted to send an invite for or \
|
||||||
room or banned room server name: {room_id}"
|
attempted to join a banned room or banned room server name: {room_id}"
|
||||||
);
|
);
|
||||||
|
|
||||||
if services.globals.config.auto_deactivate_banned_room_attempts {
|
if services.globals.config.auto_deactivate_banned_room_attempts {
|
||||||
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
warn!(
|
||||||
|
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||||
|
);
|
||||||
|
|
||||||
if services.globals.config.admin_room_notices {
|
if services.globals.config.admin_room_notices {
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"Automatically deactivating user {user_id} due to attempted banned room join from IP \
|
"Automatically deactivating user {user_id} due to attempted banned \
|
||||||
{client_ip}"
|
room join from IP {client_ip}"
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -109,19 +115,21 @@ async fn banned_room_check(
|
||||||
.contains(&server_name.to_owned())
|
.contains(&server_name.to_owned())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} that \
|
"User {user_id} who is not an admin tried joining a room which has the server \
|
||||||
is globally forbidden. Rejecting.",
|
name {server_name} that is globally forbidden. Rejecting.",
|
||||||
);
|
);
|
||||||
|
|
||||||
if services.globals.config.auto_deactivate_banned_room_attempts {
|
if services.globals.config.auto_deactivate_banned_room_attempts {
|
||||||
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
warn!(
|
||||||
|
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||||
|
);
|
||||||
|
|
||||||
if services.globals.config.admin_room_notices {
|
if services.globals.config.admin_room_notices {
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
"Automatically deactivating user {user_id} due to attempted banned room join from IP \
|
"Automatically deactivating user {user_id} due to attempted banned \
|
||||||
{client_ip}"
|
room join from IP {client_ip}"
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -155,12 +163,20 @@ async fn banned_room_check(
|
||||||
/// federation
|
/// federation
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
||||||
pub(crate) async fn join_room_by_id_route(
|
pub(crate) async fn join_room_by_id_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<join_room_by_id::v3::Request>,
|
body: Ruma<join_room_by_id::v3::Request>,
|
||||||
) -> Result<join_room_by_id::v3::Response> {
|
) -> Result<join_room_by_id::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name(), client).await?;
|
banned_room_check(
|
||||||
|
&services,
|
||||||
|
sender_user,
|
||||||
|
Some(&body.room_id),
|
||||||
|
body.room_id.server_name(),
|
||||||
|
client,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// There is no body.server_name for /roomId/join
|
// There is no body.server_name for /roomId/join
|
||||||
let mut servers: Vec<_> = services
|
let mut servers: Vec<_> = services
|
||||||
|
@ -216,7 +232,8 @@ pub(crate) async fn join_room_by_id_route(
|
||||||
/// via room alias server name and room ID server name
|
/// via room alias server name and room ID server name
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
||||||
pub(crate) async fn join_room_by_id_or_alias_route(
|
pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
|
||||||
|
@ -224,8 +241,15 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
|
||||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
||||||
Ok(room_id) => {
|
| Ok(room_id) => {
|
||||||
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name(), client).await?;
|
banned_room_check(
|
||||||
|
&services,
|
||||||
|
sender_user,
|
||||||
|
Some(&room_id),
|
||||||
|
room_id.server_name(),
|
||||||
|
client,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let mut servers = body.via.clone();
|
let mut servers = body.via.clone();
|
||||||
servers.extend(
|
servers.extend(
|
||||||
|
@ -261,14 +285,21 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
|
|
||||||
(servers, room_id)
|
(servers, room_id)
|
||||||
},
|
},
|
||||||
Err(room_alias) => {
|
| Err(room_alias) => {
|
||||||
let (room_id, mut servers) = services
|
let (room_id, mut servers) = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
.resolve_alias(&room_alias, Some(body.via.clone()))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
banned_room_check(&services, sender_user, Some(&room_id), Some(room_alias.server_name()), client).await?;
|
banned_room_check(
|
||||||
|
&services,
|
||||||
|
sender_user,
|
||||||
|
Some(&room_id),
|
||||||
|
Some(room_alias.server_name()),
|
||||||
|
client,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let addl_via_servers = services
|
let addl_via_servers = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -314,9 +345,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
.boxed()
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(join_room_by_id_or_alias::v3::Response {
|
Ok(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id })
|
||||||
room_id: join_room_response.room_id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/leave`
|
/// # `POST /_matrix/client/v3/rooms/{roomId}/leave`
|
||||||
|
@ -325,7 +354,8 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
///
|
///
|
||||||
/// - This should always work if the user is currently joined.
|
/// - This should always work if the user is currently joined.
|
||||||
pub(crate) async fn leave_room_route(
|
pub(crate) async fn leave_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<leave_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<leave_room::v3::Request>,
|
||||||
) -> Result<leave_room::v3::Response> {
|
) -> Result<leave_room::v3::Response> {
|
||||||
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone()).await?;
|
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone()).await?;
|
||||||
|
|
||||||
|
@ -337,7 +367,8 @@ pub(crate) async fn leave_room_route(
|
||||||
/// Tries to send an invite event into the room.
|
/// Tries to send an invite event into the room.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
||||||
pub(crate) async fn invite_user_route(
|
pub(crate) async fn invite_user_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<invite_user::v3::Request>,
|
body: Ruma<invite_user::v3::Request>,
|
||||||
) -> Result<invite_user::v3::Response> {
|
) -> Result<invite_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
@ -350,12 +381,16 @@ pub(crate) async fn invite_user_route(
|
||||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name(), client).await?;
|
banned_room_check(
|
||||||
|
&services,
|
||||||
|
sender_user,
|
||||||
|
Some(&body.room_id),
|
||||||
|
body.room_id.server_name(),
|
||||||
|
client,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if let invite_user::v3::InvitationRecipient::UserId {
|
if let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient {
|
||||||
user_id,
|
|
||||||
} = &body.recipient
|
|
||||||
{
|
|
||||||
let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id);
|
let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id);
|
||||||
let recipient_ignored_by_sender = services.users.user_is_ignored(user_id, sender_user);
|
let recipient_ignored_by_sender = services.users.user_is_ignored(user_id, sender_user);
|
||||||
|
|
||||||
|
@ -363,7 +398,9 @@ pub(crate) async fn invite_user_route(
|
||||||
join!(sender_ignored_recipient, recipient_ignored_by_sender);
|
join!(sender_ignored_recipient, recipient_ignored_by_sender);
|
||||||
|
|
||||||
if sender_ignored_recipient {
|
if sender_ignored_recipient {
|
||||||
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
|
return Err!(Request(Forbidden(
|
||||||
|
"You cannot invite users you have ignored to rooms."
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if recipient_ignored_by_sender {
|
if recipient_ignored_by_sender {
|
||||||
|
@ -386,7 +423,8 @@ pub(crate) async fn invite_user_route(
|
||||||
///
|
///
|
||||||
/// Tries to send a kick event into the room.
|
/// Tries to send a kick event into the room.
|
||||||
pub(crate) async fn kick_user_route(
|
pub(crate) async fn kick_user_route(
|
||||||
State(services): State<crate::State>, body: Ruma<kick_user::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<kick_user::v3::Request>,
|
||||||
) -> Result<kick_user::v3::Response> {
|
) -> Result<kick_user::v3::Response> {
|
||||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
|
|
||||||
|
@ -405,17 +443,14 @@ pub(crate) async fn kick_user_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||||
body.user_id.to_string(),
|
membership: MembershipState::Leave,
|
||||||
&RoomMemberEventContent {
|
reason: body.reason.clone(),
|
||||||
membership: MembershipState::Leave,
|
is_direct: None,
|
||||||
reason: body.reason.clone(),
|
join_authorized_via_users_server: None,
|
||||||
is_direct: None,
|
third_party_invite: None,
|
||||||
join_authorized_via_users_server: None,
|
..event
|
||||||
third_party_invite: None,
|
}),
|
||||||
..event
|
|
||||||
},
|
|
||||||
),
|
|
||||||
body.sender_user(),
|
body.sender_user(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -431,7 +466,8 @@ pub(crate) async fn kick_user_route(
|
||||||
///
|
///
|
||||||
/// Tries to send a ban event into the room.
|
/// Tries to send a ban event into the room.
|
||||||
pub(crate) async fn ban_user_route(
|
pub(crate) async fn ban_user_route(
|
||||||
State(services): State<crate::State>, body: Ruma<ban_user::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<ban_user::v3::Request>,
|
||||||
) -> Result<ban_user::v3::Response> {
|
) -> Result<ban_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -452,19 +488,16 @@ pub(crate) async fn ban_user_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||||
body.user_id.to_string(),
|
membership: MembershipState::Ban,
|
||||||
&RoomMemberEventContent {
|
reason: body.reason.clone(),
|
||||||
membership: MembershipState::Ban,
|
displayname: None, // display name may be offensive
|
||||||
reason: body.reason.clone(),
|
avatar_url: None, // avatar may be offensive
|
||||||
displayname: None, // display name may be offensive
|
is_direct: None,
|
||||||
avatar_url: None, // avatar may be offensive
|
join_authorized_via_users_server: None,
|
||||||
is_direct: None,
|
third_party_invite: None,
|
||||||
join_authorized_via_users_server: None,
|
..current_member_content
|
||||||
third_party_invite: None,
|
}),
|
||||||
..current_member_content
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -480,7 +513,8 @@ pub(crate) async fn ban_user_route(
|
||||||
///
|
///
|
||||||
/// Tries to send an unban event into the room.
|
/// Tries to send an unban event into the room.
|
||||||
pub(crate) async fn unban_user_route(
|
pub(crate) async fn unban_user_route(
|
||||||
State(services): State<crate::State>, body: Ruma<unban_user::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<unban_user::v3::Request>,
|
||||||
) -> Result<unban_user::v3::Response> {
|
) -> Result<unban_user::v3::Response> {
|
||||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
|
|
||||||
|
@ -502,17 +536,14 @@ pub(crate) async fn unban_user_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||||
body.user_id.to_string(),
|
membership: MembershipState::Leave,
|
||||||
&RoomMemberEventContent {
|
reason: body.reason.clone(),
|
||||||
membership: MembershipState::Leave,
|
join_authorized_via_users_server: None,
|
||||||
reason: body.reason.clone(),
|
third_party_invite: None,
|
||||||
join_authorized_via_users_server: None,
|
is_direct: None,
|
||||||
third_party_invite: None,
|
..current_member_content
|
||||||
is_direct: None,
|
}),
|
||||||
..current_member_content
|
|
||||||
},
|
|
||||||
),
|
|
||||||
body.sender_user(),
|
body.sender_user(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -534,7 +565,8 @@ pub(crate) async fn unban_user_route(
|
||||||
/// Note: Other devices of the user have no way of knowing the room was
|
/// Note: Other devices of the user have no way of knowing the room was
|
||||||
/// forgotten, so this has to be called from every device
|
/// forgotten, so this has to be called from every device
|
||||||
pub(crate) async fn forget_room_route(
|
pub(crate) async fn forget_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<forget_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<forget_room::v3::Request>,
|
||||||
) -> Result<forget_room::v3::Response> {
|
) -> Result<forget_room::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -559,7 +591,8 @@ pub(crate) async fn forget_room_route(
|
||||||
///
|
///
|
||||||
/// Lists all rooms the user has joined.
|
/// Lists all rooms the user has joined.
|
||||||
pub(crate) async fn joined_rooms_route(
|
pub(crate) async fn joined_rooms_route(
|
||||||
State(services): State<crate::State>, body: Ruma<joined_rooms::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<joined_rooms::v3::Request>,
|
||||||
) -> Result<joined_rooms::v3::Response> {
|
) -> Result<joined_rooms::v3::Response> {
|
||||||
Ok(joined_rooms::v3::Response {
|
Ok(joined_rooms::v3::Response {
|
||||||
joined_rooms: services
|
joined_rooms: services
|
||||||
|
@ -579,7 +612,8 @@ pub(crate) async fn joined_rooms_route(
|
||||||
///
|
///
|
||||||
/// - Only works if the user is currently joined
|
/// - Only works if the user is currently joined
|
||||||
pub(crate) async fn get_member_events_route(
|
pub(crate) async fn get_member_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_member_events::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_member_events::v3::Request>,
|
||||||
) -> Result<get_member_events::v3::Response> {
|
) -> Result<get_member_events::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -612,7 +646,8 @@ pub(crate) async fn get_member_events_route(
|
||||||
/// - The sender user must be in the room
|
/// - The sender user must be in the room
|
||||||
/// - TODO: An appservice just needs a puppet joined
|
/// - TODO: An appservice just needs a puppet joined
|
||||||
pub(crate) async fn joined_members_route(
|
pub(crate) async fn joined_members_route(
|
||||||
State(services): State<crate::State>, body: Ruma<joined_members::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<joined_members::v3::Request>,
|
||||||
) -> Result<joined_members::v3::Response> {
|
) -> Result<joined_members::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -631,25 +666,25 @@ pub(crate) async fn joined_members_route(
|
||||||
.room_members(&body.room_id)
|
.room_members(&body.room_id)
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.then(|user| async move {
|
.then(|user| async move {
|
||||||
(
|
(user.clone(), RoomMember {
|
||||||
user.clone(),
|
display_name: services.users.displayname(&user).await.ok(),
|
||||||
RoomMember {
|
avatar_url: services.users.avatar_url(&user).await.ok(),
|
||||||
display_name: services.users.displayname(&user).await.ok(),
|
})
|
||||||
avatar_url: services.users.avatar_url(&user).await.ok(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(joined_members::v3::Response {
|
Ok(joined_members::v3::Response { joined })
|
||||||
joined,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join_room_by_id_helper(
|
pub async fn join_room_by_id_helper(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
services: &Services,
|
||||||
third_party_signed: Option<&ThirdPartySigned>, appservice_info: &Option<RegistrationInfo>,
|
sender_user: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
reason: Option<String>,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
|
third_party_signed: Option<&ThirdPartySigned>,
|
||||||
|
appservice_info: &Option<RegistrationInfo>,
|
||||||
) -> Result<join_room_by_id::v3::Response> {
|
) -> Result<join_room_by_id::v3::Response> {
|
||||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||||
|
|
||||||
|
@ -671,9 +706,7 @@ pub async fn join_room_by_id_helper(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
debug_warn!("{sender_user} is already joined in {room_id}");
|
debug_warn!("{sender_user} is already joined in {room_id}");
|
||||||
return Ok(join_room_by_id::v3::Response {
|
return Ok(join_room_by_id::v3::Response { room_id: room_id.into() });
|
||||||
room_id: room_id.into(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(membership) = services
|
if let Ok(membership) = services
|
||||||
|
@ -694,18 +727,35 @@ pub async fn join_room_by_id_helper(
|
||||||
.server_in_room(services.globals.server_name(), room_id)
|
.server_in_room(services.globals.server_name(), room_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let local_join =
|
let local_join = server_in_room
|
||||||
server_in_room || servers.is_empty() || (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
|| servers.is_empty()
|
||||||
|
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
||||||
|
|
||||||
if local_join {
|
if local_join {
|
||||||
join_room_by_id_helper_local(services, sender_user, room_id, reason, servers, third_party_signed, state_lock)
|
join_room_by_id_helper_local(
|
||||||
.boxed()
|
services,
|
||||||
.await?;
|
sender_user,
|
||||||
|
room_id,
|
||||||
|
reason,
|
||||||
|
servers,
|
||||||
|
third_party_signed,
|
||||||
|
state_lock,
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
// Ask a remote server if we are not participating in this room
|
// Ask a remote server if we are not participating in this room
|
||||||
join_room_by_id_helper_remote(services, sender_user, room_id, reason, servers, third_party_signed, state_lock)
|
join_room_by_id_helper_remote(
|
||||||
.boxed()
|
services,
|
||||||
.await?;
|
sender_user,
|
||||||
|
room_id,
|
||||||
|
reason,
|
||||||
|
servers,
|
||||||
|
third_party_signed,
|
||||||
|
state_lock,
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
||||||
|
@ -713,12 +763,18 @@ pub async fn join_room_by_id_helper(
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote")]
|
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote")]
|
||||||
async fn join_room_by_id_helper_remote(
|
async fn join_room_by_id_helper_remote(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
services: &Services,
|
||||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
|
sender_user: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
reason: Option<String>,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
|
_third_party_signed: Option<&ThirdPartySigned>,
|
||||||
|
state_lock: RoomMutexGuard,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
info!("Joining {room_id} over federation.");
|
info!("Joining {room_id} over federation.");
|
||||||
|
|
||||||
let (make_join_response, remote_server) = make_join_request(services, sender_user, room_id, servers).await?;
|
let (make_join_response, remote_server) =
|
||||||
|
make_join_request(services, sender_user, room_id, servers).await?;
|
||||||
|
|
||||||
info!("make_join finished");
|
info!("make_join finished");
|
||||||
|
|
||||||
|
@ -783,8 +839,8 @@ async fn join_room_by_id_helper_remote(
|
||||||
// We keep the "event_id" in the pdu only in v1 or
|
// We keep the "event_id" in the pdu only in v1 or
|
||||||
// v2 rooms
|
// v2 rooms
|
||||||
match room_version_id {
|
match room_version_id {
|
||||||
RoomVersionId::V1 | RoomVersionId::V2 => {},
|
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||||
_ => {
|
| _ => {
|
||||||
join_event_stub.remove("event_id");
|
join_event_stub.remove("event_id");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -799,7 +855,8 @@ async fn join_room_by_id_helper_remote(
|
||||||
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
||||||
|
|
||||||
// Add event_id back
|
// Add event_id back
|
||||||
join_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
join_event_stub
|
||||||
|
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||||
|
|
||||||
// It has enough fields to be called a proper event now
|
// It has enough fields to be called a proper event now
|
||||||
let mut join_event = join_event_stub;
|
let mut join_event = join_event_stub;
|
||||||
|
@ -825,12 +882,16 @@ async fn join_room_by_id_helper_remote(
|
||||||
if join_authorized_via_users_server.is_some() {
|
if join_authorized_via_users_server.is_some() {
|
||||||
if let Some(signed_raw) = &send_join_response.room_state.event {
|
if let Some(signed_raw) = &send_join_response.room_state.event {
|
||||||
debug_info!(
|
debug_info!(
|
||||||
"There is a signed event with join_authorized_via_users_server. This room is probably using \
|
"There is a signed event with join_authorized_via_users_server. This room is \
|
||||||
restricted joins. Adding signature to our event"
|
probably using restricted joins. Adding signature to our event"
|
||||||
);
|
);
|
||||||
|
|
||||||
let (signed_event_id, signed_value) = gen_event_id_canonical_json(signed_raw, &room_version_id)
|
let (signed_event_id, signed_value) =
|
||||||
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
|
gen_event_id_canonical_json(signed_raw, &room_version_id).map_err(|e| {
|
||||||
|
err!(Request(BadJson(warn!(
|
||||||
|
"Could not convert event to canonical JSON: {e}"
|
||||||
|
))))
|
||||||
|
})?;
|
||||||
|
|
||||||
if signed_event_id != event_id {
|
if signed_event_id != event_id {
|
||||||
return Err!(Request(BadJson(
|
return Err!(Request(BadJson(
|
||||||
|
@ -840,15 +901,20 @@ async fn join_room_by_id_helper_remote(
|
||||||
|
|
||||||
match signed_value["signatures"]
|
match signed_value["signatures"]
|
||||||
.as_object()
|
.as_object()
|
||||||
.ok_or_else(|| err!(BadServerResponse(warn!("Server {remote_server} sent invalid signatures type"))))
|
.ok_or_else(|| {
|
||||||
|
err!(BadServerResponse(warn!(
|
||||||
|
"Server {remote_server} sent invalid signatures type"
|
||||||
|
)))
|
||||||
|
})
|
||||||
.and_then(|e| {
|
.and_then(|e| {
|
||||||
e.get(remote_server.as_str()).ok_or_else(|| {
|
e.get(remote_server.as_str()).ok_or_else(|| {
|
||||||
err!(BadServerResponse(warn!(
|
err!(BadServerResponse(warn!(
|
||||||
"Server {remote_server} did not send its signature for a restricted room"
|
"Server {remote_server} did not send its signature for a restricted \
|
||||||
|
room"
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
Ok(signature) => {
|
| Ok(signature) => {
|
||||||
join_event
|
join_event
|
||||||
.get_mut("signatures")
|
.get_mut("signatures")
|
||||||
.expect("we created a valid pdu")
|
.expect("we created a valid pdu")
|
||||||
|
@ -856,10 +922,10 @@ async fn join_room_by_id_helper_remote(
|
||||||
.expect("we created a valid pdu")
|
.expect("we created a valid pdu")
|
||||||
.insert(remote_server.to_string(), signature.clone());
|
.insert(remote_server.to_string(), signature.clone());
|
||||||
},
|
},
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
"Server {remote_server} sent invalid signature in send_join signatures for event \
|
"Server {remote_server} sent invalid signature in send_join signatures \
|
||||||
{signed_value:?}: {e:?}",
|
for event {signed_value:?}: {e:?}",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -900,8 +966,8 @@ async fn join_room_by_id_helper_remote(
|
||||||
.ready_filter_map(Result::ok)
|
.ready_filter_map(Result::ok)
|
||||||
.fold(HashMap::new(), |mut state, (event_id, value)| async move {
|
.fold(HashMap::new(), |mut state, (event_id, value)| async move {
|
||||||
let pdu = match PduEvent::from_id_val(&event_id, value.clone()) {
|
let pdu = match PduEvent::from_id_val(&event_id, value.clone()) {
|
||||||
Ok(pdu) => pdu,
|
| Ok(pdu) => pdu,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}");
|
debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}");
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
@ -937,7 +1003,9 @@ async fn join_room_by_id_helper_remote(
|
||||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
||||||
})
|
})
|
||||||
.ready_filter_map(Result::ok)
|
.ready_filter_map(Result::ok)
|
||||||
.ready_for_each(|(event_id, value)| services.rooms.outlier.add_pdu_outlier(&event_id, &value))
|
.ready_for_each(|(event_id, value)| {
|
||||||
|
services.rooms.outlier.add_pdu_outlier(&event_id, &value);
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
drop(cork);
|
drop(cork);
|
||||||
|
@ -1031,29 +1099,38 @@ async fn join_room_by_id_helper_remote(
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_local")]
|
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_local")]
|
||||||
async fn join_room_by_id_helper_local(
|
async fn join_room_by_id_helper_local(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
services: &Services,
|
||||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: RoomMutexGuard,
|
sender_user: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
reason: Option<String>,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
|
_third_party_signed: Option<&ThirdPartySigned>,
|
||||||
|
state_lock: RoomMutexGuard,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
debug_info!("We can join locally");
|
debug_info!("We can join locally");
|
||||||
|
|
||||||
let join_rules_event_content = services
|
let join_rules_event_content = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomJoinRulesEventContent>(room_id, &StateEventType::RoomJoinRules, "")
|
.room_state_get_content::<RoomJoinRulesEventContent>(
|
||||||
|
room_id,
|
||||||
|
&StateEventType::RoomJoinRules,
|
||||||
|
"",
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let restriction_rooms = match join_rules_event_content {
|
let restriction_rooms = match join_rules_event_content {
|
||||||
Ok(RoomJoinRulesEventContent {
|
| Ok(RoomJoinRulesEventContent {
|
||||||
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
|
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
|
||||||
}) => restricted
|
}) => restricted
|
||||||
.allow
|
.allow
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|a| match a {
|
.filter_map(|a| match a {
|
||||||
AllowRule::RoomMembership(r) => Some(r.room_id),
|
| AllowRule::RoomMembership(r) => Some(r.room_id),
|
||||||
_ => None,
|
| _ => None,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => Vec::new(),
|
| _ => Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let join_authorized_via_users_server: Option<OwnedUserId> = {
|
let join_authorized_via_users_server: Option<OwnedUserId> = {
|
||||||
|
@ -1073,10 +1150,12 @@ async fn join_room_by_id_helper_local(
|
||||||
.state_cache
|
.state_cache
|
||||||
.local_users_in_room(room_id)
|
.local_users_in_room(room_id)
|
||||||
.filter(|user| {
|
.filter(|user| {
|
||||||
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,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
.next()
|
.next()
|
||||||
|
@ -1112,13 +1191,18 @@ async fn join_room_by_id_helper_local(
|
||||||
};
|
};
|
||||||
|
|
||||||
if restriction_rooms.is_empty()
|
if restriction_rooms.is_empty()
|
||||||
&& (servers.is_empty() || servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
&& (servers.is_empty()
|
||||||
|
|| servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
||||||
{
|
{
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("We couldn't do the join locally, maybe federation can help to satisfy the restricted join requirements");
|
warn!(
|
||||||
let Ok((make_join_response, remote_server)) = make_join_request(services, sender_user, room_id, servers).await
|
"We couldn't do the join locally, maybe federation can help to satisfy the restricted \
|
||||||
|
join requirements"
|
||||||
|
);
|
||||||
|
let Ok((make_join_response, remote_server)) =
|
||||||
|
make_join_request(services, sender_user, room_id, servers).await
|
||||||
else {
|
else {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
};
|
};
|
||||||
|
@ -1133,8 +1217,10 @@ async fn join_room_by_id_helper_local(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut join_event_stub: CanonicalJsonObject = serde_json::from_str(make_join_response.event.get())
|
let mut join_event_stub: CanonicalJsonObject =
|
||||||
.map_err(|e| err!(BadServerResponse("Invalid make_join event json received from server: {e:?}")))?;
|
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
|
||||||
|
err!(BadServerResponse("Invalid make_join event json received from server: {e:?}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
let join_authorized_via_users_server = join_event_stub
|
let join_authorized_via_users_server = join_event_stub
|
||||||
.get("content")
|
.get("content")
|
||||||
|
@ -1173,8 +1259,8 @@ async fn join_room_by_id_helper_local(
|
||||||
// We keep the "event_id" in the pdu only in v1 or
|
// We keep the "event_id" in the pdu only in v1 or
|
||||||
// v2 rooms
|
// v2 rooms
|
||||||
match room_version_id {
|
match room_version_id {
|
||||||
RoomVersionId::V1 | RoomVersionId::V2 => {},
|
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||||
_ => {
|
| _ => {
|
||||||
join_event_stub.remove("event_id");
|
join_event_stub.remove("event_id");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1189,7 +1275,8 @@ async fn join_room_by_id_helper_local(
|
||||||
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
||||||
|
|
||||||
// Add event_id back
|
// Add event_id back
|
||||||
join_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
join_event_stub
|
||||||
|
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||||
|
|
||||||
// It has enough fields to be called a proper event now
|
// It has enough fields to be called a proper event now
|
||||||
let join_event = join_event_stub;
|
let join_event = join_event_stub;
|
||||||
|
@ -1211,8 +1298,10 @@ async fn join_room_by_id_helper_local(
|
||||||
.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) = gen_event_id_canonical_json(&signed_raw, &room_version_id)
|
let (signed_event_id, signed_value) =
|
||||||
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
|
gen_event_id_canonical_json(&signed_raw, &room_version_id).map_err(|e| {
|
||||||
|
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
if signed_event_id != event_id {
|
if signed_event_id != event_id {
|
||||||
return Err!(Request(BadJson(
|
return Err!(Request(BadJson(
|
||||||
|
@ -1234,9 +1323,13 @@ async fn join_room_by_id_helper_local(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn make_join_request(
|
async fn make_join_request(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, servers: &[OwnedServerName],
|
services: &Services,
|
||||||
|
sender_user: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
servers: &[OwnedServerName],
|
||||||
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
|
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
|
||||||
let mut make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining."));
|
let mut make_join_response_and_server =
|
||||||
|
Err!(BadServerResponse("No server available to assist in joining."));
|
||||||
|
|
||||||
let mut make_join_counter: usize = 0;
|
let mut make_join_counter: usize = 0;
|
||||||
let mut incompatible_room_version_count: usize = 0;
|
let mut incompatible_room_version_count: usize = 0;
|
||||||
|
@ -1266,23 +1359,28 @@ async fn make_join_request(
|
||||||
e.kind(),
|
e.kind(),
|
||||||
ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
|
ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
|
||||||
) {
|
) {
|
||||||
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
|
incompatible_room_version_count =
|
||||||
|
incompatible_room_version_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if incompatible_room_version_count > 15 {
|
if incompatible_room_version_count > 15 {
|
||||||
info!(
|
info!(
|
||||||
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or M_UNSUPPORTED_ROOM_VERSION, \
|
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or \
|
||||||
assuming that conduwuit does not support the room version {room_id}: {e}"
|
M_UNSUPPORTED_ROOM_VERSION, assuming that conduwuit does not support the \
|
||||||
|
room version {room_id}: {e}"
|
||||||
);
|
);
|
||||||
make_join_response_and_server = Err!(BadServerResponse("Room version is not supported by Conduwuit"));
|
make_join_response_and_server =
|
||||||
|
Err!(BadServerResponse("Room version is not supported by Conduwuit"));
|
||||||
return make_join_response_and_server;
|
return make_join_response_and_server;
|
||||||
}
|
}
|
||||||
|
|
||||||
if make_join_counter > 40 {
|
if make_join_counter > 40 {
|
||||||
warn!(
|
warn!(
|
||||||
"40 servers failed to provide valid make_join response, assuming no server can assist in joining."
|
"40 servers failed to provide valid make_join response, assuming no server \
|
||||||
|
can assist in joining."
|
||||||
);
|
);
|
||||||
make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining."));
|
make_join_response_and_server =
|
||||||
|
Err!(BadServerResponse("No server available to assist in joining."));
|
||||||
return make_join_response_and_server;
|
return make_join_response_and_server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1298,11 +1396,18 @@ async fn make_join_request(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn invite_helper(
|
pub(crate) async fn invite_helper(
|
||||||
services: &Services, sender_user: &UserId, user_id: &UserId, room_id: &RoomId, reason: Option<String>,
|
services: &Services,
|
||||||
|
sender_user: &UserId,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
reason: Option<String>,
|
||||||
is_direct: bool,
|
is_direct: bool,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if !services.users.is_admin(sender_user).await && services.globals.block_non_admin_invites() {
|
if !services.users.is_admin(sender_user).await && services.globals.block_non_admin_invites() {
|
||||||
info!("User {sender_user} is not an admin and attempted to send an invite to room {room_id}");
|
info!(
|
||||||
|
"User {sender_user} is not an admin and attempted to send an invite to room \
|
||||||
|
{room_id}"
|
||||||
|
);
|
||||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1339,31 +1444,30 @@ pub(crate) async fn invite_helper(
|
||||||
|
|
||||||
let response = services
|
let response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(user_id.server_name(), create_invite::v2::Request {
|
||||||
user_id.server_name(),
|
room_id: room_id.to_owned(),
|
||||||
create_invite::v2::Request {
|
event_id: (*pdu.event_id).to_owned(),
|
||||||
room_id: room_id.to_owned(),
|
room_version: room_version_id.clone(),
|
||||||
event_id: (*pdu.event_id).to_owned(),
|
event: services
|
||||||
room_version: room_version_id.clone(),
|
.sending
|
||||||
event: services
|
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||||
.sending
|
.await,
|
||||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
invite_room_state,
|
||||||
.await,
|
via: services
|
||||||
invite_room_state,
|
.rooms
|
||||||
via: services
|
.state_cache
|
||||||
.rooms
|
.servers_route_via(room_id)
|
||||||
.state_cache
|
.await
|
||||||
.servers_route_via(room_id)
|
.ok(),
|
||||||
.await
|
})
|
||||||
.ok(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// We do not add the event_id field to the pdu here because of signature and
|
// We do not add the event_id field to the pdu here because of signature and
|
||||||
// hashes checks
|
// hashes checks
|
||||||
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||||
.map_err(|e| err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}")))))?;
|
.map_err(|e| {
|
||||||
|
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
if pdu.event_id != event_id {
|
if pdu.event_id != event_id {
|
||||||
return Err!(Request(BadJson(
|
return Err!(Request(BadJson(
|
||||||
|
@ -1379,14 +1483,18 @@ pub(crate) async fn invite_helper(
|
||||||
)
|
)
|
||||||
.expect("CanonicalJson is valid json value"),
|
.expect("CanonicalJson is valid json value"),
|
||||||
)
|
)
|
||||||
.map_err(|e| err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}")))))?;
|
.map_err(|e| {
|
||||||
|
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
let pdu_id = services
|
let pdu_id = services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept incoming PDU as timeline event."))))?;
|
.ok_or_else(|| {
|
||||||
|
err!(Request(InvalidParam("Could not accept incoming PDU as timeline event.")))
|
||||||
|
})?;
|
||||||
|
|
||||||
return services.sending.send_pdu_room(room_id, &pdu_id).await;
|
return services.sending.send_pdu_room(room_id, &pdu_id).await;
|
||||||
}
|
}
|
||||||
|
@ -1456,7 +1564,12 @@ pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
pub async fn leave_room(
|
||||||
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
reason: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
//use conduwuit::utils::stream::OptionStream;
|
//use conduwuit::utils::stream::OptionStream;
|
||||||
use futures::TryFutureExt;
|
use futures::TryFutureExt;
|
||||||
|
|
||||||
|
@ -1500,7 +1613,11 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
|
||||||
let Ok(event) = services
|
let Ok(event) = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomMemberEventContent>(room_id, &StateEventType::RoomMember, user_id.as_str())
|
.room_state_get_content::<RoomMemberEventContent>(
|
||||||
|
room_id,
|
||||||
|
&StateEventType::RoomMember,
|
||||||
|
user_id.as_str(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
else {
|
else {
|
||||||
// Fix for broken rooms
|
// Fix for broken rooms
|
||||||
|
@ -1527,14 +1644,11 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||||
user_id.to_string(),
|
membership: MembershipState::Leave,
|
||||||
&RoomMemberEventContent {
|
reason,
|
||||||
membership: MembershipState::Leave,
|
..event
|
||||||
reason,
|
}),
|
||||||
..event
|
|
||||||
},
|
|
||||||
),
|
|
||||||
user_id,
|
user_id,
|
||||||
room_id,
|
room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -1545,8 +1659,13 @@ pub async fn leave_room(services: &Services, user_id: &UserId, room_id: &RoomId,
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<()> {
|
async fn remote_leave_room(
|
||||||
let mut make_leave_response_and_server = Err!(BadServerResponse("No server available to assist in leaving."));
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut make_leave_response_and_server =
|
||||||
|
Err!(BadServerResponse("No server available to assist in leaving."));
|
||||||
|
|
||||||
let invite_state = services
|
let invite_state = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -1608,8 +1727,12 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(make_leave_response.event.get())
|
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
.map_err(|e| err!(BadServerResponse("Invalid make_leave event json received from server: {e:?}")))?;
|
make_leave_response.event.get(),
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
err!(BadServerResponse("Invalid make_leave event json received from server: {e:?}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
// TODO: Is origin needed?
|
// TODO: Is origin needed?
|
||||||
leave_event_stub.insert(
|
leave_event_stub.insert(
|
||||||
|
@ -1627,8 +1750,8 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
|
||||||
|
|
||||||
// room v3 and above removed the "event_id" field from remote PDU format
|
// room v3 and above removed the "event_id" field from remote PDU format
|
||||||
match room_version_id {
|
match room_version_id {
|
||||||
RoomVersionId::V1 | RoomVersionId::V2 => {},
|
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||||
_ => {
|
| _ => {
|
||||||
leave_event_stub.remove("event_id");
|
leave_event_stub.remove("event_id");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1643,7 +1766,8 @@ async fn remote_leave_room(services: &Services, user_id: &UserId, room_id: &Room
|
||||||
let event_id = pdu::gen_event_id(&leave_event_stub, &room_version_id)?;
|
let event_id = pdu::gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||||
|
|
||||||
// Add event_id back
|
// Add event_id back
|
||||||
leave_event_stub.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
leave_event_stub
|
||||||
|
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||||
|
|
||||||
// It has enough fields to be called a proper event now
|
// It has enough fields to be called a proper event now
|
||||||
let leave_event = leave_event_stub;
|
let leave_event = leave_event_stub;
|
||||||
|
|
|
@ -56,7 +56,8 @@ const LIMIT_DEFAULT: usize = 10;
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||||
/// where the user was joined, depending on `history_visibility`)
|
/// where the user was joined, depending on `history_visibility`)
|
||||||
pub(crate) async fn get_message_events_route(
|
pub(crate) async fn get_message_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_message_events::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_message_events::v3::Request>,
|
||||||
) -> Result<get_message_events::v3::Response> {
|
) -> Result<get_message_events::v3::Response> {
|
||||||
let sender = body.sender();
|
let sender = body.sender();
|
||||||
let (sender_user, sender_device) = sender;
|
let (sender_user, sender_device) = sender;
|
||||||
|
@ -69,8 +70,8 @@ pub(crate) async fn get_message_events_route(
|
||||||
.map(str::parse)
|
.map(str::parse)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_else(|| match body.dir {
|
.unwrap_or_else(|| match body.dir {
|
||||||
Direction::Forward => PduCount::min(),
|
| Direction::Forward => PduCount::min(),
|
||||||
Direction::Backward => PduCount::max(),
|
| Direction::Backward => PduCount::max(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let to: Option<PduCount> = body.to.as_deref().map(str::parse).flat_ok();
|
let to: Option<PduCount> = body.to.as_deref().map(str::parse).flat_ok();
|
||||||
|
@ -81,10 +82,12 @@ pub(crate) async fn get_message_events_route(
|
||||||
.unwrap_or(LIMIT_DEFAULT)
|
.unwrap_or(LIMIT_DEFAULT)
|
||||||
.min(LIMIT_MAX);
|
.min(LIMIT_MAX);
|
||||||
|
|
||||||
services
|
services.rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||||
.rooms
|
sender_user,
|
||||||
.lazy_loading
|
sender_device,
|
||||||
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, from);
|
room_id,
|
||||||
|
from,
|
||||||
|
);
|
||||||
|
|
||||||
if matches!(body.dir, Direction::Backward) {
|
if matches!(body.dir, Direction::Backward) {
|
||||||
services
|
services
|
||||||
|
@ -98,14 +101,14 @@ pub(crate) async fn get_message_events_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
let it = match body.dir {
|
let it = match body.dir {
|
||||||
Direction::Forward => services
|
| Direction::Forward => services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus(Some(sender_user), room_id, Some(from))
|
.pdus(Some(sender_user), room_id, Some(from))
|
||||||
.await?
|
.await?
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
|
||||||
Direction::Backward => services
|
| Direction::Backward => services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_rev(Some(sender_user), room_id, Some(from))
|
.pdus_rev(Some(sender_user), room_id, Some(from))
|
||||||
|
@ -141,10 +144,13 @@ pub(crate) async fn get_message_events_route(
|
||||||
|
|
||||||
if !cfg!(feature = "element_hacks") {
|
if !cfg!(feature = "element_hacks") {
|
||||||
if let Some(next_token) = next_token {
|
if let Some(next_token) = next_token {
|
||||||
services
|
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
.rooms
|
sender_user,
|
||||||
.lazy_loading
|
sender_device,
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy, next_token);
|
room_id,
|
||||||
|
lazy,
|
||||||
|
next_token,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +168,11 @@ pub(crate) async fn get_message_events_route(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_member_event(services: &Services, room_id: &RoomId, user_id: &UserId) -> Option<Raw<AnyStateEvent>> {
|
async fn get_member_event(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
user_id: &UserId,
|
||||||
|
) -> Option<Raw<AnyStateEvent>> {
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
|
@ -173,7 +183,11 @@ async fn get_member_event(services: &Services, room_id: &RoomId, user_id: &UserI
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn update_lazy(
|
pub(crate) async fn update_lazy(
|
||||||
services: &Services, room_id: &RoomId, sender: (&UserId, &DeviceId), mut lazy: LazySet, item: &PdusIterItem,
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender: (&UserId, &DeviceId),
|
||||||
|
mut lazy: LazySet,
|
||||||
|
item: &PdusIterItem,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> LazySet {
|
) -> LazySet {
|
||||||
let (_, event) = &item;
|
let (_, event) = &item;
|
||||||
|
@ -204,7 +218,11 @@ pub(crate) async fn update_lazy(
|
||||||
lazy
|
lazy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn ignored_filter(services: &Services, item: PdusIterItem, user_id: &UserId) -> Option<PdusIterItem> {
|
pub(crate) async fn ignored_filter(
|
||||||
|
services: &Services,
|
||||||
|
item: PdusIterItem,
|
||||||
|
user_id: &UserId,
|
||||||
|
) -> Option<PdusIterItem> {
|
||||||
let (_, pdu) = &item;
|
let (_, pdu) = &item;
|
||||||
|
|
||||||
// exclude Synapse's dummy events from bloating up response bodies. clients
|
// exclude Synapse's dummy events from bloating up response bodies. clients
|
||||||
|
@ -223,7 +241,9 @@ pub(crate) async fn ignored_filter(services: &Services, item: PdusIterItem, user
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn visibility_filter(
|
pub(crate) async fn visibility_filter(
|
||||||
services: &Services, item: PdusIterItem, user_id: &UserId,
|
services: &Services,
|
||||||
|
item: PdusIterItem,
|
||||||
|
user_id: &UserId,
|
||||||
) -> Option<PdusIterItem> {
|
) -> Option<PdusIterItem> {
|
||||||
let (_, pdu) = &item;
|
let (_, pdu) = &item;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ use crate::{Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// - The token generated is only valid for the OpenID API
|
/// - The token generated is only valid for the OpenID API
|
||||||
pub(crate) async fn create_openid_token_route(
|
pub(crate) async fn create_openid_token_route(
|
||||||
State(services): State<crate::State>, body: Ruma<account::request_openid_token::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<account::request_openid_token::v3::Request>,
|
||||||
) -> Result<account::request_openid_token::v3::Response> {
|
) -> Result<account::request_openid_token::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");
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,14 @@ use crate::{Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Sets the presence state of the sender user.
|
/// Sets the presence state of the sender user.
|
||||||
pub(crate) async fn set_presence_route(
|
pub(crate) async fn set_presence_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_presence::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_presence::v3::Request>,
|
||||||
) -> Result<set_presence::v3::Response> {
|
) -> Result<set_presence::v3::Response> {
|
||||||
if !services.globals.allow_local_presence() {
|
if !services.globals.allow_local_presence() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"Presence is disabled on this server",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
@ -40,10 +44,14 @@ pub(crate) async fn set_presence_route(
|
||||||
///
|
///
|
||||||
/// - Only works if you share a room with the user
|
/// - Only works if you share a room with the user
|
||||||
pub(crate) async fn get_presence_route(
|
pub(crate) async fn get_presence_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_presence::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_presence::v3::Request>,
|
||||||
) -> Result<get_presence::v3::Response> {
|
) -> Result<get_presence::v3::Response> {
|
||||||
if !services.globals.allow_local_presence() {
|
if !services.globals.allow_local_presence() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Presence is disabled on this server"));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"Presence is disabled on this server",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
|
@ -11,7 +11,9 @@ use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::{
|
client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
profile::{get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name},
|
profile::{
|
||||||
|
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
|
@ -29,7 +31,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn set_displayname_route(
|
pub(crate) async fn set_displayname_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_display_name::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_display_name::v3::Request>,
|
||||||
) -> Result<set_display_name::v3::Response> {
|
) -> Result<set_display_name::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");
|
||||||
|
|
||||||
|
@ -45,7 +48,8 @@ pub(crate) async fn set_displayname_route(
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
update_displayname(&services, &body.user_id, body.displayname.clone(), &all_joined_rooms).await;
|
update_displayname(&services, &body.user_id, body.displayname.clone(), &all_joined_rooms)
|
||||||
|
.await;
|
||||||
|
|
||||||
if services.globals.allow_local_presence() {
|
if services.globals.allow_local_presence() {
|
||||||
// Presence update
|
// Presence update
|
||||||
|
@ -65,7 +69,8 @@ pub(crate) async fn set_displayname_route(
|
||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// displayname over federation
|
/// displayname over federation
|
||||||
pub(crate) async fn get_displayname_route(
|
pub(crate) async fn get_displayname_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_display_name::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_display_name::v3::Request>,
|
||||||
) -> Result<get_display_name::v3::Response> {
|
) -> Result<get_display_name::v3::Response> {
|
||||||
if !services.globals.user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
|
@ -94,9 +99,7 @@ pub(crate) async fn get_displayname_route(
|
||||||
.users
|
.users
|
||||||
.set_blurhash(&body.user_id, response.blurhash.clone());
|
.set_blurhash(&body.user_id, response.blurhash.clone());
|
||||||
|
|
||||||
return Ok(get_display_name::v3::Response {
|
return Ok(get_display_name::v3::Response { displayname: response.displayname });
|
||||||
displayname: response.displayname,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +120,8 @@ pub(crate) async fn get_displayname_route(
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn set_avatar_url_route(
|
pub(crate) async fn set_avatar_url_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_avatar_url::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_avatar_url::v3::Request>,
|
||||||
) -> Result<set_avatar_url::v3::Response> {
|
) -> Result<set_avatar_url::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");
|
||||||
|
|
||||||
|
@ -161,7 +165,8 @@ pub(crate) async fn set_avatar_url_route(
|
||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// `avatar_url` and blurhash over federation
|
/// `avatar_url` and blurhash over federation
|
||||||
pub(crate) async fn get_avatar_url_route(
|
pub(crate) async fn get_avatar_url_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_avatar_url::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_avatar_url::v3::Request>,
|
||||||
) -> Result<get_avatar_url::v3::Response> {
|
) -> Result<get_avatar_url::v3::Response> {
|
||||||
if !services.globals.user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
|
@ -218,7 +223,8 @@ pub(crate) async fn get_avatar_url_route(
|
||||||
/// - If user is on another server and we do not have a local copy already,
|
/// - If user is on another server and we do not have a local copy already,
|
||||||
/// fetch profile over federation.
|
/// fetch profile over federation.
|
||||||
pub(crate) async fn get_profile_route(
|
pub(crate) async fn get_profile_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_profile::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_profile::v3::Request>,
|
||||||
) -> Result<get_profile::v3::Response> {
|
) -> Result<get_profile::v3::Response> {
|
||||||
if !services.globals.user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
|
@ -254,9 +260,11 @@ pub(crate) async fn get_profile_route(
|
||||||
.set_timezone(&body.user_id, response.tz.clone());
|
.set_timezone(&body.user_id, response.tz.clone());
|
||||||
|
|
||||||
for (profile_key, profile_key_value) in &response.custom_profile_fields {
|
for (profile_key, profile_key_value) in &response.custom_profile_fields {
|
||||||
services
|
services.users.set_profile_key(
|
||||||
.users
|
&body.user_id,
|
||||||
.set_profile_key(&body.user_id, profile_key, Some(profile_key_value.clone()));
|
profile_key,
|
||||||
|
Some(profile_key_value.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(get_profile::v3::Response {
|
return Ok(get_profile::v3::Response {
|
||||||
|
@ -295,7 +303,10 @@ pub(crate) async fn get_profile_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_displayname(
|
pub async fn update_displayname(
|
||||||
services: &Services, user_id: &UserId, displayname: Option<String>, all_joined_rooms: &[OwnedRoomId],
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
displayname: Option<String>,
|
||||||
|
all_joined_rooms: &[OwnedRoomId],
|
||||||
) {
|
) {
|
||||||
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
||||||
services.users.avatar_url(user_id),
|
services.users.avatar_url(user_id),
|
||||||
|
@ -322,19 +333,16 @@ pub async fn update_displayname(
|
||||||
.iter()
|
.iter()
|
||||||
.try_stream()
|
.try_stream()
|
||||||
.and_then(|room_id: &OwnedRoomId| async move {
|
.and_then(|room_id: &OwnedRoomId| async move {
|
||||||
let pdu = PduBuilder::state(
|
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||||
user_id.to_string(),
|
displayname: displayname.clone(),
|
||||||
&RoomMemberEventContent {
|
membership: MembershipState::Join,
|
||||||
displayname: displayname.clone(),
|
avatar_url: avatar_url.clone(),
|
||||||
membership: MembershipState::Join,
|
blurhash: blurhash.clone(),
|
||||||
avatar_url: avatar_url.clone(),
|
join_authorized_via_users_server: None,
|
||||||
blurhash: blurhash.clone(),
|
reason: None,
|
||||||
join_authorized_via_users_server: None,
|
is_direct: None,
|
||||||
reason: None,
|
third_party_invite: None,
|
||||||
is_direct: None,
|
});
|
||||||
third_party_invite: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((pdu, room_id))
|
Ok((pdu, room_id))
|
||||||
})
|
})
|
||||||
|
@ -346,7 +354,10 @@ pub async fn update_displayname(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_avatar_url(
|
pub async fn update_avatar_url(
|
||||||
services: &Services, user_id: &UserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>,
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
avatar_url: Option<OwnedMxcUri>,
|
||||||
|
blurhash: Option<String>,
|
||||||
all_joined_rooms: &[OwnedRoomId],
|
all_joined_rooms: &[OwnedRoomId],
|
||||||
) {
|
) {
|
||||||
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
||||||
|
@ -375,19 +386,16 @@ pub async fn update_avatar_url(
|
||||||
.iter()
|
.iter()
|
||||||
.try_stream()
|
.try_stream()
|
||||||
.and_then(|room_id: &OwnedRoomId| async move {
|
.and_then(|room_id: &OwnedRoomId| async move {
|
||||||
let pdu = PduBuilder::state(
|
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||||
user_id.to_string(),
|
avatar_url: avatar_url.clone(),
|
||||||
&RoomMemberEventContent {
|
blurhash: blurhash.clone(),
|
||||||
avatar_url: avatar_url.clone(),
|
membership: MembershipState::Join,
|
||||||
blurhash: blurhash.clone(),
|
displayname: displayname.clone(),
|
||||||
membership: MembershipState::Join,
|
join_authorized_via_users_server: None,
|
||||||
displayname: displayname.clone(),
|
reason: None,
|
||||||
join_authorized_via_users_server: None,
|
is_direct: None,
|
||||||
reason: None,
|
third_party_invite: None,
|
||||||
is_direct: None,
|
});
|
||||||
third_party_invite: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((pdu, room_id))
|
Ok((pdu, room_id))
|
||||||
})
|
})
|
||||||
|
@ -399,7 +407,9 @@ pub async fn update_avatar_url(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_all_rooms(
|
pub async fn update_all_rooms(
|
||||||
services: &Services, all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: &UserId,
|
services: &Services,
|
||||||
|
all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>,
|
||||||
|
user_id: &UserId,
|
||||||
) {
|
) {
|
||||||
for (pdu_builder, room_id) in all_joined_rooms {
|
for (pdu_builder, room_id) in all_joined_rooms {
|
||||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||||
|
|
|
@ -4,15 +4,19 @@ use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
push::{
|
push::{
|
||||||
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled, get_pushrules_all,
|
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions,
|
||||||
get_pushrules_global_scope, set_pusher, set_pushrule, set_pushrule_actions, set_pushrule_enabled,
|
get_pushrule_enabled, get_pushrules_all, get_pushrules_global_scope, set_pusher,
|
||||||
|
set_pushrule, set_pushrule_actions, set_pushrule_enabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
push_rules::{PushRulesEvent, PushRulesEventContent},
|
push_rules::{PushRulesEvent, PushRulesEventContent},
|
||||||
GlobalAccountDataEventType,
|
GlobalAccountDataEventType,
|
||||||
},
|
},
|
||||||
push::{InsertPushRuleError, PredefinedContentRuleId, PredefinedOverrideRuleId, RemovePushRuleError, Ruleset},
|
push::{
|
||||||
|
InsertPushRuleError, PredefinedContentRuleId, PredefinedOverrideRuleId,
|
||||||
|
RemovePushRuleError, Ruleset,
|
||||||
|
},
|
||||||
CanonicalJsonObject, CanonicalJsonValue,
|
CanonicalJsonObject, CanonicalJsonValue,
|
||||||
};
|
};
|
||||||
use service::Services;
|
use service::Services;
|
||||||
|
@ -23,7 +27,8 @@ use crate::{Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Retrieves the push rules event for this user.
|
/// Retrieves the push rules event for this user.
|
||||||
pub(crate) async fn get_pushrules_all_route(
|
pub(crate) async fn get_pushrules_all_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushrules_all::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushrules_all::v3::Request>,
|
||||||
) -> Result<get_pushrules_all::v3::Response> {
|
) -> Result<get_pushrules_all::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -40,8 +45,10 @@ pub(crate) async fn get_pushrules_all_route(
|
||||||
return recreate_push_rules_and_return(&services, sender_user).await;
|
return recreate_push_rules_and_return(&services, sender_user).await;
|
||||||
};
|
};
|
||||||
|
|
||||||
let account_data_content = serde_json::from_value::<PushRulesEventContent>(content_value.into())
|
let account_data_content =
|
||||||
.map_err(|e| err!(Database(warn!("Invalid push rules account data event in database: {e}"))))?;
|
serde_json::from_value::<PushRulesEventContent>(content_value.into()).map_err(|e| {
|
||||||
|
err!(Database(warn!("Invalid push rules account data event in database: {e}")))
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut global_ruleset = account_data_content.global;
|
let mut global_ruleset = account_data_content.global;
|
||||||
|
|
||||||
|
@ -79,9 +86,7 @@ pub(crate) async fn get_pushrules_all_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
&serde_json::to_value(PushRulesEvent {
|
&serde_json::to_value(PushRulesEvent {
|
||||||
content: PushRulesEventContent {
|
content: PushRulesEventContent { global: global_ruleset.clone() },
|
||||||
global: global_ruleset.clone(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.expect("to json always works"),
|
.expect("to json always works"),
|
||||||
)
|
)
|
||||||
|
@ -89,9 +94,7 @@ pub(crate) async fn get_pushrules_all_route(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_pushrules_all::v3::Response {
|
Ok(get_pushrules_all::v3::Response { global: global_ruleset })
|
||||||
global: global_ruleset,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/pushrules/global/`
|
/// # `GET /_matrix/client/r0/pushrules/global/`
|
||||||
|
@ -100,7 +103,8 @@ pub(crate) async fn get_pushrules_all_route(
|
||||||
///
|
///
|
||||||
/// This appears to be the exact same as `GET /_matrix/client/r0/pushrules/`.
|
/// This appears to be the exact same as `GET /_matrix/client/r0/pushrules/`.
|
||||||
pub(crate) async fn get_pushrules_global_route(
|
pub(crate) async fn get_pushrules_global_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushrules_global_scope::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushrules_global_scope::v3::Request>,
|
||||||
) -> Result<get_pushrules_global_scope::v3::Response> {
|
) -> Result<get_pushrules_global_scope::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");
|
||||||
|
|
||||||
|
@ -134,8 +138,10 @@ pub(crate) async fn get_pushrules_global_route(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let account_data_content = serde_json::from_value::<PushRulesEventContent>(content_value.into())
|
let account_data_content =
|
||||||
.map_err(|e| err!(Database(warn!("Invalid push rules account data event in database: {e}"))))?;
|
serde_json::from_value::<PushRulesEventContent>(content_value.into()).map_err(|e| {
|
||||||
|
err!(Database(warn!("Invalid push rules account data event in database: {e}")))
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut global_ruleset = account_data_content.global;
|
let mut global_ruleset = account_data_content.global;
|
||||||
|
|
||||||
|
@ -173,9 +179,7 @@ pub(crate) async fn get_pushrules_global_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
&serde_json::to_value(PushRulesEvent {
|
&serde_json::to_value(PushRulesEvent {
|
||||||
content: PushRulesEventContent {
|
content: PushRulesEventContent { global: global_ruleset.clone() },
|
||||||
global: global_ruleset.clone(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.expect("to json always works"),
|
.expect("to json always works"),
|
||||||
)
|
)
|
||||||
|
@ -183,16 +187,15 @@ pub(crate) async fn get_pushrules_global_route(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(get_pushrules_global_scope::v3::Response {
|
Ok(get_pushrules_global_scope::v3::Response { global: global_ruleset })
|
||||||
global: global_ruleset,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||||
///
|
///
|
||||||
/// Retrieves a single specified push rule for this user.
|
/// Retrieves a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_route(
|
pub(crate) async fn get_pushrule_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushrule::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushrule::v3::Request>,
|
||||||
) -> Result<get_pushrule::v3::Response> {
|
) -> Result<get_pushrule::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");
|
||||||
|
|
||||||
|
@ -218,9 +221,7 @@ pub(crate) async fn get_pushrule_route(
|
||||||
.map(Into::into);
|
.map(Into::into);
|
||||||
|
|
||||||
if let Some(rule) = rule {
|
if let Some(rule) = rule {
|
||||||
Ok(get_pushrule::v3::Response {
|
Ok(get_pushrule::v3::Response { rule })
|
||||||
rule,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."))
|
Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."))
|
||||||
}
|
}
|
||||||
|
@ -230,7 +231,8 @@ pub(crate) async fn get_pushrule_route(
|
||||||
///
|
///
|
||||||
/// Creates a single specified push rule for this user.
|
/// Creates a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_route(
|
pub(crate) async fn set_pushrule_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_pushrule::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_pushrule::v3::Request>,
|
||||||
) -> Result<set_pushrule::v3::Response> {
|
) -> Result<set_pushrule::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 body = body.body;
|
let body = body.body;
|
||||||
|
@ -241,32 +243,33 @@ pub(crate) async fn set_pushrule_route(
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
|
.map_err(|_| err!(Request(NotFound("PushRules event not found."))))?;
|
||||||
|
|
||||||
if let Err(error) =
|
if let Err(error) = account_data.content.global.insert(
|
||||||
account_data
|
body.rule.clone(),
|
||||||
.content
|
body.after.as_deref(),
|
||||||
.global
|
body.before.as_deref(),
|
||||||
.insert(body.rule.clone(), body.after.as_deref(), body.before.as_deref())
|
) {
|
||||||
{
|
|
||||||
let err = match error {
|
let err = match error {
|
||||||
InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
|
| InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Rule IDs starting with a dot are reserved for server-default rules.",
|
"Rule IDs starting with a dot are reserved for server-default rules.",
|
||||||
),
|
),
|
||||||
InsertPushRuleError::InvalidRuleId => {
|
| InsertPushRuleError::InvalidRuleId => Error::BadRequest(
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Rule ID containing invalid characters.")
|
ErrorKind::InvalidParam,
|
||||||
},
|
"Rule ID containing invalid characters.",
|
||||||
InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
|
),
|
||||||
|
| InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Can't place a push rule relatively to a server-default rule.",
|
"Can't place a push rule relatively to a server-default rule.",
|
||||||
),
|
),
|
||||||
InsertPushRuleError::UnknownRuleId => {
|
| InsertPushRuleError::UnknownRuleId => Error::BadRequest(
|
||||||
Error::BadRequest(ErrorKind::NotFound, "The before or after rule could not be found.")
|
ErrorKind::NotFound,
|
||||||
},
|
"The before or after rule could not be found.",
|
||||||
InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
|
),
|
||||||
|
| InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"The before rule has a higher priority than the after rule.",
|
"The before rule has a higher priority than the after rule.",
|
||||||
),
|
),
|
||||||
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -289,7 +292,8 @@ pub(crate) async fn set_pushrule_route(
|
||||||
///
|
///
|
||||||
/// Gets the actions of a single specified push rule for this user.
|
/// Gets the actions of a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_actions_route(
|
pub(crate) async fn get_pushrule_actions_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushrule_actions::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushrule_actions::v3::Request>,
|
||||||
) -> Result<get_pushrule_actions::v3::Response> {
|
) -> Result<get_pushrule_actions::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");
|
||||||
|
|
||||||
|
@ -315,16 +319,15 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||||
.map(|rule| rule.actions().to_owned())
|
.map(|rule| rule.actions().to_owned())
|
||||||
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
|
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
|
||||||
|
|
||||||
Ok(get_pushrule_actions::v3::Response {
|
Ok(get_pushrule_actions::v3::Response { actions })
|
||||||
actions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/actions`
|
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/actions`
|
||||||
///
|
///
|
||||||
/// Sets the actions of a single specified push rule for this user.
|
/// Sets the actions of a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_actions_route(
|
pub(crate) async fn set_pushrule_actions_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_pushrule_actions::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_pushrule_actions::v3::Request>,
|
||||||
) -> Result<set_pushrule_actions::v3::Response> {
|
) -> Result<set_pushrule_actions::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");
|
||||||
|
|
||||||
|
@ -360,7 +363,8 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||||
///
|
///
|
||||||
/// Gets the enabled status of a single specified push rule for this user.
|
/// Gets the enabled status of a single specified push rule for this user.
|
||||||
pub(crate) async fn get_pushrule_enabled_route(
|
pub(crate) async fn get_pushrule_enabled_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushrule_enabled::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
) -> Result<get_pushrule_enabled::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");
|
||||||
|
|
||||||
|
@ -370,9 +374,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||||
|| body.rule_id.as_str() == PredefinedOverrideRuleId::ContainsDisplayName.as_str()
|
|| body.rule_id.as_str() == PredefinedOverrideRuleId::ContainsDisplayName.as_str()
|
||||||
|| body.rule_id.as_str() == PredefinedOverrideRuleId::RoomNotif.as_str()
|
|| body.rule_id.as_str() == PredefinedOverrideRuleId::RoomNotif.as_str()
|
||||||
{
|
{
|
||||||
return Ok(get_pushrule_enabled::v3::Response {
|
return Ok(get_pushrule_enabled::v3::Response { enabled: false });
|
||||||
enabled: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let event: PushRulesEvent = services
|
let event: PushRulesEvent = services
|
||||||
|
@ -388,16 +390,15 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||||
.map(ruma::push::AnyPushRuleRef::enabled)
|
.map(ruma::push::AnyPushRuleRef::enabled)
|
||||||
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
|
.ok_or_else(|| err!(Request(NotFound("Push rule not found."))))?;
|
||||||
|
|
||||||
Ok(get_pushrule_enabled::v3::Response {
|
Ok(get_pushrule_enabled::v3::Response { enabled })
|
||||||
enabled,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/enabled`
|
/// # `PUT /_matrix/client/r0/pushrules/global/{kind}/{ruleId}/enabled`
|
||||||
///
|
///
|
||||||
/// Sets the enabled status of a single specified push rule for this user.
|
/// Sets the enabled status of a single specified push rule for this user.
|
||||||
pub(crate) async fn set_pushrule_enabled_route(
|
pub(crate) async fn set_pushrule_enabled_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_pushrule_enabled::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
) -> Result<set_pushrule_enabled::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");
|
||||||
|
|
||||||
|
@ -433,7 +434,8 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||||
///
|
///
|
||||||
/// Deletes a single specified push rule for this user.
|
/// Deletes a single specified push rule for this user.
|
||||||
pub(crate) async fn delete_pushrule_route(
|
pub(crate) async fn delete_pushrule_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_pushrule::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_pushrule::v3::Request>,
|
||||||
) -> Result<delete_pushrule::v3::Response> {
|
) -> Result<delete_pushrule::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");
|
||||||
|
|
||||||
|
@ -449,11 +451,13 @@ pub(crate) async fn delete_pushrule_route(
|
||||||
.remove(body.kind.clone(), &body.rule_id)
|
.remove(body.kind.clone(), &body.rule_id)
|
||||||
{
|
{
|
||||||
let err = match error {
|
let err = match error {
|
||||||
RemovePushRuleError::ServerDefault => {
|
| RemovePushRuleError::ServerDefault => Error::BadRequest(
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Cannot delete a server-default pushrule.")
|
ErrorKind::InvalidParam,
|
||||||
},
|
"Cannot delete a server-default pushrule.",
|
||||||
RemovePushRuleError::NotFound => Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
|
),
|
||||||
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
| RemovePushRuleError::NotFound =>
|
||||||
|
Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
|
||||||
|
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -476,7 +480,8 @@ pub(crate) async fn delete_pushrule_route(
|
||||||
///
|
///
|
||||||
/// Gets all currently active pushers for the sender user.
|
/// Gets all currently active pushers for the sender user.
|
||||||
pub(crate) async fn get_pushers_route(
|
pub(crate) async fn get_pushers_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_pushers::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_pushers::v3::Request>,
|
||||||
) -> Result<get_pushers::v3::Response> {
|
) -> Result<get_pushers::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");
|
||||||
|
|
||||||
|
@ -491,7 +496,8 @@ pub(crate) async fn get_pushers_route(
|
||||||
///
|
///
|
||||||
/// - TODO: Handle `append`
|
/// - TODO: Handle `append`
|
||||||
pub(crate) async fn set_pushers_route(
|
pub(crate) async fn set_pushers_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_pusher::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_pusher::v3::Request>,
|
||||||
) -> Result<set_pusher::v3::Response> {
|
) -> Result<set_pusher::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");
|
||||||
|
|
||||||
|
@ -506,7 +512,8 @@ pub(crate) async fn set_pushers_route(
|
||||||
/// user somehow has bad push rules, these must always exist per spec.
|
/// user somehow has bad push rules, these must always exist per spec.
|
||||||
/// so recreate it and return server default silently
|
/// so recreate it and return server default silently
|
||||||
async fn recreate_push_rules_and_return(
|
async fn recreate_push_rules_and_return(
|
||||||
services: &Services, sender_user: &ruma::UserId,
|
services: &Services,
|
||||||
|
sender_user: &ruma::UserId,
|
||||||
) -> Result<get_pushrules_all::v3::Response> {
|
) -> Result<get_pushrules_all::v3::Response> {
|
||||||
services
|
services
|
||||||
.account_data
|
.account_data
|
||||||
|
|
|
@ -21,15 +21,14 @@ use crate::{Result, Ruma};
|
||||||
/// - If `read_receipt` is set: Update private marker and public read receipt
|
/// - If `read_receipt` is set: Update private marker and public read receipt
|
||||||
/// EDU
|
/// EDU
|
||||||
pub(crate) async fn set_read_marker_route(
|
pub(crate) async fn set_read_marker_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_read_marker::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_read_marker::v3::Request>,
|
||||||
) -> Result<set_read_marker::v3::Response> {
|
) -> Result<set_read_marker::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
if let Some(event) = &body.fully_read {
|
if let Some(event) = &body.fully_read {
|
||||||
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
||||||
content: ruma::events::fully_read::FullyReadEventContent {
|
content: ruma::events::fully_read::FullyReadEventContent { event_id: event.clone() },
|
||||||
event_id: event.clone(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
|
@ -55,13 +54,10 @@ pub(crate) async fn set_read_marker_route(
|
||||||
event.to_owned(),
|
event.to_owned(),
|
||||||
BTreeMap::from_iter([(
|
BTreeMap::from_iter([(
|
||||||
ReceiptType::Read,
|
ReceiptType::Read,
|
||||||
BTreeMap::from_iter([(
|
BTreeMap::from_iter([(sender_user.to_owned(), ruma::events::receipt::Receipt {
|
||||||
sender_user.to_owned(),
|
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||||
ruma::events::receipt::Receipt {
|
thread: ReceiptThread::Unthreaded,
|
||||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
})]),
|
||||||
thread: ReceiptThread::Unthreaded,
|
|
||||||
},
|
|
||||||
)]),
|
|
||||||
)]),
|
)]),
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
|
@ -88,7 +84,9 @@ pub(crate) async fn set_read_marker_route(
|
||||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||||
|
|
||||||
let PduCount::Normal(count) = count else {
|
let PduCount::Normal(count) = count else {
|
||||||
return Err!(Request(InvalidParam("Event is a backfilled PDU and cannot be marked as read.")));
|
return Err!(Request(InvalidParam(
|
||||||
|
"Event is a backfilled PDU and cannot be marked as read."
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
|
@ -104,7 +102,8 @@ pub(crate) async fn set_read_marker_route(
|
||||||
///
|
///
|
||||||
/// Sets private read marker and public read receipt EDU.
|
/// Sets private read marker and public read receipt EDU.
|
||||||
pub(crate) async fn create_receipt_route(
|
pub(crate) async fn create_receipt_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_receipt::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_receipt::v3::Request>,
|
||||||
) -> Result<create_receipt::v3::Response> {
|
) -> Result<create_receipt::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
@ -119,7 +118,7 @@ pub(crate) async fn create_receipt_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
match body.receipt_type {
|
match body.receipt_type {
|
||||||
create_receipt::v3::ReceiptType::FullyRead => {
|
| create_receipt::v3::ReceiptType::FullyRead => {
|
||||||
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
||||||
content: ruma::events::fully_read::FullyReadEventContent {
|
content: ruma::events::fully_read::FullyReadEventContent {
|
||||||
event_id: body.event_id.clone(),
|
event_id: body.event_id.clone(),
|
||||||
|
@ -135,7 +134,7 @@ pub(crate) async fn create_receipt_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
create_receipt::v3::ReceiptType::Read => {
|
| create_receipt::v3::ReceiptType::Read => {
|
||||||
let receipt_content = BTreeMap::from_iter([(
|
let receipt_content = BTreeMap::from_iter([(
|
||||||
body.event_id.clone(),
|
body.event_id.clone(),
|
||||||
BTreeMap::from_iter([(
|
BTreeMap::from_iter([(
|
||||||
|
@ -163,7 +162,7 @@ pub(crate) async fn create_receipt_route(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
},
|
},
|
||||||
create_receipt::v3::ReceiptType::ReadPrivate => {
|
| create_receipt::v3::ReceiptType::ReadPrivate => {
|
||||||
let count = services
|
let count = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
|
@ -172,7 +171,9 @@ pub(crate) async fn create_receipt_route(
|
||||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||||
|
|
||||||
let PduCount::Normal(count) = count else {
|
let PduCount::Normal(count) = count else {
|
||||||
return Err!(Request(InvalidParam("Event is a backfilled PDU and cannot be marked as read.")));
|
return Err!(Request(InvalidParam(
|
||||||
|
"Event is a backfilled PDU and cannot be marked as read."
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
|
@ -180,12 +181,11 @@ pub(crate) async fn create_receipt_route(
|
||||||
.read_receipt
|
.read_receipt
|
||||||
.private_read_set(&body.room_id, sender_user, count);
|
.private_read_set(&body.room_id, sender_user, count);
|
||||||
},
|
},
|
||||||
_ => {
|
| _ =>
|
||||||
return Err!(Request(InvalidParam(warn!(
|
return Err!(Request(InvalidParam(warn!(
|
||||||
"Received unknown read receipt type: {}",
|
"Received unknown read receipt type: {}",
|
||||||
&body.receipt_type
|
&body.receipt_type
|
||||||
))))
|
)))),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(create_receipt::v3::Response {})
|
Ok(create_receipt::v3::Response {})
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use ruma::{api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent};
|
use ruma::{
|
||||||
|
api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{service::pdu::PduBuilder, Result, Ruma};
|
use crate::{service::pdu::PduBuilder, Result, Ruma};
|
||||||
|
|
||||||
|
@ -9,7 +11,8 @@ use crate::{service::pdu::PduBuilder, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// - TODO: Handle txn id
|
/// - TODO: Handle txn id
|
||||||
pub(crate) async fn redact_event_route(
|
pub(crate) async fn redact_event_route(
|
||||||
State(services): State<crate::State>, body: Ruma<redact_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<redact_event::v3::Request>,
|
||||||
) -> Result<redact_event::v3::Response> {
|
) -> Result<redact_event::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 body = body.body;
|
let body = body.body;
|
||||||
|
@ -35,7 +38,5 @@ pub(crate) async fn redact_event_route(
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
Ok(redact_event::v3::Response {
|
Ok(redact_event::v3::Response { event_id: event_id.into() })
|
||||||
event_id: event_id.into(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::relations::{
|
client::relations::{
|
||||||
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
|
get_relating_events, get_relating_events_with_rel_type,
|
||||||
|
get_relating_events_with_rel_type_and_event_type,
|
||||||
},
|
},
|
||||||
Direction,
|
Direction,
|
||||||
},
|
},
|
||||||
|
@ -21,7 +22,8 @@ use crate::Ruma;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
||||||
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
||||||
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
|
@ -47,7 +49,8 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
||||||
pub(crate) async fn get_relating_events_with_rel_type_route(
|
pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
||||||
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
|
@ -73,7 +76,8 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
||||||
pub(crate) async fn get_relating_events_route(
|
pub(crate) async fn get_relating_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_relating_events::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_relating_events::v1::Request>,
|
||||||
) -> Result<get_relating_events::v1::Response> {
|
) -> Result<get_relating_events::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
|
@ -93,16 +97,24 @@ pub(crate) async fn get_relating_events_route(
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn paginate_relations_with_filter(
|
async fn paginate_relations_with_filter(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, target: &EventId,
|
services: &Services,
|
||||||
filter_event_type: Option<TimelineEventType>, filter_rel_type: Option<RelationType>, from: Option<&str>,
|
sender_user: &UserId,
|
||||||
to: Option<&str>, limit: Option<UInt>, recurse: bool, dir: Direction,
|
room_id: &RoomId,
|
||||||
|
target: &EventId,
|
||||||
|
filter_event_type: Option<TimelineEventType>,
|
||||||
|
filter_rel_type: Option<RelationType>,
|
||||||
|
from: Option<&str>,
|
||||||
|
to: Option<&str>,
|
||||||
|
limit: Option<UInt>,
|
||||||
|
recurse: bool,
|
||||||
|
dir: Direction,
|
||||||
) -> Result<get_relating_events::v1::Response> {
|
) -> Result<get_relating_events::v1::Response> {
|
||||||
let start: PduCount = from
|
let start: PduCount = from
|
||||||
.map(str::parse)
|
.map(str::parse)
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_else(|| match dir {
|
.unwrap_or_else(|| match dir {
|
||||||
Direction::Forward => PduCount::min(),
|
| Direction::Forward => PduCount::min(),
|
||||||
Direction::Backward => PduCount::max(),
|
| Direction::Backward => PduCount::max(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let to: Option<PduCount> = to.map(str::parse).flat_ok();
|
let to: Option<PduCount> = to.map(str::parse).flat_ok();
|
||||||
|
@ -115,11 +127,7 @@ async fn paginate_relations_with_filter(
|
||||||
.min(100);
|
.min(100);
|
||||||
|
|
||||||
// Spec (v1.10) recommends depth of at least 3
|
// Spec (v1.10) recommends depth of at least 3
|
||||||
let depth: u8 = if recurse {
|
let depth: u8 = if recurse { 3 } else { 1 };
|
||||||
3
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
let events: Vec<PdusIterItem> = services
|
let events: Vec<PdusIterItem> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -145,8 +153,8 @@ async fn paginate_relations_with_filter(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let next_batch = match dir {
|
let next_batch = match dir {
|
||||||
Direction::Forward => events.last(),
|
| Direction::Forward => events.last(),
|
||||||
Direction::Backward => events.first(),
|
| Direction::Backward => events.first(),
|
||||||
}
|
}
|
||||||
.map(at!(0))
|
.map(at!(0))
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -164,7 +172,11 @@ async fn paginate_relations_with_filter(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn visibility_filter(services: &Services, sender_user: &UserId, item: PdusIterItem) -> Option<PdusIterItem> {
|
async fn visibility_filter(
|
||||||
|
services: &Services,
|
||||||
|
sender_user: &UserId,
|
||||||
|
item: PdusIterItem,
|
||||||
|
) -> Option<PdusIterItem> {
|
||||||
let (_, pdu) = &item;
|
let (_, pdu) = &item;
|
||||||
|
|
||||||
services
|
services
|
||||||
|
|
|
@ -25,7 +25,8 @@ use crate::{
|
||||||
/// Reports an abusive room to homeserver admins
|
/// Reports an abusive room to homeserver admins
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "report_room")]
|
#[tracing::instrument(skip_all, fields(%client), name = "report_room")]
|
||||||
pub(crate) async fn report_room_route(
|
pub(crate) async fn report_room_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<report_room::v3::Request>,
|
body: Ruma<report_room::v3::Request>,
|
||||||
) -> Result<report_room::v3::Response> {
|
) -> Result<report_room::v3::Response> {
|
||||||
// user authentication
|
// user authentication
|
||||||
|
@ -78,14 +79,16 @@ pub(crate) async fn report_room_route(
|
||||||
/// Reports an inappropriate event to homeserver admins
|
/// Reports an inappropriate event to homeserver admins
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "report_event")]
|
#[tracing::instrument(skip_all, fields(%client), name = "report_event")]
|
||||||
pub(crate) async fn report_event_route(
|
pub(crate) async fn report_event_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<report_content::v3::Request>,
|
body: Ruma<report_content::v3::Request>,
|
||||||
) -> Result<report_content::v3::Response> {
|
) -> Result<report_content::v3::Response> {
|
||||||
// user authentication
|
// user authentication
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Received event report by user {sender_user} for room {} and event ID {}, with reason: \"{}\"",
|
"Received event report by user {sender_user} for room {} and event ID {}, with reason: \
|
||||||
|
\"{}\"",
|
||||||
body.room_id,
|
body.room_id,
|
||||||
body.event_id,
|
body.event_id,
|
||||||
body.reason.as_deref().unwrap_or("")
|
body.reason.as_deref().unwrap_or("")
|
||||||
|
@ -114,8 +117,8 @@ pub(crate) async fn report_event_route(
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_message(message::RoomMessageEventContent::text_markdown(format!(
|
.send_message(message::RoomMessageEventContent::text_markdown(format!(
|
||||||
"@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: {}\n\nReport Score: \
|
"@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: \
|
||||||
{}\nReport Reason: {}",
|
{}\n\nReport Score: {}\nReport Reason: {}",
|
||||||
sender_user.to_owned(),
|
sender_user.to_owned(),
|
||||||
pdu.event_id,
|
pdu.event_id,
|
||||||
pdu.room_id,
|
pdu.room_id,
|
||||||
|
@ -136,10 +139,18 @@ pub(crate) async fn report_event_route(
|
||||||
/// check if report reasoning is less than or equal to 750 characters
|
/// check if report reasoning is less than or equal to 750 characters
|
||||||
/// check if reporting user is in the reporting room
|
/// check if reporting user is in the reporting room
|
||||||
async fn is_event_report_valid(
|
async fn is_event_report_valid(
|
||||||
services: &Services, event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: Option<&String>,
|
services: &Services,
|
||||||
score: Option<ruma::Int>, pdu: &PduEvent,
|
event_id: &EventId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender_user: &UserId,
|
||||||
|
reason: Option<&String>,
|
||||||
|
score: Option<ruma::Int>,
|
||||||
|
pdu: &PduEvent,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
|
debug_info!(
|
||||||
|
"Checking if report from user {sender_user} for event {event_id} in room {room_id} is \
|
||||||
|
valid"
|
||||||
|
);
|
||||||
|
|
||||||
if room_id != pdu.room_id {
|
if room_id != pdu.room_id {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -183,6 +194,9 @@ async fn is_event_report_valid(
|
||||||
/// enumerating for potential events existing in our server.
|
/// enumerating for potential events existing in our server.
|
||||||
async fn delay_response() {
|
async fn delay_response() {
|
||||||
let time_to_wait = rand::thread_rng().gen_range(2..5);
|
let time_to_wait = rand::thread_rng().gen_range(2..5);
|
||||||
debug_info!("Got successful /report request, waiting {time_to_wait} seconds before sending successful response.");
|
debug_info!(
|
||||||
|
"Got successful /report request, waiting {time_to_wait} seconds before sending \
|
||||||
|
successful response."
|
||||||
|
);
|
||||||
sleep(Duration::from_secs(time_to_wait)).await;
|
sleep(Duration::from_secs(time_to_wait)).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ use crate::Ruma;
|
||||||
/// - Only users joined to the room are allowed to call this, or if
|
/// - Only users joined to the room are allowed to call this, or if
|
||||||
/// `history_visibility` is world readable in the room
|
/// `history_visibility` is world readable in the room
|
||||||
pub(crate) async fn get_room_aliases_route(
|
pub(crate) async fn get_room_aliases_route(
|
||||||
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<aliases::v3::Request>,
|
||||||
) -> Result<aliases::v3::Response> {
|
) -> Result<aliases::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");
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ use ruma::{
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
serde::{JsonObject, Raw},
|
serde::{JsonObject, Raw},
|
||||||
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomVersionId,
|
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId,
|
||||||
|
RoomVersionId,
|
||||||
};
|
};
|
||||||
use serde_json::{json, value::to_raw_value};
|
use serde_json::{json, value::to_raw_value};
|
||||||
use service::{appservice::RegistrationInfo, Services};
|
use service::{appservice::RegistrationInfo, Services};
|
||||||
|
@ -49,7 +50,8 @@ use crate::{client::invite_helper, Ruma};
|
||||||
/// - Send invite events
|
/// - Send invite events
|
||||||
#[allow(clippy::large_stack_frames)]
|
#[allow(clippy::large_stack_frames)]
|
||||||
pub(crate) async fn create_room_route(
|
pub(crate) async fn create_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_room::v3::Request>,
|
||||||
) -> Result<create_room::v3::Response> {
|
) -> Result<create_room::v3::Response> {
|
||||||
use create_room::v3::RoomPreset;
|
use create_room::v3::RoomPreset;
|
||||||
|
|
||||||
|
@ -59,7 +61,10 @@ pub(crate) async fn create_room_route(
|
||||||
&& body.appservice_info.is_none()
|
&& body.appservice_info.is_none()
|
||||||
&& !services.users.is_admin(sender_user).await
|
&& !services.users.is_admin(sender_user).await
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"Room creation has been disabled.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
||||||
|
@ -91,8 +96,8 @@ pub(crate) async fn create_room_route(
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_text(&format!(
|
.send_text(&format!(
|
||||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
||||||
\"lockdown_public_room_directory\" is enabled",
|
while \"lockdown_public_room_directory\" is enabled",
|
||||||
&room_id
|
&room_id
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
@ -115,7 +120,7 @@ pub(crate) async fn create_room_route(
|
||||||
};
|
};
|
||||||
|
|
||||||
let room_version = match body.room_version.clone() {
|
let room_version = match body.room_version.clone() {
|
||||||
Some(room_version) => {
|
| Some(room_version) =>
|
||||||
if services.server.supported_room_version(&room_version) {
|
if services.server.supported_room_version(&room_version) {
|
||||||
room_version
|
room_version
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,13 +128,12 @@ pub(crate) async fn create_room_route(
|
||||||
ErrorKind::UnsupportedRoomVersion,
|
ErrorKind::UnsupportedRoomVersion,
|
||||||
"This server does not support that room version.",
|
"This server does not support that room version.",
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
},
|
| None => services.server.config.default_room_version.clone(),
|
||||||
None => services.server.config.default_room_version.clone(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let create_content = match &body.creation_content {
|
let create_content = match &body.creation_content {
|
||||||
Some(content) => {
|
| Some(content) => {
|
||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
|
|
||||||
let mut content = content
|
let mut content = content
|
||||||
|
@ -139,7 +143,7 @@ pub(crate) async fn create_room_route(
|
||||||
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
||||||
})?;
|
})?;
|
||||||
match room_version {
|
match room_version {
|
||||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||||
content.insert(
|
content.insert(
|
||||||
"creator".into(),
|
"creator".into(),
|
||||||
json!(&sender_user).try_into().map_err(|e| {
|
json!(&sender_user).try_into().map_err(|e| {
|
||||||
|
@ -148,24 +152,25 @@ pub(crate) async fn create_room_route(
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {
|
| _ => {
|
||||||
// V11+ removed the "creator" key
|
// V11+ removed the "creator" key
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
content.insert(
|
content.insert(
|
||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
json!(room_version.as_str())
|
json!(room_version.as_str()).try_into().map_err(|_| {
|
||||||
.try_into()
|
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?,
|
})?,
|
||||||
);
|
);
|
||||||
content
|
content
|
||||||
},
|
},
|
||||||
None => {
|
| None => {
|
||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
|
|
||||||
let content = match room_version {
|
let content = match room_version {
|
||||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
|
||||||
_ => RoomCreateEventContent::new_v11(),
|
RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||||
|
| _ => RoomCreateEventContent::new_v11(),
|
||||||
};
|
};
|
||||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
to_raw_value(&content)
|
to_raw_value(&content)
|
||||||
|
@ -190,7 +195,8 @@ pub(crate) async fn create_room_route(
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: TimelineEventType::RoomCreate,
|
||||||
content: to_raw_value(&create_content).expect("create event content serialization"),
|
content: to_raw_value(&create_content)
|
||||||
|
.expect("create event content serialization"),
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
@ -206,16 +212,13 @@ pub(crate) async fn create_room_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(sender_user.to_string(), &RoomMemberEventContent {
|
||||||
sender_user.to_string(),
|
displayname: services.users.displayname(sender_user).await.ok(),
|
||||||
&RoomMemberEventContent {
|
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||||
displayname: services.users.displayname(sender_user).await.ok(),
|
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
is_direct: Some(body.is_direct),
|
||||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
..RoomMemberEventContent::new(MembershipState::Join)
|
||||||
is_direct: Some(body.is_direct),
|
}),
|
||||||
..RoomMemberEventContent::new(MembershipState::Join)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -227,8 +230,8 @@ pub(crate) 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::Public => RoomPreset::PublicChat,
|
| room::Visibility::Public => RoomPreset::PublicChat,
|
||||||
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
| _ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut users = BTreeMap::from_iter([(sender_user.clone(), int!(100))]);
|
let mut users = BTreeMap::from_iter([(sender_user.clone(), int!(100))]);
|
||||||
|
@ -236,7 +239,9 @@ pub(crate) async fn create_room_route(
|
||||||
if preset == RoomPreset::TrustedPrivateChat {
|
if preset == RoomPreset::TrustedPrivateChat {
|
||||||
for invite in &body.invite {
|
for invite in &body.invite {
|
||||||
if services.users.user_is_ignored(sender_user, invite).await {
|
if services.users.user_is_ignored(sender_user, invite).await {
|
||||||
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
|
return Err!(Request(Forbidden(
|
||||||
|
"You cannot invite users you have ignored to rooms."
|
||||||
|
)));
|
||||||
} else if services.users.user_is_ignored(invite, sender_user).await {
|
} else if services.users.user_is_ignored(invite, sender_user).await {
|
||||||
// silently drop the invite to the recipient if they've been ignored by the
|
// silently drop the invite to the recipient if they've been ignored by the
|
||||||
// sender, pretend it worked
|
// sender, pretend it worked
|
||||||
|
@ -247,8 +252,11 @@ pub(crate) async fn create_room_route(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let power_levels_content =
|
let power_levels_content = default_power_levels_content(
|
||||||
default_power_levels_content(body.power_level_content_override.as_ref(), &body.visibility, users)?;
|
body.power_level_content_override.as_ref(),
|
||||||
|
&body.visibility,
|
||||||
|
users,
|
||||||
|
)?;
|
||||||
|
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -256,7 +264,8 @@ pub(crate) async fn create_room_route(
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomPowerLevels,
|
event_type: TimelineEventType::RoomPowerLevels,
|
||||||
content: to_raw_value(&power_levels_content).expect("serialized power_levels event content"),
|
content: to_raw_value(&power_levels_content)
|
||||||
|
.expect("serialized power_levels event content"),
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
@ -273,13 +282,10 @@ pub(crate) async fn create_room_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
|
||||||
String::new(),
|
alias: Some(room_alias_id.to_owned()),
|
||||||
&RoomCanonicalAliasEventContent {
|
alt_aliases: vec![],
|
||||||
alias: Some(room_alias_id.to_owned()),
|
}),
|
||||||
alt_aliases: vec![],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -298,9 +304,9 @@ pub(crate) async fn create_room_route(
|
||||||
PduBuilder::state(
|
PduBuilder::state(
|
||||||
String::new(),
|
String::new(),
|
||||||
&RoomJoinRulesEventContent::new(match preset {
|
&RoomJoinRulesEventContent::new(match preset {
|
||||||
RoomPreset::PublicChat => JoinRule::Public,
|
| RoomPreset::PublicChat => JoinRule::Public,
|
||||||
// according to spec "invite" is the default
|
// according to spec "invite" is the default
|
||||||
_ => JoinRule::Invite,
|
| _ => JoinRule::Invite,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -334,8 +340,8 @@ pub(crate) async fn create_room_route(
|
||||||
PduBuilder::state(
|
PduBuilder::state(
|
||||||
String::new(),
|
String::new(),
|
||||||
&RoomGuestAccessEventContent::new(match preset {
|
&RoomGuestAccessEventContent::new(match preset {
|
||||||
RoomPreset::PublicChat => GuestAccess::Forbidden,
|
| RoomPreset::PublicChat => GuestAccess::Forbidden,
|
||||||
_ => GuestAccess::CanJoin,
|
| _ => GuestAccess::CanJoin,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -367,7 +373,9 @@ pub(crate) async fn create_room_route(
|
||||||
pdu_builder.state_key.get_or_insert_with(String::new);
|
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 && !services.globals.allow_encryption() {
|
if pdu_builder.event_type == TimelineEventType::RoomEncryption
|
||||||
|
&& !services.globals.allow_encryption()
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,12 +407,7 @@ pub(crate) async fn create_room_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(String::new(), &RoomTopicEventContent { topic: topic.clone() }),
|
||||||
String::new(),
|
|
||||||
&RoomTopicEventContent {
|
|
||||||
topic: topic.clone(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -417,16 +420,19 @@ pub(crate) async fn create_room_route(
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
for user_id in &body.invite {
|
for user_id in &body.invite {
|
||||||
if services.users.user_is_ignored(sender_user, user_id).await {
|
if services.users.user_is_ignored(sender_user, user_id).await {
|
||||||
return Err!(Request(Forbidden("You cannot invite users you have ignored to rooms.")));
|
return Err!(Request(Forbidden(
|
||||||
|
"You cannot invite users you have ignored to rooms."
|
||||||
|
)));
|
||||||
} else if services.users.user_is_ignored(user_id, sender_user).await {
|
} else if services.users.user_is_ignored(user_id, sender_user).await {
|
||||||
// silently drop the invite to the recipient if they've been ignored by the
|
// silently drop the invite to the recipient if they've been ignored by the
|
||||||
// sender, pretend it worked
|
// sender, pretend it worked
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct)
|
if let Err(e) =
|
||||||
.boxed()
|
invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct)
|
||||||
.await
|
.boxed()
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
warn!(%e, "Failed to send invite");
|
warn!(%e, "Failed to send invite");
|
||||||
}
|
}
|
||||||
|
@ -446,7 +452,10 @@ pub(crate) async fn create_room_route(
|
||||||
if services.globals.config.admin_room_notices {
|
if services.globals.config.admin_room_notices {
|
||||||
services
|
services
|
||||||
.admin
|
.admin
|
||||||
.send_text(&format!("{sender_user} made {} public to the room directory", &room_id))
|
.send_text(&format!(
|
||||||
|
"{sender_user} made {} public to the room directory",
|
||||||
|
&room_id
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
info!("{sender_user} made {0} public to the room directory", &room_id);
|
info!("{sender_user} made {0} public to the room directory", &room_id);
|
||||||
|
@ -459,21 +468,24 @@ pub(crate) async fn create_room_route(
|
||||||
|
|
||||||
/// creates the power_levels_content for the PDU builder
|
/// creates the power_levels_content for the PDU builder
|
||||||
fn default_power_levels_content(
|
fn default_power_levels_content(
|
||||||
power_level_content_override: Option<&Raw<RoomPowerLevelsEventContent>>, visibility: &room::Visibility,
|
power_level_content_override: Option<&Raw<RoomPowerLevelsEventContent>>,
|
||||||
|
visibility: &room::Visibility,
|
||||||
users: BTreeMap<OwnedUserId, Int>,
|
users: BTreeMap<OwnedUserId, Int>,
|
||||||
) -> Result<serde_json::Value> {
|
) -> Result<serde_json::Value> {
|
||||||
let mut power_levels_content = serde_json::to_value(RoomPowerLevelsEventContent {
|
let mut power_levels_content =
|
||||||
users,
|
serde_json::to_value(RoomPowerLevelsEventContent { users, ..Default::default() })
|
||||||
..Default::default()
|
.expect("event is valid, we just created it");
|
||||||
})
|
|
||||||
.expect("event is valid, we just created it");
|
|
||||||
|
|
||||||
// secure proper defaults of sensitive/dangerous permissions that moderators
|
// secure proper defaults of sensitive/dangerous permissions that moderators
|
||||||
// (power level 50) should not have easy access to
|
// (power level 50) should not have easy access to
|
||||||
power_levels_content["events"]["m.room.power_levels"] = serde_json::to_value(100).expect("100 is valid Value");
|
power_levels_content["events"]["m.room.power_levels"] =
|
||||||
power_levels_content["events"]["m.room.server_acl"] = serde_json::to_value(100).expect("100 is valid Value");
|
serde_json::to_value(100).expect("100 is valid Value");
|
||||||
power_levels_content["events"]["m.room.tombstone"] = serde_json::to_value(100).expect("100 is valid Value");
|
power_levels_content["events"]["m.room.server_acl"] =
|
||||||
power_levels_content["events"]["m.room.encryption"] = serde_json::to_value(100).expect("100 is valid Value");
|
serde_json::to_value(100).expect("100 is valid Value");
|
||||||
|
power_levels_content["events"]["m.room.tombstone"] =
|
||||||
|
serde_json::to_value(100).expect("100 is valid Value");
|
||||||
|
power_levels_content["events"]["m.room.encryption"] =
|
||||||
|
serde_json::to_value(100).expect("100 is valid Value");
|
||||||
power_levels_content["events"]["m.room.history_visibility"] =
|
power_levels_content["events"]["m.room.history_visibility"] =
|
||||||
serde_json::to_value(100).expect("100 is valid Value");
|
serde_json::to_value(100).expect("100 is valid Value");
|
||||||
|
|
||||||
|
@ -481,14 +493,18 @@ fn default_power_levels_content(
|
||||||
// useful in read-only announcement rooms that post a public poll.
|
// useful in read-only announcement rooms that post a public poll.
|
||||||
power_levels_content["events"]["org.matrix.msc3381.poll.response"] =
|
power_levels_content["events"]["org.matrix.msc3381.poll.response"] =
|
||||||
serde_json::to_value(0).expect("0 is valid Value");
|
serde_json::to_value(0).expect("0 is valid Value");
|
||||||
power_levels_content["events"]["m.poll.response"] = serde_json::to_value(0).expect("0 is valid Value");
|
power_levels_content["events"]["m.poll.response"] =
|
||||||
|
serde_json::to_value(0).expect("0 is valid Value");
|
||||||
|
|
||||||
// synapse does this too. clients do not expose these permissions. it prevents
|
// synapse does this too. clients do not expose these permissions. it prevents
|
||||||
// default users from calling public rooms, for obvious reasons.
|
// default users from calling public rooms, for obvious reasons.
|
||||||
if *visibility == room::Visibility::Public {
|
if *visibility == room::Visibility::Public {
|
||||||
power_levels_content["events"]["m.call.invite"] = serde_json::to_value(50).expect("50 is valid Value");
|
power_levels_content["events"]["m.call.invite"] =
|
||||||
power_levels_content["events"]["m.call"] = serde_json::to_value(50).expect("50 is valid Value");
|
serde_json::to_value(50).expect("50 is valid Value");
|
||||||
power_levels_content["events"]["m.call.member"] = serde_json::to_value(50).expect("50 is valid Value");
|
power_levels_content["events"]["m.call"] =
|
||||||
|
serde_json::to_value(50).expect("50 is valid Value");
|
||||||
|
power_levels_content["events"]["m.call.member"] =
|
||||||
|
serde_json::to_value(50).expect("50 is valid Value");
|
||||||
power_levels_content["events"]["org.matrix.msc3401.call"] =
|
power_levels_content["events"]["org.matrix.msc3401.call"] =
|
||||||
serde_json::to_value(50).expect("50 is valid Value");
|
serde_json::to_value(50).expect("50 is valid Value");
|
||||||
power_levels_content["events"]["org.matrix.msc3401.call.member"] =
|
power_levels_content["events"]["org.matrix.msc3401.call.member"] =
|
||||||
|
@ -497,7 +513,9 @@ fn default_power_levels_content(
|
||||||
|
|
||||||
if let Some(power_level_content_override) = power_level_content_override {
|
if let Some(power_level_content_override) = power_level_content_override {
|
||||||
let json: JsonObject = serde_json::from_str(power_level_content_override.json().get())
|
let json: JsonObject = serde_json::from_str(power_level_content_override.json().get())
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override."))?;
|
.map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
|
||||||
|
})?;
|
||||||
|
|
||||||
for (key, value) in json {
|
for (key, value) in json {
|
||||||
power_levels_content[key] = value;
|
power_levels_content[key] = value;
|
||||||
|
@ -509,14 +527,16 @@ fn default_power_levels_content(
|
||||||
|
|
||||||
/// if a room is being created with a room alias, run our checks
|
/// if a room is being created with a room alias, run our checks
|
||||||
async fn room_alias_check(
|
async fn room_alias_check(
|
||||||
services: &Services, room_alias_name: &str, appservice_info: Option<&RegistrationInfo>,
|
services: &Services,
|
||||||
|
room_alias_name: &str,
|
||||||
|
appservice_info: Option<&RegistrationInfo>,
|
||||||
) -> Result<OwnedRoomAliasId> {
|
) -> Result<OwnedRoomAliasId> {
|
||||||
// Basic checks on the room alias validity
|
// Basic checks on the room alias validity
|
||||||
if room_alias_name.contains(':') {
|
if room_alias_name.contains(':') {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Room alias contained `:` which is not allowed. Please note that this expects a localpart, not the full \
|
"Room alias contained `:` which is not allowed. Please note that this expects a \
|
||||||
room alias.",
|
localpart, not the full room alias.",
|
||||||
));
|
));
|
||||||
} else if room_alias_name.contains(char::is_whitespace) {
|
} else if room_alias_name.contains(char::is_whitespace) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -534,8 +554,11 @@ async fn room_alias_check(
|
||||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
|
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_room_alias = RoomAliasId::parse(format!("#{}:{}", room_alias_name, services.globals.config.server_name))
|
let full_room_alias = RoomAliasId::parse(format!(
|
||||||
.map_err(|e| {
|
"#{}:{}",
|
||||||
|
room_alias_name, services.globals.config.server_name
|
||||||
|
))
|
||||||
|
.map_err(|e| {
|
||||||
info!("Failed to parse room alias {room_alias_name}: {e}");
|
info!("Failed to parse room alias {room_alias_name}: {e}");
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
|
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
|
||||||
})?;
|
})?;
|
||||||
|
@ -552,14 +575,20 @@ async fn room_alias_check(
|
||||||
|
|
||||||
if let Some(info) = appservice_info {
|
if let Some(info) = appservice_info {
|
||||||
if !info.aliases.is_match(full_room_alias.as_str()) {
|
if !info.aliases.is_match(full_room_alias.as_str()) {
|
||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Exclusive,
|
||||||
|
"Room alias is not in namespace.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else if services
|
} else if services
|
||||||
.appservice
|
.appservice
|
||||||
.is_exclusive_alias(&full_room_alias)
|
.is_exclusive_alias(&full_room_alias)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Exclusive,
|
||||||
|
"Room alias reserved by appservice.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_info!("Full room alias: {full_room_alias}");
|
debug_info!("Full room alias: {full_room_alias}");
|
||||||
|
@ -581,8 +610,8 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
|
||||||
if custom_room_id.contains(':') {
|
if custom_room_id.contains(':') {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Custom room ID contained `:` which is not allowed. Please note that this expects a localpart, not the \
|
"Custom room ID contained `:` which is not allowed. Please note that this expects a \
|
||||||
full room ID.",
|
localpart, not the full room ID.",
|
||||||
));
|
));
|
||||||
} else if custom_room_id.contains(char::is_whitespace) {
|
} else if custom_room_id.contains(char::is_whitespace) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -596,7 +625,10 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
|
||||||
debug_info!("Full custom room ID: {full_room_id}");
|
debug_info!("Full custom room ID: {full_room_id}");
|
||||||
|
|
||||||
RoomId::parse(full_room_id).map_err(|e| {
|
RoomId::parse(full_room_id).map_err(|e| {
|
||||||
info!("User attempted to create room with custom room ID {custom_room_id} but failed parsing: {e}");
|
info!(
|
||||||
|
"User attempted to create room with custom room ID {custom_room_id} but failed \
|
||||||
|
parsing: {e}"
|
||||||
|
);
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Custom room ID could not be parsed")
|
Error::BadRequest(ErrorKind::InvalidParam, "Custom room ID could not be parsed")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ use crate::{client::ignored_filter, Ruma};
|
||||||
///
|
///
|
||||||
/// Gets a single event.
|
/// Gets a single event.
|
||||||
pub(crate) async fn get_room_event_route(
|
pub(crate) async fn get_room_event_route(
|
||||||
State(services): State<crate::State>, ref body: Ruma<get_room_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
ref body: Ruma<get_room_event::v3::Request>,
|
||||||
) -> Result<get_room_event::v3::Response> {
|
) -> Result<get_room_event::v3::Response> {
|
||||||
let event = services
|
let event = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -47,7 +48,5 @@ pub(crate) async fn get_room_event_route(
|
||||||
|
|
||||||
let event = event.to_room_event();
|
let event = event.to_room_event();
|
||||||
|
|
||||||
Ok(get_room_event::v3::Response {
|
Ok(get_room_event::v3::Response { event })
|
||||||
event,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ use crate::Ruma;
|
||||||
const LIMIT_MAX: usize = 100;
|
const LIMIT_MAX: usize = 100;
|
||||||
|
|
||||||
pub(crate) async fn room_initial_sync_route(
|
pub(crate) async fn room_initial_sync_route(
|
||||||
State(services): State<crate::State>, body: Ruma<Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<Request>,
|
||||||
) -> Result<Response> {
|
) -> Result<Response> {
|
||||||
let room_id = &body.room_id;
|
let room_id = &body.room_id;
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,8 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||||
/// - Moves local aliases
|
/// - Moves local aliases
|
||||||
/// - Modifies old room power levels to prevent users from speaking
|
/// - Modifies old room power levels to prevent users from speaking
|
||||||
pub(crate) async fn upgrade_room_route(
|
pub(crate) async fn upgrade_room_route(
|
||||||
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<upgrade_room::v3::Request>,
|
||||||
) -> Result<upgrade_room::v3::Response> {
|
) -> Result<upgrade_room::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");
|
||||||
|
|
||||||
|
@ -72,13 +73,10 @@ pub(crate) async fn upgrade_room_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(String::new(), &RoomTombstoneEventContent {
|
||||||
String::new(),
|
body: "This room has been replaced".to_owned(),
|
||||||
&RoomTombstoneEventContent {
|
replacement_room: replacement_room.clone(),
|
||||||
body: "This room has been replaced".to_owned(),
|
}),
|
||||||
replacement_room: replacement_room.clone(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -108,7 +106,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
{
|
{
|
||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
match body.new_version {
|
match body.new_version {
|
||||||
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||||
create_event_content.insert(
|
create_event_content.insert(
|
||||||
"creator".into(),
|
"creator".into(),
|
||||||
json!(&sender_user).try_into().map_err(|e| {
|
json!(&sender_user).try_into().map_err(|e| {
|
||||||
|
@ -117,7 +115,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {
|
| _ => {
|
||||||
// "creator" key no longer exists in V11+ rooms
|
// "creator" key no longer exists in V11+ rooms
|
||||||
create_event_content.remove("creator");
|
create_event_content.remove("creator");
|
||||||
},
|
},
|
||||||
|
@ -154,7 +152,8 @@ pub(crate) async fn upgrade_room_route(
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: TimelineEventType::RoomCreate,
|
||||||
content: to_raw_value(&create_event_content).expect("event is valid, we just created it"),
|
content: to_raw_value(&create_event_content)
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
@ -203,8 +202,8 @@ pub(crate) async fn upgrade_room_route(
|
||||||
.room_state_get(&body.room_id, event_type, "")
|
.room_state_get(&body.room_id, event_type, "")
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => v.content.clone(),
|
| Ok(v) => v.content.clone(),
|
||||||
Err(_) => continue, // Skipping missing events.
|
| Err(_) => continue, // Skipping missing events.
|
||||||
};
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
|
@ -258,7 +257,9 @@ pub(crate) async fn upgrade_room_route(
|
||||||
power_levels_event_content
|
power_levels_event_content
|
||||||
.users_default
|
.users_default
|
||||||
.checked_add(int!(1))
|
.checked_add(int!(1))
|
||||||
.ok_or_else(|| err!(Request(BadJson("users_default power levels event content is not valid"))))?,
|
.ok_or_else(|| {
|
||||||
|
err!(Request(BadJson("users_default power levels event content is not valid")))
|
||||||
|
})?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Modify the power levels in the old room to prevent sending of events and
|
// Modify the power levels in the old room to prevent sending of events and
|
||||||
|
@ -267,14 +268,11 @@ pub(crate) async fn upgrade_room_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.build_and_append_pdu(
|
.build_and_append_pdu(
|
||||||
PduBuilder::state(
|
PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
|
||||||
String::new(),
|
events_default: new_level,
|
||||||
&RoomPowerLevelsEventContent {
|
invite: new_level,
|
||||||
events_default: new_level,
|
..power_levels_event_content
|
||||||
invite: new_level,
|
}),
|
||||||
..power_levels_event_content
|
|
||||||
},
|
|
||||||
),
|
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -284,7 +282,5 @@ pub(crate) async fn upgrade_room_route(
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
// Return the replacement room id
|
// Return the replacement room id
|
||||||
Ok(upgrade_room::v3::Response {
|
Ok(upgrade_room::v3::Response { replacement_room })
|
||||||
replacement_room,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,10 @@ const BATCH_MAX: usize = 20;
|
||||||
///
|
///
|
||||||
/// - Only works if the user is currently joined to the room (TODO: Respect
|
/// - Only works if the user is currently joined to the room (TODO: Respect
|
||||||
/// history visibility)
|
/// history visibility)
|
||||||
pub(crate) async fn search_events_route(State(services): State<crate::State>, body: Ruma<Request>) -> Result<Response> {
|
pub(crate) async fn search_events_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<Request>,
|
||||||
|
) -> Result<Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
let next_batch = body.next_batch.as_deref();
|
let next_batch = body.next_batch.as_deref();
|
||||||
let room_events_result: OptionFuture<_> = body
|
let room_events_result: OptionFuture<_> = body
|
||||||
|
@ -56,7 +59,10 @@ pub(crate) async fn search_events_route(State(services): State<crate::State>, bo
|
||||||
|
|
||||||
#[allow(clippy::map_unwrap_or)]
|
#[allow(clippy::map_unwrap_or)]
|
||||||
async fn category_room_events(
|
async fn category_room_events(
|
||||||
services: &Services, sender_user: &UserId, next_batch: Option<&str>, criteria: &Criteria,
|
services: &Services,
|
||||||
|
sender_user: &UserId,
|
||||||
|
next_batch: Option<&str>,
|
||||||
|
criteria: &Criteria,
|
||||||
) -> Result<ResultRoomEvents> {
|
) -> Result<ResultRoomEvents> {
|
||||||
let filter = &criteria.filter;
|
let filter = &criteria.filter;
|
||||||
|
|
||||||
|
@ -186,11 +192,17 @@ async fn procure_room_state(services: &Services, room_id: &RoomId) -> Result<Roo
|
||||||
Ok(state_events)
|
Ok(state_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_room_visible(services: &Services, user_id: &UserId, room_id: &RoomId, search: &Criteria) -> Result {
|
async fn check_room_visible(
|
||||||
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
search: &Criteria,
|
||||||
|
) -> Result {
|
||||||
let check_visible = search.filter.rooms.is_some();
|
let check_visible = search.filter.rooms.is_some();
|
||||||
let check_state = check_visible && search.include_state.is_some_and(is_true!());
|
let check_state = check_visible && search.include_state.is_some_and(is_true!());
|
||||||
|
|
||||||
let is_joined = !check_visible || services.rooms.state_cache.is_joined(user_id, room_id).await;
|
let is_joined =
|
||||||
|
!check_visible || services.rooms.state_cache.is_joined(user_id, room_id).await;
|
||||||
|
|
||||||
let state_visible = !check_state
|
let state_visible = !check_state
|
||||||
|| services
|
|| services
|
||||||
|
|
|
@ -17,14 +17,17 @@ use crate::{service::pdu::PduBuilder, utils, Result, Ruma};
|
||||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||||
/// allowed
|
/// allowed
|
||||||
pub(crate) async fn send_message_event_route(
|
pub(crate) async fn send_message_event_route(
|
||||||
State(services): State<crate::State>, body: Ruma<send_message_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<send_message_event::v3::Request>,
|
||||||
) -> Result<send_message_event::v3::Response> {
|
) -> Result<send_message_event::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
let appservice_info = body.appservice_info.as_ref();
|
let appservice_info = body.appservice_info.as_ref();
|
||||||
|
|
||||||
// Forbid m.room.encrypted if encryption is disabled
|
// Forbid m.room.encrypted if encryption is disabled
|
||||||
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.globals.allow_encryption() {
|
if MessageLikeEventType::RoomEncrypted == body.event_type
|
||||||
|
&& !services.globals.allow_encryption()
|
||||||
|
{
|
||||||
return Err!(Request(Forbidden("Encryption has been disabled")));
|
return Err!(Request(Forbidden("Encryption has been disabled")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +63,8 @@ pub(crate) async fn send_message_event_route(
|
||||||
let mut unsigned = BTreeMap::new();
|
let mut unsigned = BTreeMap::new();
|
||||||
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
||||||
|
|
||||||
let content =
|
let content = from_str(body.body.body.json().get())
|
||||||
from_str(body.body.body.json().get()).map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
|
.map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
|
||||||
|
|
||||||
let event_id = services
|
let event_id = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -80,13 +83,14 @@ pub(crate) async fn send_message_event_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
services
|
services.transaction_ids.add_txnid(
|
||||||
.transaction_ids
|
sender_user,
|
||||||
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes());
|
sender_device,
|
||||||
|
&body.txn_id,
|
||||||
|
event_id.as_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
Ok(send_message_event::v3::Response {
|
Ok(send_message_event::v3::Response { event_id: event_id.into() })
|
||||||
event_id: event_id.into(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ struct Claims {
|
||||||
/// the `type` field when logging in.
|
/// the `type` field when logging in.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
||||||
pub(crate) async fn get_login_types_route(
|
pub(crate) async fn get_login_types_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, _body: Ruma<get_login_types::v3::Request>,
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
_body: Ruma<get_login_types::v3::Request>,
|
||||||
) -> Result<get_login_types::v3::Response> {
|
) -> 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(PasswordLoginType::default()),
|
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
||||||
|
@ -61,13 +62,15 @@ pub(crate) async fn get_login_types_route(
|
||||||
/// supported login types.
|
/// supported login types.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
||||||
pub(crate) async fn login_route(
|
pub(crate) async fn login_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<login::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<login::v3::Request>,
|
||||||
) -> Result<login::v3::Response> {
|
) -> Result<login::v3::Response> {
|
||||||
// Validate login method
|
// Validate login method
|
||||||
// TODO: Other login methods
|
// TODO: Other login methods
|
||||||
let user_id = match &body.login_info {
|
let user_id = match &body.login_info {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
login::v3::LoginInfo::Password(login::v3::Password {
|
| login::v3::LoginInfo::Password(login::v3::Password {
|
||||||
identifier,
|
identifier,
|
||||||
password,
|
password,
|
||||||
user,
|
user,
|
||||||
|
@ -75,7 +78,10 @@ pub(crate) async fn login_route(
|
||||||
}) => {
|
}) => {
|
||||||
debug!("Got password login type");
|
debug!("Got password login type");
|
||||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
|
UserId::parse_with_server_name(
|
||||||
|
user_id.to_lowercase(),
|
||||||
|
services.globals.server_name(),
|
||||||
|
)
|
||||||
} else if let Some(user) = user {
|
} else if let Some(user) = user {
|
||||||
UserId::parse(user)
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,22 +106,29 @@ pub(crate) async fn login_route(
|
||||||
|
|
||||||
user_id
|
user_id
|
||||||
},
|
},
|
||||||
login::v3::LoginInfo::Token(login::v3::Token {
|
| login::v3::LoginInfo::Token(login::v3::Token { token }) => {
|
||||||
token,
|
|
||||||
}) => {
|
|
||||||
debug!("Got token login type");
|
debug!("Got token login type");
|
||||||
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
|
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
|
||||||
let token =
|
let token = jsonwebtoken::decode::<Claims>(
|
||||||
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
|
token,
|
||||||
.map_err(|e| {
|
jwt_decoding_key,
|
||||||
warn!("Failed to parse JWT token from user logging in: {e}");
|
&jsonwebtoken::Validation::default(),
|
||||||
Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid.")
|
)
|
||||||
})?;
|
.map_err(|e| {
|
||||||
|
warn!("Failed to parse JWT token from user logging in: {e}");
|
||||||
|
Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid.")
|
||||||
|
})?;
|
||||||
|
|
||||||
let username = token.claims.sub.to_lowercase();
|
let username = token.claims.sub.to_lowercase();
|
||||||
|
|
||||||
UserId::parse_with_server_name(username, services.globals.server_name())
|
UserId::parse_with_server_name(username, services.globals.server_name()).map_err(
|
||||||
.map_err(|e| err!(Request(InvalidUsername(debug_error!(?e, "Failed to parse login username")))))?
|
|e| {
|
||||||
|
err!(Request(InvalidUsername(debug_error!(
|
||||||
|
?e,
|
||||||
|
"Failed to parse login username"
|
||||||
|
))))
|
||||||
|
},
|
||||||
|
)?
|
||||||
} else {
|
} else {
|
||||||
return Err!(Request(Unknown(
|
return Err!(Request(Unknown(
|
||||||
"Token login is not supported (server has no jwt decoding key)."
|
"Token login is not supported (server has no jwt decoding key)."
|
||||||
|
@ -123,13 +136,16 @@ pub(crate) async fn login_route(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
| login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||||
identifier,
|
identifier,
|
||||||
user,
|
user,
|
||||||
}) => {
|
}) => {
|
||||||
debug!("Got appservice login type");
|
debug!("Got appservice login type");
|
||||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||||
UserId::parse_with_server_name(user_id.to_lowercase(), services.globals.server_name())
|
UserId::parse_with_server_name(
|
||||||
|
user_id.to_lowercase(),
|
||||||
|
services.globals.server_name(),
|
||||||
|
)
|
||||||
} else if let Some(user) = user {
|
} else if let Some(user) = user {
|
||||||
UserId::parse(user)
|
UserId::parse(user)
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,18 +159,27 @@ pub(crate) async fn login_route(
|
||||||
|
|
||||||
if let Some(ref info) = body.appservice_info {
|
if let Some(ref info) = body.appservice_info {
|
||||||
if !info.is_user_match(&user_id) {
|
if !info.is_user_match(&user_id) {
|
||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "User is not in namespace."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Exclusive,
|
||||||
|
"User is not in namespace.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::MissingToken,
|
||||||
|
"Missing appservice token.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
user_id
|
user_id
|
||||||
},
|
},
|
||||||
_ => {
|
| _ => {
|
||||||
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
|
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
|
||||||
debug!("JSON body: {:?}", &body.json_body);
|
debug!("JSON body: {:?}", &body.json_body);
|
||||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Unsupported or unknown login type."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Unsupported or unknown login type.",
|
||||||
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,7 +258,9 @@ pub(crate) async fn login_route(
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
||||||
pub(crate) async fn logout_route(
|
pub(crate) async fn logout_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<logout::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<logout::v3::Request>,
|
||||||
) -> Result<logout::v3::Response> {
|
) -> Result<logout::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 sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -261,7 +288,8 @@ pub(crate) async fn logout_route(
|
||||||
/// user.
|
/// user.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
||||||
pub(crate) async fn logout_all_route(
|
pub(crate) async fn logout_all_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<logout_all::v3::Request>,
|
body: Ruma<logout_all::v3::Request>,
|
||||||
) -> Result<logout_all::v3::Response> {
|
) -> Result<logout_all::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");
|
||||||
|
|
|
@ -13,7 +13,8 @@ use crate::{service::rooms::spaces::PaginationToken, Error, Result, Ruma};
|
||||||
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
||||||
/// of a given space.
|
/// of a given space.
|
||||||
pub(crate) async fn get_hierarchy_route(
|
pub(crate) async fn get_hierarchy_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_hierarchy::v1::Request>,
|
||||||
) -> Result<get_hierarchy::v1::Response> {
|
) -> Result<get_hierarchy::v1::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");
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ use crate::{Ruma, RumaResponse};
|
||||||
/// allowed
|
/// allowed
|
||||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub(crate) async fn send_state_event_for_key_route(
|
pub(crate) async fn send_state_event_for_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<send_state_event::v3::Response> {
|
) -> Result<send_state_event::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");
|
||||||
|
|
||||||
|
@ -63,7 +64,8 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||||
/// allowed
|
/// allowed
|
||||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||||
pub(crate) async fn send_state_event_for_empty_key_route(
|
pub(crate) async fn send_state_event_for_empty_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||||
send_state_event_for_key_route(State(services), body)
|
send_state_event_for_key_route(State(services), body)
|
||||||
.await
|
.await
|
||||||
|
@ -77,7 +79,8 @@ pub(crate) async fn send_state_event_for_empty_key_route(
|
||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_route(
|
pub(crate) async fn get_state_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_state_events::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_state_events::v3::Request>,
|
||||||
) -> Result<get_state_events::v3::Response> {
|
) -> Result<get_state_events::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");
|
||||||
|
|
||||||
|
@ -111,7 +114,8 @@ pub(crate) async fn get_state_events_route(
|
||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_key_route(
|
pub(crate) async fn get_state_events_for_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_state_events_for_key::v3::Request>,
|
||||||
) -> Result<get_state_events_for_key::v3::Response> {
|
) -> Result<get_state_events_for_key::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");
|
||||||
|
|
||||||
|
@ -157,7 +161,8 @@ pub(crate) async fn get_state_events_for_key_route(
|
||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_empty_key_route(
|
pub(crate) async fn get_state_events_for_empty_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_state_events_for_key::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
||||||
get_state_events_for_key_route(State(services), body)
|
get_state_events_for_key_route(State(services), body)
|
||||||
.await
|
.await
|
||||||
|
@ -165,8 +170,13 @@ pub(crate) async fn get_state_events_for_empty_key_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_state_event_for_key_helper(
|
async fn send_state_event_for_key_helper(
|
||||||
services: &Services, sender: &UserId, room_id: &RoomId, event_type: &StateEventType,
|
services: &Services,
|
||||||
json: &Raw<AnyStateEventContent>, state_key: String, timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
|
sender: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
event_type: &StateEventType,
|
||||||
|
json: &Raw<AnyStateEventContent>,
|
||||||
|
state_key: String,
|
||||||
|
timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
|
||||||
) -> Result<Arc<EventId>> {
|
) -> Result<Arc<EventId>> {
|
||||||
allowed_to_send_state_event(services, room_id, event_type, json).await?;
|
allowed_to_send_state_event(services, room_id, event_type, json).await?;
|
||||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||||
|
@ -191,20 +201,27 @@ async fn send_state_event_for_key_helper(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn allowed_to_send_state_event(
|
async fn allowed_to_send_state_event(
|
||||||
services: &Services, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
event_type: &StateEventType,
|
||||||
|
json: &Raw<AnyStateEventContent>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
match event_type {
|
match event_type {
|
||||||
// Forbid m.room.encryption if encryption is disabled
|
// Forbid m.room.encryption if encryption is disabled
|
||||||
StateEventType::RoomEncryption => {
|
| StateEventType::RoomEncryption =>
|
||||||
if !services.globals.allow_encryption() {
|
if !services.globals.allow_encryption() {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
return Err(Error::BadRequest(
|
||||||
}
|
ErrorKind::forbidden(),
|
||||||
},
|
"Encryption has been disabled",
|
||||||
|
));
|
||||||
|
},
|
||||||
// admin room is a sensitive room, it should not ever be made public
|
// admin room is a sensitive room, it should not ever be made public
|
||||||
StateEventType::RoomJoinRules => {
|
| StateEventType::RoomJoinRules => {
|
||||||
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
||||||
if admin_room_id == room_id {
|
if admin_room_id == room_id {
|
||||||
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
|
if let Ok(join_rule) =
|
||||||
|
serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get())
|
||||||
|
{
|
||||||
if join_rule.join_rule == JoinRule::Public {
|
if join_rule.join_rule == JoinRule::Public {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
|
@ -216,16 +233,20 @@ async fn allowed_to_send_state_event(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// admin room is a sensitive room, it should not ever be made world readable
|
// admin room is a sensitive room, it should not ever be made world readable
|
||||||
StateEventType::RoomHistoryVisibility => {
|
| StateEventType::RoomHistoryVisibility => {
|
||||||
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
||||||
if admin_room_id == room_id {
|
if admin_room_id == room_id {
|
||||||
if let Ok(visibility_content) =
|
if let Ok(visibility_content) = serde_json::from_str::<
|
||||||
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
|
RoomHistoryVisibilityEventContent,
|
||||||
|
>(json.json().get())
|
||||||
{
|
{
|
||||||
if visibility_content.history_visibility == HistoryVisibility::WorldReadable {
|
if visibility_content.history_visibility
|
||||||
|
== HistoryVisibility::WorldReadable
|
||||||
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"Admin room is not allowed to be made world readable (public room history).",
|
"Admin room is not allowed to be made world readable (public \
|
||||||
|
room history).",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,8 +254,10 @@ async fn allowed_to_send_state_event(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: allow alias if it previously existed
|
// TODO: allow alias if it previously existed
|
||||||
StateEventType::RoomCanonicalAlias => {
|
| StateEventType::RoomCanonicalAlias => {
|
||||||
if let Ok(canonical_alias) = serde_json::from_str::<RoomCanonicalAliasEventContent>(json.json().get()) {
|
if let Ok(canonical_alias) =
|
||||||
|
serde_json::from_str::<RoomCanonicalAliasEventContent>(json.json().get())
|
||||||
|
{
|
||||||
let mut aliases = canonical_alias.alt_aliases.clone();
|
let mut aliases = canonical_alias.alt_aliases.clone();
|
||||||
|
|
||||||
if let Some(alias) = canonical_alias.alias {
|
if let Some(alias) = canonical_alias.alias {
|
||||||
|
@ -243,7 +266,9 @@ async fn allowed_to_send_state_event(
|
||||||
|
|
||||||
for alias in aliases {
|
for alias in aliases {
|
||||||
if !services.globals.server_is_ours(alias.server_name()) {
|
if !services.globals.server_is_ours(alias.server_name()) {
|
||||||
return Err!(Request(Forbidden("canonical_alias must be for this server")));
|
return Err!(Request(Forbidden(
|
||||||
|
"canonical_alias must be for this server"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services
|
if !services
|
||||||
|
@ -255,13 +280,14 @@ async fn allowed_to_send_state_event(
|
||||||
// Make sure it's the right room
|
// Make sure it's the right room
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
"You are only allowed to send canonical_alias events when its aliases already exist"
|
"You are only allowed to send canonical_alias events when its \
|
||||||
|
aliases already exist"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => (),
|
| _ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -12,8 +12,12 @@ pub(crate) use self::{v3::sync_events_route, v4::sync_events_v4_route};
|
||||||
use crate::{service::Services, Error, PduEvent, Result};
|
use crate::{service::Services, Error, PduEvent, Result};
|
||||||
|
|
||||||
async fn load_timeline(
|
async fn load_timeline(
|
||||||
services: &Services, sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount,
|
services: &Services,
|
||||||
next_batch: Option<PduCount>, limit: usize,
|
sender_user: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
roomsincecount: PduCount,
|
||||||
|
next_batch: Option<PduCount>,
|
||||||
|
limit: usize,
|
||||||
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
||||||
let last_timeline_count = services
|
let last_timeline_count = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -51,7 +55,10 @@ async fn load_timeline(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn share_encrypted_room(
|
async fn share_encrypted_room(
|
||||||
services: &Services, sender_user: &UserId, user_id: &UserId, ignore_room: Option<&RoomId>,
|
services: &Services,
|
||||||
|
sender_user: &UserId,
|
||||||
|
user_id: &UserId,
|
||||||
|
ignore_room: Option<&RoomId>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -32,8 +32,9 @@ use ruma::{
|
||||||
sync::sync_events::{
|
sync::sync_events::{
|
||||||
self,
|
self,
|
||||||
v3::{
|
v3::{
|
||||||
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
|
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom,
|
||||||
RoomAccountData, RoomSummary, Rooms, State as RoomState, Timeline, ToDevice,
|
LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State as RoomState,
|
||||||
|
Timeline, ToDevice,
|
||||||
},
|
},
|
||||||
DeviceLists, UnreadNotificationsCount,
|
DeviceLists, UnreadNotificationsCount,
|
||||||
},
|
},
|
||||||
|
@ -107,7 +108,8 @@ type PresenceUpdates = HashMap<OwnedUserId, PresenceEvent>;
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub(crate) async fn sync_events_route(
|
pub(crate) async fn sync_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<sync_events::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<sync_events::v3::Request>,
|
||||||
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
||||||
let (sender_user, sender_device) = body.sender();
|
let (sender_user, sender_device) = body.sender();
|
||||||
|
|
||||||
|
@ -127,9 +129,9 @@ pub(crate) async fn sync_events_route(
|
||||||
|
|
||||||
// Load filter
|
// Load filter
|
||||||
let filter = match body.body.filter.as_ref() {
|
let filter = match body.body.filter.as_ref() {
|
||||||
None => FilterDefinition::default(),
|
| None => FilterDefinition::default(),
|
||||||
Some(Filter::FilterDefinition(ref filter)) => filter.clone(),
|
| Some(Filter::FilterDefinition(ref filter)) => filter.clone(),
|
||||||
Some(Filter::FilterId(ref filter_id)) => services
|
| Some(Filter::FilterId(ref filter_id)) => services
|
||||||
.users
|
.users
|
||||||
.get_filter(sender_user, filter_id)
|
.get_filter(sender_user, filter_id)
|
||||||
.await
|
.await
|
||||||
|
@ -138,11 +140,11 @@ pub(crate) async fn sync_events_route(
|
||||||
|
|
||||||
// some clients, at least element, seem to require knowledge of redundant
|
// some clients, at least element, seem to require knowledge of redundant
|
||||||
// members for "inline" profiles on the timeline to work properly
|
// members for "inline" profiles on the timeline to work properly
|
||||||
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options {
|
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options
|
||||||
LazyLoadOptions::Enabled {
|
{
|
||||||
include_redundant_members,
|
| LazyLoadOptions::Enabled { include_redundant_members } =>
|
||||||
} => (true, include_redundant_members),
|
(true, include_redundant_members),
|
||||||
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
|
| LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let full_state = body.body.full_state;
|
let full_state = body.body.full_state;
|
||||||
|
@ -230,9 +232,7 @@ pub(crate) async fn sync_events_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
let invited_room = InvitedRoom {
|
let invited_room = InvitedRoom {
|
||||||
invite_state: InviteState {
|
invite_state: InviteState { events: invite_state },
|
||||||
events: invite_state,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
invited_rooms.insert(room_id, invited_room);
|
invited_rooms.insert(room_id, invited_room);
|
||||||
|
@ -268,9 +268,10 @@ pub(crate) async fn sync_events_route(
|
||||||
.count_one_time_keys(sender_user, sender_device);
|
.count_one_time_keys(sender_user, sender_device);
|
||||||
|
|
||||||
// Remove all to-device events the device received *last time*
|
// Remove all to-device events the device received *last time*
|
||||||
let remove_to_device_events = services
|
let remove_to_device_events =
|
||||||
.users
|
services
|
||||||
.remove_to_device_events(sender_user, sender_device, since);
|
.users
|
||||||
|
.remove_to_device_events(sender_user, sender_device, since);
|
||||||
|
|
||||||
let rooms = join3(joined_rooms, left_rooms, invited_rooms);
|
let rooms = join3(joined_rooms, left_rooms, invited_rooms);
|
||||||
let ephemeral = join3(remove_to_device_events, to_device_events, presence_updates);
|
let ephemeral = join3(remove_to_device_events, to_device_events, presence_updates);
|
||||||
|
@ -290,7 +291,8 @@ pub(crate) async fn sync_events_route(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.stream()
|
.stream()
|
||||||
.broad_filter_map(|user_id| async move {
|
.broad_filter_map(|user_id| async move {
|
||||||
let no_shared_encrypted_room = !share_encrypted_room(&services, sender_user, &user_id, None).await;
|
let no_shared_encrypted_room =
|
||||||
|
!share_encrypted_room(&services, sender_user, &user_id, None).await;
|
||||||
no_shared_encrypted_room.then_some(user_id)
|
no_shared_encrypted_room.then_some(user_id)
|
||||||
})
|
})
|
||||||
.ready_fold(HashSet::new(), |mut device_list_left, user_id| {
|
.ready_fold(HashSet::new(), |mut device_list_left, user_id| {
|
||||||
|
@ -300,9 +302,7 @@ pub(crate) async fn sync_events_route(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let response = sync_events::v3::Response {
|
let response = sync_events::v3::Response {
|
||||||
account_data: GlobalAccountData {
|
account_data: GlobalAccountData { events: account_data },
|
||||||
events: account_data,
|
|
||||||
},
|
|
||||||
device_lists: DeviceLists {
|
device_lists: DeviceLists {
|
||||||
changed: device_list_updates.into_iter().collect(),
|
changed: device_list_updates.into_iter().collect(),
|
||||||
left: device_list_left.into_iter().collect(),
|
left: device_list_left.into_iter().collect(),
|
||||||
|
@ -324,9 +324,7 @@ pub(crate) async fn sync_events_route(
|
||||||
invite: invited_rooms,
|
invite: invited_rooms,
|
||||||
knock: BTreeMap::new(), // TODO
|
knock: BTreeMap::new(), // TODO
|
||||||
},
|
},
|
||||||
to_device: ToDevice {
|
to_device: ToDevice { events: to_device_events },
|
||||||
events: to_device_events,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Retry the endpoint instead of returning
|
// TODO: Retry the endpoint instead of returning
|
||||||
|
@ -348,7 +346,11 @@ pub(crate) async fn sync_events_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "presence", level = "debug", skip_all)]
|
#[tracing::instrument(name = "presence", level = "debug", skip_all)]
|
||||||
async fn process_presence_updates(services: &Services, since: u64, syncing_user: &UserId) -> PresenceUpdates {
|
async fn process_presence_updates(
|
||||||
|
services: &Services,
|
||||||
|
since: u64,
|
||||||
|
syncing_user: &UserId,
|
||||||
|
) -> PresenceUpdates {
|
||||||
services
|
services
|
||||||
.presence
|
.presence
|
||||||
.presence_since(since)
|
.presence_since(since)
|
||||||
|
@ -367,10 +369,10 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
|
||||||
})
|
})
|
||||||
.ready_fold(PresenceUpdates::new(), |mut updates, (user_id, event)| {
|
.ready_fold(PresenceUpdates::new(), |mut updates, (user_id, event)| {
|
||||||
match updates.entry(user_id.into()) {
|
match updates.entry(user_id.into()) {
|
||||||
Entry::Vacant(slot) => {
|
| Entry::Vacant(slot) => {
|
||||||
slot.insert(event);
|
slot.insert(event);
|
||||||
},
|
},
|
||||||
Entry::Occupied(mut slot) => {
|
| Entry::Occupied(mut slot) => {
|
||||||
let curr_event = slot.get_mut();
|
let curr_event = slot.get_mut();
|
||||||
let curr_content = &mut curr_event.content;
|
let curr_content = &mut curr_event.content;
|
||||||
let new_content = event.content;
|
let new_content = event.content;
|
||||||
|
@ -380,7 +382,8 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
|
||||||
curr_content.status_msg = new_content
|
curr_content.status_msg = new_content
|
||||||
.status_msg
|
.status_msg
|
||||||
.or_else(|| curr_content.status_msg.take());
|
.or_else(|| curr_content.status_msg.take());
|
||||||
curr_content.last_active_ago = new_content.last_active_ago.or(curr_content.last_active_ago);
|
curr_content.last_active_ago =
|
||||||
|
new_content.last_active_ago.or(curr_content.last_active_ago);
|
||||||
curr_content.displayname = new_content
|
curr_content.displayname = new_content
|
||||||
.displayname
|
.displayname
|
||||||
.or_else(|| curr_content.displayname.take());
|
.or_else(|| curr_content.displayname.take());
|
||||||
|
@ -410,8 +413,13 @@ async fn process_presence_updates(services: &Services, since: u64, syncing_user:
|
||||||
)]
|
)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn handle_left_room(
|
async fn handle_left_room(
|
||||||
services: &Services, since: u64, ref room_id: OwnedRoomId, sender_user: &UserId, next_batch_string: &str,
|
services: &Services,
|
||||||
full_state: bool, lazy_load_enabled: bool,
|
since: u64,
|
||||||
|
ref room_id: OwnedRoomId,
|
||||||
|
sender_user: &UserId,
|
||||||
|
next_batch_string: &str,
|
||||||
|
full_state: bool,
|
||||||
|
lazy_load_enabled: bool,
|
||||||
) -> Result<Option<LeftRoom>> {
|
) -> Result<Option<LeftRoom>> {
|
||||||
// Get and drop the lock to wait for remaining operations to finish
|
// Get and drop the lock to wait for remaining operations to finish
|
||||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||||
|
@ -440,7 +448,8 @@ async fn handle_left_room(
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Timestamp is valid js_int value"),
|
.expect("Timestamp is valid js_int value"),
|
||||||
kind: RoomMember,
|
kind: RoomMember,
|
||||||
content: serde_json::from_str(r#"{"membership":"leave"}"#).expect("this is valid JSON"),
|
content: serde_json::from_str(r#"{"membership":"leave"}"#)
|
||||||
|
.expect("this is valid JSON"),
|
||||||
state_key: Some(sender_user.to_string()),
|
state_key: Some(sender_user.to_string()),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
// The following keys are dropped on conversion
|
// The following keys are dropped on conversion
|
||||||
|
@ -449,16 +458,12 @@ async fn handle_left_room(
|
||||||
depth: uint!(1),
|
depth: uint!(1),
|
||||||
auth_events: vec![],
|
auth_events: vec![],
|
||||||
redacts: None,
|
redacts: None,
|
||||||
hashes: EventHash {
|
hashes: EventHash { sha256: String::new() },
|
||||||
sha256: String::new(),
|
|
||||||
},
|
|
||||||
signatures: None,
|
signatures: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(Some(LeftRoom {
|
return Ok(Some(LeftRoom {
|
||||||
account_data: RoomAccountData {
|
account_data: RoomAccountData { events: Vec::new() },
|
||||||
events: Vec::new(),
|
|
||||||
},
|
|
||||||
timeline: Timeline {
|
timeline: Timeline {
|
||||||
limited: false,
|
limited: false,
|
||||||
prev_batch: Some(next_batch_string.to_owned()),
|
prev_batch: Some(next_batch_string.to_owned()),
|
||||||
|
@ -479,8 +484,8 @@ async fn handle_left_room(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let since_state_ids = match since_shortstatehash {
|
let since_state_ids = match since_shortstatehash {
|
||||||
Ok(s) => services.rooms.state_accessor.state_full_ids(s).await?,
|
| Ok(s) => services.rooms.state_accessor.state_full_ids(s).await?,
|
||||||
Err(_) => HashMap::new(),
|
| Err(_) => HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(left_event_id): Result<OwnedEventId> = services
|
let Ok(left_event_id): Result<OwnedEventId> = services
|
||||||
|
@ -542,17 +547,14 @@ async fn handle_left_room(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(LeftRoom {
|
Ok(Some(LeftRoom {
|
||||||
account_data: RoomAccountData {
|
account_data: RoomAccountData { events: Vec::new() },
|
||||||
events: Vec::new(),
|
|
||||||
},
|
|
||||||
timeline: Timeline {
|
timeline: Timeline {
|
||||||
limited: true, // TODO: support left timeline events so we dont need to set this to true
|
limited: true, /* TODO: support left timeline events so we dont need to set this to
|
||||||
|
* true */
|
||||||
prev_batch: Some(next_batch_string.to_owned()),
|
prev_batch: Some(next_batch_string.to_owned()),
|
||||||
events: Vec::new(), // and so we dont need to set this to empty vec
|
events: Vec::new(), // and so we dont need to set this to empty vec
|
||||||
},
|
},
|
||||||
state: RoomState {
|
state: RoomState { events: left_state_events },
|
||||||
events: left_state_events,
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,8 +568,15 @@ async fn handle_left_room(
|
||||||
)]
|
)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn load_joined_room(
|
async fn load_joined_room(
|
||||||
services: &Services, sender_user: &UserId, sender_device: &DeviceId, ref room_id: OwnedRoomId, since: u64,
|
services: &Services,
|
||||||
next_batch: u64, lazy_load_enabled: bool, lazy_load_send_redundant: bool, full_state: bool,
|
sender_user: &UserId,
|
||||||
|
sender_device: &DeviceId,
|
||||||
|
ref room_id: OwnedRoomId,
|
||||||
|
since: u64,
|
||||||
|
next_batch: u64,
|
||||||
|
lazy_load_enabled: bool,
|
||||||
|
lazy_load_send_redundant: bool,
|
||||||
|
full_state: bool,
|
||||||
) -> Result<(JoinedRoom, HashSet<OwnedUserId>, HashSet<OwnedUserId>)> {
|
) -> Result<(JoinedRoom, HashSet<OwnedUserId>, HashSet<OwnedUserId>)> {
|
||||||
// Get and drop the lock to wait for remaining operations to finish
|
// Get and drop the lock to wait for remaining operations to finish
|
||||||
// This will make sure the we have all events until next_batch
|
// This will make sure the we have all events until next_batch
|
||||||
|
@ -590,18 +599,26 @@ async fn load_joined_room(
|
||||||
.ok()
|
.ok()
|
||||||
.map(Ok);
|
.map(Ok);
|
||||||
|
|
||||||
let timeline = load_timeline(services, sender_user, room_id, sincecount, Some(next_batchcount), 10_usize);
|
let timeline = load_timeline(
|
||||||
|
services,
|
||||||
|
sender_user,
|
||||||
|
room_id,
|
||||||
|
sincecount,
|
||||||
|
Some(next_batchcount),
|
||||||
|
10_usize,
|
||||||
|
);
|
||||||
|
|
||||||
let (current_shortstatehash, since_shortstatehash, timeline) =
|
let (current_shortstatehash, since_shortstatehash, timeline) =
|
||||||
try_join3(current_shortstatehash, since_shortstatehash, timeline).await?;
|
try_join3(current_shortstatehash, since_shortstatehash, timeline).await?;
|
||||||
|
|
||||||
let (timeline_pdus, limited) = timeline;
|
let (timeline_pdus, limited) = timeline;
|
||||||
let timeline_users = timeline_pdus
|
let timeline_users =
|
||||||
.iter()
|
timeline_pdus
|
||||||
.fold(HashSet::new(), |mut timeline_users, (_, event)| {
|
.iter()
|
||||||
timeline_users.insert(event.sender.as_str().to_owned());
|
.fold(HashSet::new(), |mut timeline_users, (_, event)| {
|
||||||
timeline_users
|
timeline_users.insert(event.sender.as_str().to_owned());
|
||||||
});
|
timeline_users
|
||||||
|
});
|
||||||
|
|
||||||
let last_notification_read: OptionFuture<_> = timeline_pdus
|
let last_notification_read: OptionFuture<_> = timeline_pdus
|
||||||
.is_empty()
|
.is_empty()
|
||||||
|
@ -617,13 +634,16 @@ async fn load_joined_room(
|
||||||
.is_none_or(|&count| count > since)
|
.is_none_or(|&count| count > since)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
services
|
services.rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||||
.rooms
|
sender_user,
|
||||||
.lazy_loading
|
sender_device,
|
||||||
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount);
|
room_id,
|
||||||
|
sincecount,
|
||||||
|
);
|
||||||
|
|
||||||
let no_state_changes = timeline_pdus.is_empty()
|
let no_state_changes = timeline_pdus.is_empty()
|
||||||
&& (since_shortstatehash.is_none() || since_shortstatehash.is_some_and(is_equal_to!(current_shortstatehash)));
|
&& (since_shortstatehash.is_none()
|
||||||
|
|| since_shortstatehash.is_some_and(is_equal_to!(current_shortstatehash)));
|
||||||
|
|
||||||
let mut device_list_updates = HashSet::<OwnedUserId>::new();
|
let mut device_list_updates = HashSet::<OwnedUserId>::new();
|
||||||
let mut left_encrypted_users = HashSet::<OwnedUserId>::new();
|
let mut left_encrypted_users = HashSet::<OwnedUserId>::new();
|
||||||
|
@ -732,9 +752,10 @@ async fn load_joined_room(
|
||||||
|
|
||||||
let events = join4(room_events, account_data_events, receipt_events, typing_events);
|
let events = join4(room_events, account_data_events, receipt_events, typing_events);
|
||||||
let unread_notifications = join(notification_count, highlight_count);
|
let unread_notifications = join(notification_count, highlight_count);
|
||||||
let (unread_notifications, events, device_updates) = join3(unread_notifications, events, device_updates)
|
let (unread_notifications, events, device_updates) =
|
||||||
.boxed()
|
join3(unread_notifications, events, device_updates)
|
||||||
.await;
|
.boxed()
|
||||||
|
.await;
|
||||||
|
|
||||||
let (room_events, account_data_events, receipt_events, typing_events) = events;
|
let (room_events, account_data_events, receipt_events, typing_events) = events;
|
||||||
let (notification_count, highlight_count) = unread_notifications;
|
let (notification_count, highlight_count) = unread_notifications;
|
||||||
|
@ -773,9 +794,7 @@ async fn load_joined_room(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let joined_room = JoinedRoom {
|
let joined_room = JoinedRoom {
|
||||||
account_data: RoomAccountData {
|
account_data: RoomAccountData { events: account_data_events },
|
||||||
events: account_data_events,
|
|
||||||
},
|
|
||||||
summary: RoomSummary {
|
summary: RoomSummary {
|
||||||
joined_member_count: joined_member_count.map(ruma_from_u64),
|
joined_member_count: joined_member_count.map(ruma_from_u64),
|
||||||
invited_member_count: invited_member_count.map(ruma_from_u64),
|
invited_member_count: invited_member_count.map(ruma_from_u64),
|
||||||
|
@ -786,10 +805,7 @@ async fn load_joined_room(
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
unread_notifications: UnreadNotificationsCount {
|
unread_notifications: UnreadNotificationsCount { highlight_count, notification_count },
|
||||||
highlight_count,
|
|
||||||
notification_count,
|
|
||||||
},
|
|
||||||
timeline: Timeline {
|
timeline: Timeline {
|
||||||
limited: limited || joined_since_last_sync,
|
limited: limited || joined_since_last_sync,
|
||||||
events: room_events,
|
events: room_events,
|
||||||
|
@ -805,9 +821,7 @@ async fn load_joined_room(
|
||||||
.map(PduEvent::to_sync_state_event)
|
.map(PduEvent::to_sync_state_event)
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
ephemeral: Ephemeral {
|
ephemeral: Ephemeral { events: edus },
|
||||||
events: edus,
|
|
||||||
},
|
|
||||||
unread_thread_notifications: BTreeMap::new(),
|
unread_thread_notifications: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -827,11 +841,20 @@ async fn load_joined_room(
|
||||||
)]
|
)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn calculate_state_changes(
|
async fn calculate_state_changes(
|
||||||
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
|
services: &Services,
|
||||||
lazy_load_enabled: bool, lazy_load_send_redundant: bool, full_state: bool,
|
sender_user: &UserId,
|
||||||
device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
|
sender_device: &DeviceId,
|
||||||
since_shortstatehash: Option<ShortStateHash>, current_shortstatehash: ShortStateHash,
|
room_id: &RoomId,
|
||||||
timeline_pdus: &Vec<(PduCount, PduEvent)>, timeline_users: &HashSet<String>,
|
next_batchcount: PduCount,
|
||||||
|
lazy_load_enabled: bool,
|
||||||
|
lazy_load_send_redundant: bool,
|
||||||
|
full_state: bool,
|
||||||
|
device_list_updates: &mut HashSet<OwnedUserId>,
|
||||||
|
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||||
|
since_shortstatehash: Option<ShortStateHash>,
|
||||||
|
current_shortstatehash: ShortStateHash,
|
||||||
|
timeline_pdus: &Vec<(PduCount, PduEvent)>,
|
||||||
|
timeline_users: &HashSet<String>,
|
||||||
) -> Result<StateChanges> {
|
) -> Result<StateChanges> {
|
||||||
let since_sender_member: OptionFuture<_> = since_shortstatehash
|
let since_sender_member: OptionFuture<_> = since_shortstatehash
|
||||||
.map(|short| {
|
.map(|short| {
|
||||||
|
@ -843,12 +866,13 @@ async fn calculate_state_changes(
|
||||||
})
|
})
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let joined_since_last_sync = since_sender_member
|
let joined_since_last_sync =
|
||||||
.await
|
since_sender_member
|
||||||
.flatten()
|
.await
|
||||||
.map_or(true, |content: RoomMemberEventContent| {
|
.flatten()
|
||||||
content.membership != MembershipState::Join
|
.is_none_or(|content: RoomMemberEventContent| {
|
||||||
});
|
content.membership != MembershipState::Join
|
||||||
|
});
|
||||||
|
|
||||||
if since_shortstatehash.is_none() || joined_since_last_sync {
|
if since_shortstatehash.is_none() || joined_since_last_sync {
|
||||||
calculate_state_initial(
|
calculate_state_initial(
|
||||||
|
@ -886,8 +910,14 @@ async fn calculate_state_changes(
|
||||||
#[tracing::instrument(name = "initial", level = "trace", skip_all)]
|
#[tracing::instrument(name = "initial", level = "trace", skip_all)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn calculate_state_initial(
|
async fn calculate_state_initial(
|
||||||
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
|
services: &Services,
|
||||||
lazy_load_enabled: bool, full_state: bool, current_shortstatehash: ShortStateHash,
|
sender_user: &UserId,
|
||||||
|
sender_device: &DeviceId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
next_batchcount: PduCount,
|
||||||
|
lazy_load_enabled: bool,
|
||||||
|
full_state: bool,
|
||||||
|
current_shortstatehash: ShortStateHash,
|
||||||
timeline_users: &HashSet<String>,
|
timeline_users: &HashSet<String>,
|
||||||
) -> Result<StateChanges> {
|
) -> Result<StateChanges> {
|
||||||
// Probably since = 0, we will do an initial sync
|
// Probably since = 0, we will do an initial sync
|
||||||
|
@ -956,10 +986,13 @@ async fn calculate_state_initial(
|
||||||
|
|
||||||
// The state_events above should contain all timeline_users, let's mark them as
|
// The state_events above should contain all timeline_users, let's mark them as
|
||||||
// lazy loaded.
|
// lazy loaded.
|
||||||
services
|
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
.rooms
|
sender_user,
|
||||||
.lazy_loading
|
sender_device,
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount);
|
room_id,
|
||||||
|
lazy_loaded,
|
||||||
|
next_batchcount,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(StateChanges {
|
Ok(StateChanges {
|
||||||
heroes,
|
heroes,
|
||||||
|
@ -973,13 +1006,23 @@ async fn calculate_state_initial(
|
||||||
#[tracing::instrument(name = "incremental", level = "trace", skip_all)]
|
#[tracing::instrument(name = "incremental", level = "trace", skip_all)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn calculate_state_incremental(
|
async fn calculate_state_incremental(
|
||||||
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, next_batchcount: PduCount,
|
services: &Services,
|
||||||
lazy_load_send_redundant: bool, full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>,
|
sender_user: &UserId,
|
||||||
left_encrypted_users: &mut HashSet<OwnedUserId>, since_shortstatehash: Option<ShortStateHash>,
|
sender_device: &DeviceId,
|
||||||
current_shortstatehash: ShortStateHash, timeline_pdus: &Vec<(PduCount, PduEvent)>, joined_since_last_sync: bool,
|
room_id: &RoomId,
|
||||||
|
next_batchcount: PduCount,
|
||||||
|
lazy_load_send_redundant: bool,
|
||||||
|
full_state: bool,
|
||||||
|
device_list_updates: &mut HashSet<OwnedUserId>,
|
||||||
|
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||||
|
since_shortstatehash: Option<ShortStateHash>,
|
||||||
|
current_shortstatehash: ShortStateHash,
|
||||||
|
timeline_pdus: &Vec<(PduCount, PduEvent)>,
|
||||||
|
joined_since_last_sync: bool,
|
||||||
) -> Result<StateChanges> {
|
) -> Result<StateChanges> {
|
||||||
// Incremental /sync
|
// Incremental /sync
|
||||||
let since_shortstatehash = since_shortstatehash.expect("missing since_shortstatehash on incremental sync");
|
let since_shortstatehash =
|
||||||
|
since_shortstatehash.expect("missing since_shortstatehash on incremental sync");
|
||||||
|
|
||||||
let mut delta_state_events = Vec::new();
|
let mut delta_state_events = Vec::new();
|
||||||
|
|
||||||
|
@ -994,8 +1037,10 @@ async fn calculate_state_incremental(
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(since_shortstatehash);
|
.state_full_ids(since_shortstatehash);
|
||||||
|
|
||||||
let (current_state_ids, since_state_ids): (HashMap<_, OwnedEventId>, HashMap<_, OwnedEventId>) =
|
let (current_state_ids, since_state_ids): (
|
||||||
try_join(current_state_ids, since_state_ids).await?;
|
HashMap<_, OwnedEventId>,
|
||||||
|
HashMap<_, OwnedEventId>,
|
||||||
|
) = try_join(current_state_ids, since_state_ids).await?;
|
||||||
|
|
||||||
current_state_ids
|
current_state_ids
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1044,17 +1089,19 @@ async fn calculate_state_incremental(
|
||||||
let content: RoomMemberEventContent = state_event.get_content()?;
|
let content: RoomMemberEventContent = state_event.get_content()?;
|
||||||
|
|
||||||
match content.membership {
|
match content.membership {
|
||||||
MembershipState::Join => {
|
| MembershipState::Join => {
|
||||||
// A new user joined an encrypted room
|
// A new user joined an encrypted room
|
||||||
if !share_encrypted_room(services, sender_user, &user_id, Some(room_id)).await {
|
if !share_encrypted_room(services, sender_user, &user_id, Some(room_id))
|
||||||
|
.await
|
||||||
|
{
|
||||||
device_list_updates.insert(user_id);
|
device_list_updates.insert(user_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MembershipState::Leave => {
|
| MembershipState::Leave => {
|
||||||
// Write down users that have left encrypted rooms we are in
|
// Write down users that have left encrypted rooms we are in
|
||||||
left_encrypted_users.insert(user_id);
|
left_encrypted_users.insert(user_id);
|
||||||
},
|
},
|
||||||
_ => {},
|
| _ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1139,10 +1186,13 @@ async fn calculate_state_incremental(
|
||||||
state_events.push(member_event);
|
state_events.push(member_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
services
|
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
.rooms
|
sender_user,
|
||||||
.lazy_loading
|
sender_device,
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount);
|
room_id,
|
||||||
|
lazy_loaded,
|
||||||
|
next_batchcount,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(StateChanges {
|
Ok(StateChanges {
|
||||||
heroes,
|
heroes,
|
||||||
|
@ -1154,7 +1204,9 @@ async fn calculate_state_incremental(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn calculate_counts(
|
async fn calculate_counts(
|
||||||
services: &Services, room_id: &RoomId, sender_user: &UserId,
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender_user: &UserId,
|
||||||
) -> Result<(Option<u64>, Option<u64>, Option<Vec<OwnedUserId>>)> {
|
) -> Result<(Option<u64>, Option<u64>, Option<Vec<OwnedUserId>>)> {
|
||||||
let joined_member_count = services
|
let joined_member_count = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -1168,7 +1220,8 @@ async fn calculate_counts(
|
||||||
.room_invited_count(room_id)
|
.room_invited_count(room_id)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let (joined_member_count, invited_member_count) = join(joined_member_count, invited_member_count).await;
|
let (joined_member_count, invited_member_count) =
|
||||||
|
join(joined_member_count, invited_member_count).await;
|
||||||
|
|
||||||
let small_room = joined_member_count.saturating_add(invited_member_count) > 5;
|
let small_room = joined_member_count.saturating_add(invited_member_count) > 5;
|
||||||
|
|
||||||
|
@ -1179,20 +1232,32 @@ async fn calculate_counts(
|
||||||
Ok((Some(joined_member_count), Some(invited_member_count), heroes.await))
|
Ok((Some(joined_member_count), Some(invited_member_count), heroes.await))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn calculate_heroes(services: &Services, room_id: &RoomId, sender_user: &UserId) -> Vec<OwnedUserId> {
|
async fn calculate_heroes(
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender_user: &UserId,
|
||||||
|
) -> Vec<OwnedUserId> {
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.all_pdus(sender_user, room_id)
|
.all_pdus(sender_user, room_id)
|
||||||
.ready_filter(|(_, pdu)| pdu.kind == RoomMember)
|
.ready_filter(|(_, pdu)| pdu.kind == RoomMember)
|
||||||
.fold_default(|heroes: Vec<_>, (_, pdu)| fold_hero(heroes, services, room_id, sender_user, pdu))
|
.fold_default(|heroes: Vec<_>, (_, pdu)| {
|
||||||
|
fold_hero(heroes, services, room_id, sender_user, pdu)
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fold_hero(
|
async fn fold_hero(
|
||||||
mut heroes: Vec<OwnedUserId>, services: &Services, room_id: &RoomId, sender_user: &UserId, pdu: PduEvent,
|
mut heroes: Vec<OwnedUserId>,
|
||||||
|
services: &Services,
|
||||||
|
room_id: &RoomId,
|
||||||
|
sender_user: &UserId,
|
||||||
|
pdu: PduEvent,
|
||||||
) -> Vec<OwnedUserId> {
|
) -> Vec<OwnedUserId> {
|
||||||
let Some(user_id): Option<&UserId> = pdu.state_key.as_deref().map(TryInto::try_into).flat_ok() else {
|
let Some(user_id): Option<&UserId> =
|
||||||
|
pdu.state_key.as_deref().map(TryInto::try_into).flat_ok()
|
||||||
|
else {
|
||||||
return heroes;
|
return heroes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,8 @@ const DEFAULT_BUMP_TYPES: &[TimelineEventType; 6] =
|
||||||
///
|
///
|
||||||
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
||||||
pub(crate) async fn sync_events_v4_route(
|
pub(crate) async fn sync_events_v4_route(
|
||||||
State(services): State<crate::State>, body: Ruma<sync_events::v4::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<sync_events::v4::Request>,
|
||||||
) -> Result<sync_events::v4::Response> {
|
) -> Result<sync_events::v4::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 sender_device = body.sender_device.expect("user is authenticated");
|
let sender_device = body.sender_device.expect("user is authenticated");
|
||||||
|
@ -81,16 +82,19 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalsince == 0 {
|
if globalsince == 0 {
|
||||||
services
|
services.sync.forget_sync_request_connection(
|
||||||
.sync
|
sender_user.clone(),
|
||||||
.forget_sync_request_connection(sender_user.clone(), sender_device.clone(), conn_id.clone());
|
sender_device.clone(),
|
||||||
|
conn_id.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get sticky parameters from cache
|
// Get sticky parameters from cache
|
||||||
let known_rooms =
|
let known_rooms = services.sync.update_sync_request_with_cache(
|
||||||
services
|
sender_user.clone(),
|
||||||
.sync
|
sender_device.clone(),
|
||||||
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
|
&mut body,
|
||||||
|
);
|
||||||
|
|
||||||
let all_joined_rooms: Vec<_> = services
|
let all_joined_rooms: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -125,9 +129,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
let mut device_list_changes = HashSet::new();
|
let mut device_list_changes = HashSet::new();
|
||||||
let mut device_list_left = HashSet::new();
|
let mut device_list_left = HashSet::new();
|
||||||
|
|
||||||
let mut receipts = sync_events::v4::Receipts {
|
let mut receipts = sync_events::v4::Receipts { rooms: BTreeMap::new() };
|
||||||
rooms: BTreeMap::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut account_data = sync_events::v4::AccountData {
|
let mut account_data = sync_events::v4::AccountData {
|
||||||
global: Vec::new(),
|
global: Vec::new(),
|
||||||
|
@ -168,7 +170,9 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in &all_joined_rooms {
|
for room_id in &all_joined_rooms {
|
||||||
let Ok(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id).await else {
|
let Ok(current_shortstatehash) =
|
||||||
|
services.rooms.state.get_room_shortstatehash(room_id).await
|
||||||
|
else {
|
||||||
error!("Room {room_id} has no state");
|
error!("Room {room_id} has no state");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -202,12 +206,17 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
let since_sender_member: Option<RoomMemberEventContent> = services
|
let since_sender_member: Option<RoomMemberEventContent> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_get_content(since_shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
.state_get_content(
|
||||||
|
since_shortstatehash,
|
||||||
|
&StateEventType::RoomMember,
|
||||||
|
sender_user.as_str(),
|
||||||
|
)
|
||||||
.ok()
|
.ok()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let joined_since_last_sync =
|
let joined_since_last_sync = since_sender_member
|
||||||
since_sender_member.map_or(true, |member| member.membership != MembershipState::Join);
|
.as_ref()
|
||||||
|
.is_none_or(|member| member.membership != MembershipState::Join);
|
||||||
|
|
||||||
let new_encrypted_room = encrypted_room && since_encryption.is_err();
|
let new_encrypted_room = encrypted_room && since_encryption.is_err();
|
||||||
|
|
||||||
|
@ -232,8 +241,10 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
};
|
};
|
||||||
if pdu.kind == RoomMember {
|
if pdu.kind == RoomMember {
|
||||||
if let Some(state_key) = &pdu.state_key {
|
if let Some(state_key) = &pdu.state_key {
|
||||||
let user_id = UserId::parse(state_key.clone())
|
let user_id =
|
||||||
.map_err(|_| Error::bad_database("Invalid UserId in member PDU."))?;
|
UserId::parse(state_key.clone()).map_err(|_| {
|
||||||
|
Error::bad_database("Invalid UserId in member PDU.")
|
||||||
|
})?;
|
||||||
|
|
||||||
if user_id == *sender_user {
|
if user_id == *sender_user {
|
||||||
continue;
|
continue;
|
||||||
|
@ -241,19 +252,25 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
|
|
||||||
let content: RoomMemberEventContent = pdu.get_content()?;
|
let content: RoomMemberEventContent = pdu.get_content()?;
|
||||||
match content.membership {
|
match content.membership {
|
||||||
MembershipState::Join => {
|
| MembershipState::Join => {
|
||||||
// A new user joined an encrypted room
|
// A new user joined an encrypted room
|
||||||
if !share_encrypted_room(&services, sender_user, &user_id, Some(room_id))
|
if !share_encrypted_room(
|
||||||
.await
|
&services,
|
||||||
|
sender_user,
|
||||||
|
&user_id,
|
||||||
|
Some(room_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
device_list_changes.insert(user_id);
|
device_list_changes.insert(user_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MembershipState::Leave => {
|
| MembershipState::Leave => {
|
||||||
// Write down users that have left encrypted rooms we are in
|
// Write down users that have left encrypted rooms we
|
||||||
|
// are in
|
||||||
left_encrypted_users.insert(user_id);
|
left_encrypted_users.insert(user_id);
|
||||||
},
|
},
|
||||||
_ => {},
|
| _ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +310,8 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
for user_id in left_encrypted_users {
|
for user_id in left_encrypted_users {
|
||||||
let dont_share_encrypted_room = !share_encrypted_room(&services, sender_user, &user_id, None).await;
|
let dont_share_encrypted_room =
|
||||||
|
!share_encrypted_room(&services, sender_user, &user_id, None).await;
|
||||||
|
|
||||||
// If the user doesn't share an encrypted room with the target anymore, we need
|
// If the user doesn't share an encrypted room with the target anymore, we need
|
||||||
// to tell them
|
// to tell them
|
||||||
|
@ -308,85 +326,85 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
|
|
||||||
for (list_id, list) in &body.lists {
|
for (list_id, list) in &body.lists {
|
||||||
let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) {
|
let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) {
|
||||||
Some(true) => &all_invited_rooms,
|
| Some(true) => &all_invited_rooms,
|
||||||
Some(false) => &all_joined_rooms,
|
| Some(false) => &all_joined_rooms,
|
||||||
None => &all_rooms,
|
| None => &all_rooms,
|
||||||
};
|
};
|
||||||
|
|
||||||
let active_rooms = match list.filters.clone().map(|f| f.not_room_types) {
|
let active_rooms = match list.filters.clone().map(|f| f.not_room_types) {
|
||||||
Some(filter) if filter.is_empty() => active_rooms.clone(),
|
| Some(filter) if filter.is_empty() => active_rooms.clone(),
|
||||||
Some(value) => filter_rooms(&services, active_rooms, &value, true).await,
|
| Some(value) => filter_rooms(&services, active_rooms, &value, true).await,
|
||||||
None => active_rooms.clone(),
|
| None => active_rooms.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let active_rooms = match list.filters.clone().map(|f| f.room_types) {
|
let active_rooms = match list.filters.clone().map(|f| f.room_types) {
|
||||||
Some(filter) if filter.is_empty() => active_rooms.clone(),
|
| Some(filter) if filter.is_empty() => active_rooms.clone(),
|
||||||
Some(value) => filter_rooms(&services, &active_rooms, &value, false).await,
|
| Some(value) => filter_rooms(&services, &active_rooms, &value, false).await,
|
||||||
None => active_rooms,
|
| None => active_rooms,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_known_rooms = BTreeSet::new();
|
let mut new_known_rooms = BTreeSet::new();
|
||||||
|
|
||||||
let ranges = list.ranges.clone();
|
let ranges = list.ranges.clone();
|
||||||
lists.insert(
|
lists.insert(list_id.clone(), sync_events::v4::SyncList {
|
||||||
list_id.clone(),
|
ops: ranges
|
||||||
sync_events::v4::SyncList {
|
.into_iter()
|
||||||
ops: ranges
|
.map(|mut r| {
|
||||||
.into_iter()
|
r.0 = r.0.clamp(
|
||||||
.map(|mut r| {
|
uint!(0),
|
||||||
r.0 = r.0.clamp(
|
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
||||||
uint!(0),
|
);
|
||||||
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
r.1 = r.1.clamp(
|
||||||
|
r.0,
|
||||||
|
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
|
||||||
|
);
|
||||||
|
|
||||||
|
let room_ids = if !active_rooms.is_empty() {
|
||||||
|
active_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
new_known_rooms.extend(room_ids.iter().cloned());
|
||||||
|
for room_id in &room_ids {
|
||||||
|
let todo_room = todo_rooms.entry(room_id.clone()).or_insert((
|
||||||
|
BTreeSet::new(),
|
||||||
|
0_usize,
|
||||||
|
u64::MAX,
|
||||||
|
));
|
||||||
|
|
||||||
|
let limit: usize = list
|
||||||
|
.room_details
|
||||||
|
.timeline_limit
|
||||||
|
.map(u64::from)
|
||||||
|
.map_or(10, usize_from_u64_truncated)
|
||||||
|
.min(100);
|
||||||
|
|
||||||
|
todo_room
|
||||||
|
.0
|
||||||
|
.extend(list.room_details.required_state.iter().cloned());
|
||||||
|
|
||||||
|
todo_room.1 = todo_room.1.max(limit);
|
||||||
|
// 0 means unknown because it got out of date
|
||||||
|
todo_room.2 = todo_room.2.min(
|
||||||
|
known_rooms
|
||||||
|
.get(list_id.as_str())
|
||||||
|
.and_then(|k| k.get(room_id))
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0),
|
||||||
);
|
);
|
||||||
r.1 =
|
}
|
||||||
r.1.clamp(r.0, UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX));
|
sync_events::v4::SyncOp {
|
||||||
|
op: SlidingOp::Sync,
|
||||||
let room_ids = if !active_rooms.is_empty() {
|
range: Some(r),
|
||||||
active_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
|
index: None,
|
||||||
} else {
|
room_ids,
|
||||||
Vec::new()
|
room_id: None,
|
||||||
};
|
}
|
||||||
|
})
|
||||||
new_known_rooms.extend(room_ids.iter().cloned());
|
.collect(),
|
||||||
for room_id in &room_ids {
|
count: ruma_from_usize(active_rooms.len()),
|
||||||
let todo_room =
|
});
|
||||||
todo_rooms
|
|
||||||
.entry(room_id.clone())
|
|
||||||
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
|
|
||||||
|
|
||||||
let limit: usize = list
|
|
||||||
.room_details
|
|
||||||
.timeline_limit
|
|
||||||
.map(u64::from)
|
|
||||||
.map_or(10, usize_from_u64_truncated)
|
|
||||||
.min(100);
|
|
||||||
|
|
||||||
todo_room
|
|
||||||
.0
|
|
||||||
.extend(list.room_details.required_state.iter().cloned());
|
|
||||||
|
|
||||||
todo_room.1 = todo_room.1.max(limit);
|
|
||||||
// 0 means unknown because it got out of date
|
|
||||||
todo_room.2 = todo_room.2.min(
|
|
||||||
known_rooms
|
|
||||||
.get(list_id.as_str())
|
|
||||||
.and_then(|k| k.get(room_id))
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
sync_events::v4::SyncOp {
|
|
||||||
op: SlidingOp::Sync,
|
|
||||||
range: Some(r),
|
|
||||||
index: None,
|
|
||||||
room_ids,
|
|
||||||
room_id: None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
count: ruma_from_usize(active_rooms.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services.sync.update_sync_known_rooms(
|
services.sync.update_sync_known_rooms(
|
||||||
|
@ -405,9 +423,10 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
if !services.rooms.metadata.exists(room_id).await {
|
if !services.rooms.metadata.exists(room_id).await {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let todo_room = todo_rooms
|
let todo_room =
|
||||||
.entry(room_id.clone())
|
todo_rooms
|
||||||
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
|
.entry(room_id.clone())
|
||||||
|
.or_insert((BTreeSet::new(), 0_usize, u64::MAX));
|
||||||
|
|
||||||
let limit: usize = room
|
let limit: usize = room
|
||||||
.timeline_limit
|
.timeline_limit
|
||||||
|
@ -471,14 +490,22 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
|
|
||||||
(timeline_pdus, limited) = (Vec::new(), true);
|
(timeline_pdus, limited) = (Vec::new(), true);
|
||||||
} else {
|
} else {
|
||||||
(timeline_pdus, limited) =
|
(timeline_pdus, limited) = match load_timeline(
|
||||||
match load_timeline(&services, sender_user, room_id, roomsincecount, None, *timeline_limit).await {
|
&services,
|
||||||
Ok(value) => value,
|
sender_user,
|
||||||
Err(err) => {
|
room_id,
|
||||||
warn!("Encountered missing timeline in {}, error {}", room_id, err);
|
roomsincecount,
|
||||||
continue;
|
None,
|
||||||
},
|
*timeline_limit,
|
||||||
};
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Ok(value) => value,
|
||||||
|
| Err(err) => {
|
||||||
|
warn!("Encountered missing timeline in {}, error {}", room_id, err);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
account_data.rooms.insert(
|
account_data.rooms.insert(
|
||||||
|
@ -543,11 +570,11 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
.first()
|
.first()
|
||||||
.map_or(Ok::<_, Error>(None), |(pdu_count, _)| {
|
.map_or(Ok::<_, Error>(None), |(pdu_count, _)| {
|
||||||
Ok(Some(match pdu_count {
|
Ok(Some(match pdu_count {
|
||||||
PduCount::Backfilled(_) => {
|
| PduCount::Backfilled(_) => {
|
||||||
error!("timeline in backfill state?!");
|
error!("timeline in backfill state?!");
|
||||||
"0".to_owned()
|
"0".to_owned()
|
||||||
},
|
},
|
||||||
PduCount::Normal(c) => c.to_string(),
|
| PduCount::Normal(c) => c.to_string(),
|
||||||
}))
|
}))
|
||||||
})?
|
})?
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
@ -568,7 +595,9 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
|
|
||||||
for (_, pdu) in timeline_pdus {
|
for (_, pdu) in timeline_pdus {
|
||||||
let ts = MilliSecondsSinceUnixEpoch(pdu.origin_server_ts);
|
let ts = MilliSecondsSinceUnixEpoch(pdu.origin_server_ts);
|
||||||
if DEFAULT_BUMP_TYPES.contains(pdu.event_type()) && timestamp.is_none_or(|time| time <= ts) {
|
if DEFAULT_BUMP_TYPES.contains(pdu.event_type())
|
||||||
|
&& timestamp.is_none_or(|time| time <= ts)
|
||||||
|
{
|
||||||
timestamp = Some(ts);
|
timestamp = Some(ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +640,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let name = match heroes.len().cmp(&(1_usize)) {
|
let name = match heroes.len().cmp(&(1_usize)) {
|
||||||
Ordering::Greater => {
|
| Ordering::Greater => {
|
||||||
let firsts = heroes[1..]
|
let firsts = heroes[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| h.name.clone().unwrap_or_else(|| h.user_id.to_string()))
|
.map(|h| h.name.clone().unwrap_or_else(|| h.user_id.to_string()))
|
||||||
|
@ -625,13 +654,13 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
|
|
||||||
Some(format!("{firsts} and {last}"))
|
Some(format!("{firsts} and {last}"))
|
||||||
},
|
},
|
||||||
Ordering::Equal => Some(
|
| Ordering::Equal => Some(
|
||||||
heroes[0]
|
heroes[0]
|
||||||
.name
|
.name
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| heroes[0].user_id.to_string()),
|
.unwrap_or_else(|| heroes[0].user_id.to_string()),
|
||||||
),
|
),
|
||||||
Ordering::Less => None,
|
| Ordering::Less => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let heroes_avatar = if heroes.len() == 1 {
|
let heroes_avatar = if heroes.len() == 1 {
|
||||||
|
@ -640,77 +669,74 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
rooms.insert(
|
rooms.insert(room_id.clone(), sync_events::v4::SlidingSyncRoom {
|
||||||
room_id.clone(),
|
name: services
|
||||||
sync_events::v4::SlidingSyncRoom {
|
.rooms
|
||||||
name: services
|
.state_accessor
|
||||||
.rooms
|
.get_name(room_id)
|
||||||
.state_accessor
|
.await
|
||||||
.get_name(room_id)
|
.ok()
|
||||||
.await
|
.or(name),
|
||||||
.ok()
|
avatar: if let Some(heroes_avatar) = heroes_avatar {
|
||||||
.or(name),
|
ruma::JsOption::Some(heroes_avatar)
|
||||||
avatar: if let Some(heroes_avatar) = heroes_avatar {
|
} else {
|
||||||
ruma::JsOption::Some(heroes_avatar)
|
match services.rooms.state_accessor.get_avatar(room_id).await {
|
||||||
} else {
|
| ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
|
||||||
match services.rooms.state_accessor.get_avatar(room_id).await {
|
| ruma::JsOption::Null => ruma::JsOption::Null,
|
||||||
ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
|
| ruma::JsOption::Undefined => ruma::JsOption::Undefined,
|
||||||
ruma::JsOption::Null => ruma::JsOption::Null,
|
}
|
||||||
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initial: Some(roomsince == &0),
|
|
||||||
is_dm: None,
|
|
||||||
invite_state,
|
|
||||||
unread_notifications: UnreadNotificationsCount {
|
|
||||||
highlight_count: Some(
|
|
||||||
services
|
|
||||||
.rooms
|
|
||||||
.user
|
|
||||||
.highlight_count(sender_user, room_id)
|
|
||||||
.await
|
|
||||||
.try_into()
|
|
||||||
.expect("notification count can't go that high"),
|
|
||||||
),
|
|
||||||
notification_count: Some(
|
|
||||||
services
|
|
||||||
.rooms
|
|
||||||
.user
|
|
||||||
.notification_count(sender_user, room_id)
|
|
||||||
.await
|
|
||||||
.try_into()
|
|
||||||
.expect("notification count can't go that high"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
timeline: room_events,
|
|
||||||
required_state,
|
|
||||||
prev_batch,
|
|
||||||
limited,
|
|
||||||
joined_count: Some(
|
|
||||||
services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_joined_count(room_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(0)
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or_else(|_| uint!(0)),
|
|
||||||
),
|
|
||||||
invited_count: Some(
|
|
||||||
services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_invited_count(room_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(0)
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or_else(|_| uint!(0)),
|
|
||||||
),
|
|
||||||
num_live: None, // Count events in timeline greater than global sync counter
|
|
||||||
timestamp,
|
|
||||||
heroes: Some(heroes),
|
|
||||||
},
|
},
|
||||||
);
|
initial: Some(roomsince == &0),
|
||||||
|
is_dm: None,
|
||||||
|
invite_state,
|
||||||
|
unread_notifications: UnreadNotificationsCount {
|
||||||
|
highlight_count: Some(
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.user
|
||||||
|
.highlight_count(sender_user, room_id)
|
||||||
|
.await
|
||||||
|
.try_into()
|
||||||
|
.expect("notification count can't go that high"),
|
||||||
|
),
|
||||||
|
notification_count: Some(
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.user
|
||||||
|
.notification_count(sender_user, room_id)
|
||||||
|
.await
|
||||||
|
.try_into()
|
||||||
|
.expect("notification count can't go that high"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
timeline: room_events,
|
||||||
|
required_state,
|
||||||
|
prev_batch,
|
||||||
|
limited,
|
||||||
|
joined_count: Some(
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.room_joined_count(room_id)
|
||||||
|
.await
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_| uint!(0)),
|
||||||
|
),
|
||||||
|
invited_count: Some(
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.room_invited_count(room_id)
|
||||||
|
.await
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_| uint!(0)),
|
||||||
|
),
|
||||||
|
num_live: None, // Count events in timeline greater than global sync counter
|
||||||
|
timestamp,
|
||||||
|
heroes: Some(heroes),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if rooms
|
if rooms
|
||||||
|
@ -757,16 +783,17 @@ pub(crate) async fn sync_events_v4_route(
|
||||||
},
|
},
|
||||||
account_data,
|
account_data,
|
||||||
receipts,
|
receipts,
|
||||||
typing: sync_events::v4::Typing {
|
typing: sync_events::v4::Typing { rooms: BTreeMap::new() },
|
||||||
rooms: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
delta_token: None,
|
delta_token: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn filter_rooms(
|
async fn filter_rooms(
|
||||||
services: &Services, rooms: &[OwnedRoomId], filter: &[RoomTypeFilter], negate: bool,
|
services: &Services,
|
||||||
|
rooms: &[OwnedRoomId],
|
||||||
|
filter: &[RoomTypeFilter],
|
||||||
|
negate: bool,
|
||||||
) -> Vec<OwnedRoomId> {
|
) -> Vec<OwnedRoomId> {
|
||||||
rooms
|
rooms
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -17,7 +17,8 @@ use crate::{Result, Ruma};
|
||||||
///
|
///
|
||||||
/// - Inserts the tag into the tag event of the room account data.
|
/// - Inserts the tag into the tag event of the room account data.
|
||||||
pub(crate) async fn update_tag_route(
|
pub(crate) async fn update_tag_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_tag::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_tag::v3::Request>,
|
||||||
) -> Result<create_tag::v3::Response> {
|
) -> Result<create_tag::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");
|
||||||
|
|
||||||
|
@ -26,9 +27,7 @@ pub(crate) async fn update_tag_route(
|
||||||
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tags_event
|
tags_event
|
||||||
|
@ -55,7 +54,8 @@ pub(crate) async fn update_tag_route(
|
||||||
///
|
///
|
||||||
/// - Removes the tag from the tag event of the room account data.
|
/// - Removes the tag from the tag event of the room account data.
|
||||||
pub(crate) async fn delete_tag_route(
|
pub(crate) async fn delete_tag_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_tag::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_tag::v3::Request>,
|
||||||
) -> Result<delete_tag::v3::Response> {
|
) -> Result<delete_tag::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");
|
||||||
|
|
||||||
|
@ -64,9 +64,7 @@ pub(crate) async fn delete_tag_route(
|
||||||
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
tags_event.content.tags.remove(&body.tag.clone().into());
|
||||||
|
@ -90,7 +88,8 @@ pub(crate) async fn delete_tag_route(
|
||||||
///
|
///
|
||||||
/// - Gets the tag event of the room account data.
|
/// - Gets the tag event of the room account data.
|
||||||
pub(crate) async fn get_tags_route(
|
pub(crate) async fn get_tags_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_tags::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_tags::v3::Request>,
|
||||||
) -> Result<get_tags::v3::Response> {
|
) -> Result<get_tags::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");
|
||||||
|
|
||||||
|
@ -99,12 +98,8 @@ pub(crate) async fn get_tags_route(
|
||||||
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
.get_room(&body.room_id, sender_user, RoomAccountDataEventType::Tag)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(TagEvent {
|
.unwrap_or(TagEvent {
|
||||||
content: TagEventContent {
|
content: TagEventContent { tags: BTreeMap::new() },
|
||||||
tags: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(get_tags::v3::Response {
|
Ok(get_tags::v3::Response { tags: tags_event.content.tags })
|
||||||
tags: tags_event.content.tags,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,7 @@ pub(crate) async fn get_protocols_route(
|
||||||
_body: Ruma<get_protocols::v3::Request>,
|
_body: Ruma<get_protocols::v3::Request>,
|
||||||
) -> Result<get_protocols::v3::Response> {
|
) -> Result<get_protocols::v3::Response> {
|
||||||
// TODO
|
// TODO
|
||||||
Ok(get_protocols::v3::Response {
|
Ok(get_protocols::v3::Response { protocols: BTreeMap::new() })
|
||||||
protocols: BTreeMap::new(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/unstable/thirdparty/protocols`
|
/// # `GET /_matrix/client/unstable/thirdparty/protocols`
|
||||||
|
|
|
@ -7,7 +7,8 @@ use crate::{Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
||||||
pub(crate) async fn get_threads_route(
|
pub(crate) async fn get_threads_route(
|
||||||
State(services): State<crate::State>, ref body: Ruma<get_threads::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
ref body: Ruma<get_threads::v1::Request>,
|
||||||
) -> Result<get_threads::v1::Response> {
|
) -> Result<get_threads::v1::Response> {
|
||||||
// Use limit or else 10, with maximum 100
|
// Use limit or else 10, with maximum 100
|
||||||
let limit = body
|
let limit = body
|
||||||
|
|
|
@ -17,7 +17,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// Send a to-device event to a set of client devices.
|
/// Send a to-device event to a set of client devices.
|
||||||
pub(crate) async fn send_event_to_device_route(
|
pub(crate) async fn send_event_to_device_route(
|
||||||
State(services): State<crate::State>, body: Ruma<send_event_to_device::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<send_event_to_device::v3::Request>,
|
||||||
) -> Result<send_event_to_device::v3::Response> {
|
) -> Result<send_event_to_device::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 sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
|
@ -43,12 +44,14 @@ pub(crate) async fn send_event_to_device_route(
|
||||||
|
|
||||||
services.sending.send_edu_server(
|
services.sending.send_edu_server(
|
||||||
target_user_id.server_name(),
|
target_user_id.server_name(),
|
||||||
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
|
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(
|
||||||
sender: sender_user.clone(),
|
DirectDeviceContent {
|
||||||
ev_type: body.event_type.clone(),
|
sender: sender_user.clone(),
|
||||||
message_id: count.to_string().into(),
|
ev_type: body.event_type.clone(),
|
||||||
messages,
|
message_id: count.to_string().into(),
|
||||||
}))
|
messages,
|
||||||
|
},
|
||||||
|
))
|
||||||
.expect("DirectToDevice EDU can be serialized"),
|
.expect("DirectToDevice EDU can be serialized"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -62,14 +65,20 @@ pub(crate) async fn send_event_to_device_route(
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
|
||||||
|
|
||||||
match target_device_id_maybe {
|
match target_device_id_maybe {
|
||||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.add_to_device_event(sender_user, target_user_id, target_device_id, event_type, event)
|
.add_to_device_event(
|
||||||
|
sender_user,
|
||||||
|
target_user_id,
|
||||||
|
target_device_id,
|
||||||
|
event_type,
|
||||||
|
event,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
},
|
},
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
| DeviceIdOrAllDevices::AllDevices => {
|
||||||
let (event_type, event) = (&event_type, &event);
|
let (event_type, event) = (&event_type, &event);
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
|
|
|
@ -7,7 +7,8 @@ use crate::{utils, Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Sets the typing state of the sender user.
|
/// Sets the typing state of the sender user.
|
||||||
pub(crate) async fn create_typing_event_route(
|
pub(crate) async fn create_typing_event_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_typing_event::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_typing_event::v3::Request>,
|
||||||
) -> Result<create_typing_event::v3::Response> {
|
) -> Result<create_typing_event::v3::Response> {
|
||||||
use create_typing_event::v3::Typing;
|
use create_typing_event::v3::Typing;
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ use ruma::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
membership::mutual_rooms,
|
membership::mutual_rooms,
|
||||||
profile::{
|
profile::{
|
||||||
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key, set_profile_key,
|
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key,
|
||||||
set_timezone_key,
|
set_profile_key, set_timezone_key,
|
||||||
},
|
},
|
||||||
room::get_summary,
|
room::get_summary,
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,8 @@ use crate::{Error, Result, Ruma, RumaResponse};
|
||||||
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
|
||||||
pub(crate) async fn get_mutual_rooms_route(
|
pub(crate) async fn get_mutual_rooms_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<mutual_rooms::unstable::Request>,
|
body: Ruma<mutual_rooms::unstable::Request>,
|
||||||
) -> Result<mutual_rooms::unstable::Response> {
|
) -> Result<mutual_rooms::unstable::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");
|
||||||
|
@ -47,10 +48,7 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services.users.exists(&body.user_id).await {
|
if !services.users.exists(&body.user_id).await {
|
||||||
return Ok(mutual_rooms::unstable::Response {
|
return Ok(mutual_rooms::unstable::Response { joined: vec![], next_batch_token: None });
|
||||||
joined: vec![],
|
|
||||||
next_batch_token: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mutual_rooms: Vec<OwnedRoomId> = services
|
let mutual_rooms: Vec<OwnedRoomId> = services
|
||||||
|
@ -77,7 +75,8 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||||
///
|
///
|
||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
pub(crate) async fn get_room_summary_legacy(
|
pub(crate) async fn get_room_summary_legacy(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_summary::msc3266::Request>,
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
||||||
get_room_summary(State(services), InsecureClientIp(client), body)
|
get_room_summary(State(services), InsecureClientIp(client), body)
|
||||||
|
@ -94,7 +93,8 @@ pub(crate) async fn get_room_summary_legacy(
|
||||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
||||||
pub(crate) async fn get_room_summary(
|
pub(crate) async fn get_room_summary(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_summary::msc3266::Request>,
|
body: Ruma<get_summary::msc3266::Request>,
|
||||||
) -> Result<get_summary::msc3266::Response> {
|
) -> Result<get_summary::msc3266::Response> {
|
||||||
let sender_user = body.sender_user.as_ref();
|
let sender_user = body.sender_user.as_ref();
|
||||||
|
@ -194,7 +194,8 @@ pub(crate) async fn get_room_summary(
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn delete_timezone_key_route(
|
pub(crate) async fn delete_timezone_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_timezone_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_timezone_key::unstable::Request>,
|
||||||
) -> Result<delete_timezone_key::unstable::Response> {
|
) -> Result<delete_timezone_key::unstable::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");
|
||||||
|
|
||||||
|
@ -221,7 +222,8 @@ pub(crate) async fn delete_timezone_key_route(
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub(crate) async fn set_timezone_key_route(
|
pub(crate) async fn set_timezone_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_timezone_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_timezone_key::unstable::Request>,
|
||||||
) -> Result<set_timezone_key::unstable::Response> {
|
) -> Result<set_timezone_key::unstable::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");
|
||||||
|
|
||||||
|
@ -248,7 +250,8 @@ pub(crate) async fn set_timezone_key_route(
|
||||||
///
|
///
|
||||||
/// This also handles the avatar_url and displayname being updated.
|
/// This also handles the avatar_url and displayname being updated.
|
||||||
pub(crate) async fn set_profile_key_route(
|
pub(crate) async fn set_profile_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<set_profile_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<set_profile_key::unstable::Request>,
|
||||||
) -> Result<set_profile_key::unstable::Response> {
|
) -> Result<set_profile_key::unstable::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");
|
||||||
|
|
||||||
|
@ -264,7 +267,9 @@ pub(crate) async fn set_profile_key_route(
|
||||||
|
|
||||||
if body.kv_pair.len() > 1 {
|
if body.kv_pair.len() > 1 {
|
||||||
// TODO: support PATCH or "recursively" adding keys in some sort
|
// TODO: support PATCH or "recursively" adding keys in some sort
|
||||||
return Err!(Request(BadJson("This endpoint can only take one key-value pair at a time")));
|
return Err!(Request(BadJson(
|
||||||
|
"This endpoint can only take one key-value pair at a time"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(profile_key_value) = body.kv_pair.get(&body.key) else {
|
let Some(profile_key_value) = body.kv_pair.get(&body.key) else {
|
||||||
|
@ -294,7 +299,13 @@ pub(crate) async fn set_profile_key_route(
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
update_displayname(&services, &body.user_id, Some(profile_key_value.to_string()), &all_joined_rooms).await;
|
update_displayname(
|
||||||
|
&services,
|
||||||
|
&body.user_id,
|
||||||
|
Some(profile_key_value.to_string()),
|
||||||
|
&all_joined_rooms,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
} else if body.key == "avatar_url" {
|
} else if body.key == "avatar_url" {
|
||||||
let mxc = ruma::OwnedMxcUri::from(profile_key_value.to_string());
|
let mxc = ruma::OwnedMxcUri::from(profile_key_value.to_string());
|
||||||
|
|
||||||
|
@ -330,7 +341,8 @@ pub(crate) async fn set_profile_key_route(
|
||||||
///
|
///
|
||||||
/// This also handles the avatar_url and displayname being updated.
|
/// This also handles the avatar_url and displayname being updated.
|
||||||
pub(crate) async fn delete_profile_key_route(
|
pub(crate) async fn delete_profile_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<delete_profile_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<delete_profile_key::unstable::Request>,
|
||||||
) -> Result<delete_profile_key::unstable::Response> {
|
) -> Result<delete_profile_key::unstable::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");
|
||||||
|
|
||||||
|
@ -340,7 +352,9 @@ pub(crate) async fn delete_profile_key_route(
|
||||||
|
|
||||||
if body.kv_pair.len() > 1 {
|
if body.kv_pair.len() > 1 {
|
||||||
// TODO: support PATCH or "recursively" adding keys in some sort
|
// TODO: support PATCH or "recursively" adding keys in some sort
|
||||||
return Err!(Request(BadJson("This endpoint can only take one key-value pair at a time")));
|
return Err!(Request(BadJson(
|
||||||
|
"This endpoint can only take one key-value pair at a time"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.key == "displayname" {
|
if body.key == "displayname" {
|
||||||
|
@ -387,7 +401,8 @@ pub(crate) async fn delete_profile_key_route(
|
||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// `timezone` over federation
|
/// `timezone` over federation
|
||||||
pub(crate) async fn get_timezone_key_route(
|
pub(crate) async fn get_timezone_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_timezone_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_timezone_key::unstable::Request>,
|
||||||
) -> Result<get_timezone_key::unstable::Response> {
|
) -> Result<get_timezone_key::unstable::Response> {
|
||||||
if !services.globals.user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
// Create and update our local copy of the user
|
// Create and update our local copy of the user
|
||||||
|
@ -422,9 +437,7 @@ pub(crate) async fn get_timezone_key_route(
|
||||||
.users
|
.users
|
||||||
.set_timezone(&body.user_id, response.tz.clone());
|
.set_timezone(&body.user_id, response.tz.clone());
|
||||||
|
|
||||||
return Ok(get_timezone_key::unstable::Response {
|
return Ok(get_timezone_key::unstable::Response { tz: response.tz });
|
||||||
tz: response.tz,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +459,8 @@ pub(crate) async fn get_timezone_key_route(
|
||||||
/// - If user is on another server and we do not have a local copy already fetch
|
/// - If user is on another server and we do not have a local copy already fetch
|
||||||
/// `timezone` over federation
|
/// `timezone` over federation
|
||||||
pub(crate) async fn get_profile_key_route(
|
pub(crate) async fn get_profile_key_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_profile_key::unstable::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_profile_key::unstable::Request>,
|
||||||
) -> Result<get_profile_key::unstable::Response> {
|
) -> Result<get_profile_key::unstable::Response> {
|
||||||
let mut profile_key_value: BTreeMap<String, serde_json::Value> = BTreeMap::new();
|
let mut profile_key_value: BTreeMap<String, serde_json::Value> = BTreeMap::new();
|
||||||
|
|
||||||
|
@ -492,9 +506,7 @@ pub(crate) async fn get_profile_key_route(
|
||||||
return Err!(Request(NotFound("The requested profile key does not exist.")));
|
return Err!(Request(NotFound("The requested profile key does not exist.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(get_profile_key::unstable::Response {
|
return Ok(get_profile_key::unstable::Response { value: profile_key_value });
|
||||||
value: profile_key_value,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +522,5 @@ pub(crate) async fn get_profile_key_route(
|
||||||
return Err!(Request(NotFound("The requested profile key does not exist.")));
|
return Err!(Request(NotFound("The requested profile key does not exist.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_profile_key::unstable::Response {
|
Ok(get_profile_key::unstable::Response { value: profile_key_value })
|
||||||
value: profile_key_value,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,9 @@ pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||||
/// conduwuit-specific API to return the amount of users registered on this
|
/// conduwuit-specific API to return the amount of users registered on this
|
||||||
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
|
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
|
||||||
/// only includes active users (not deactivated, no guests, etc)
|
/// only includes active users (not deactivated, no guests, etc)
|
||||||
pub(crate) async fn conduwuit_local_user_count(State(services): State<crate::State>) -> Result<impl IntoResponse> {
|
pub(crate) async fn conduwuit_local_user_count(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
let user_count = services.users.list_local_users().count().await;
|
let user_count = services.users.list_local_users().count().await;
|
||||||
|
|
||||||
Ok(Json(serde_json::json!({
|
Ok(Json(serde_json::json!({
|
||||||
|
|
|
@ -18,7 +18,8 @@ use crate::{Result, Ruma};
|
||||||
/// - Hides any local users that aren't in any public rooms (i.e. those that
|
/// - Hides any local users that aren't in any public rooms (i.e. those that
|
||||||
/// have the join rule set to public) and don't share a room with the sender
|
/// have the join rule set to public) and don't share a room with the sender
|
||||||
pub(crate) async fn search_users_route(
|
pub(crate) async fn search_users_route(
|
||||||
State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<search_users::v3::Request>,
|
||||||
) -> Result<search_users::v3::Response> {
|
) -> Result<search_users::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 limit = usize::try_from(body.limit).map_or(10, usize::from).min(100); // default limit is 10
|
let limit = usize::try_from(body.limit).map_or(10, usize::from).min(100); // default limit is 10
|
||||||
|
@ -61,7 +62,11 @@ pub(crate) async fn search_users_route(
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomJoinRulesEventContent>(room, &StateEventType::RoomJoinRules, "")
|
.room_state_get_content::<RoomJoinRulesEventContent>(
|
||||||
|
room,
|
||||||
|
&StateEventType::RoomJoinRules,
|
||||||
|
"",
|
||||||
|
)
|
||||||
.map_ok_or(false, |content| content.join_rule == JoinRule::Public)
|
.map_ok_or(false, |content| content.join_rule == JoinRule::Public)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -89,8 +94,5 @@ pub(crate) async fn search_users_route(
|
||||||
|
|
||||||
let results = users.take(limit).collect().await;
|
let results = users.take(limit).collect().await;
|
||||||
|
|
||||||
Ok(search_users::v3::Response {
|
Ok(search_users::v3::Response { results, limited })
|
||||||
results,
|
|
||||||
limited,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ type HmacSha1 = Hmac<Sha1>;
|
||||||
///
|
///
|
||||||
/// TODO: Returns information about the recommended turn server.
|
/// TODO: Returns information about the recommended turn server.
|
||||||
pub(crate) async fn turn_server_route(
|
pub(crate) async fn turn_server_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_turn_server_info::v3::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_turn_server_info::v3::Request>,
|
||||||
) -> Result<get_turn_server_info::v3::Response> {
|
) -> Result<get_turn_server_info::v3::Response> {
|
||||||
// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way
|
// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way
|
||||||
if services.server.config.turn_uris.is_empty() {
|
if services.server.config.turn_uris.is_empty() {
|
||||||
|
@ -44,7 +45,8 @@ pub(crate) async fn turn_server_route(
|
||||||
|
|
||||||
let username: String = format!("{}:{}", expiry.get(), user);
|
let username: String = format!("{}:{}", expiry.get(), user);
|
||||||
|
|
||||||
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes()).expect("HMAC can take key of any size");
|
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
|
||||||
|
.expect("HMAC can take key of any size");
|
||||||
mac.update(username.as_bytes());
|
mac.update(username.as_bytes());
|
||||||
|
|
||||||
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
|
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
|
||||||
|
|
|
@ -13,21 +13,18 @@ use crate::{Error, Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
||||||
pub(crate) async fn well_known_client(
|
pub(crate) async fn well_known_client(
|
||||||
State(services): State<crate::State>, _body: Ruma<discover_homeserver::Request>,
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<discover_homeserver::Request>,
|
||||||
) -> Result<discover_homeserver::Response> {
|
) -> Result<discover_homeserver::Response> {
|
||||||
let client_url = match services.server.config.well_known.client.as_ref() {
|
let client_url = match services.server.config.well_known.client.as_ref() {
|
||||||
Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(discover_homeserver::Response {
|
Ok(discover_homeserver::Response {
|
||||||
homeserver: HomeserverInfo {
|
homeserver: HomeserverInfo { base_url: client_url.clone() },
|
||||||
base_url: client_url.clone(),
|
|
||||||
},
|
|
||||||
identity_server: None,
|
identity_server: None,
|
||||||
sliding_sync_proxy: Some(SlidingSyncProxyInfo {
|
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url }),
|
||||||
url: client_url,
|
|
||||||
}),
|
|
||||||
tile_server: None,
|
tile_server: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -36,7 +33,8 @@ pub(crate) async fn well_known_client(
|
||||||
///
|
///
|
||||||
/// Server support contact and support page of a homeserver's domain.
|
/// Server support contact and support page of a homeserver's domain.
|
||||||
pub(crate) async fn well_known_support(
|
pub(crate) async fn well_known_support(
|
||||||
State(services): State<crate::State>, _body: Ruma<discover_support::Request>,
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<discover_support::Request>,
|
||||||
) -> Result<discover_support::Response> {
|
) -> Result<discover_support::Response> {
|
||||||
let support_page = services
|
let support_page = services
|
||||||
.server
|
.server
|
||||||
|
@ -65,11 +63,7 @@ pub(crate) async fn well_known_support(
|
||||||
let mut contacts: Vec<Contact> = vec![];
|
let mut contacts: Vec<Contact> = vec![];
|
||||||
|
|
||||||
if let Some(role) = role {
|
if let Some(role) = role {
|
||||||
let contact = Contact {
|
let contact = Contact { role, email_address, matrix_id };
|
||||||
role,
|
|
||||||
email_address,
|
|
||||||
matrix_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
contacts.push(contact);
|
contacts.push(contact);
|
||||||
}
|
}
|
||||||
|
@ -79,22 +73,21 @@ pub(crate) async fn well_known_support(
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(discover_support::Response {
|
Ok(discover_support::Response { contacts, support_page })
|
||||||
contacts,
|
|
||||||
support_page,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /client/server.json`
|
/// # `GET /client/server.json`
|
||||||
///
|
///
|
||||||
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
||||||
/// Web as a non-standard health check.
|
/// Web as a non-standard health check.
|
||||||
pub(crate) async fn syncv3_client_server_json(State(services): State<crate::State>) -> Result<impl IntoResponse> {
|
pub(crate) async fn syncv3_client_server_json(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
let server_url = match services.server.config.well_known.client.as_ref() {
|
let server_url = match services.server.config.well_known.client.as_ref() {
|
||||||
Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
None => match services.server.config.well_known.server.as_ref() {
|
| None => match services.server.config.well_known.server.as_ref() {
|
||||||
Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,10 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||||
router = router
|
router = router
|
||||||
.ruma_route(&server::get_server_version_route)
|
.ruma_route(&server::get_server_version_route)
|
||||||
.route("/_matrix/key/v2/server", get(server::get_server_keys_route))
|
.route("/_matrix/key/v2/server", get(server::get_server_keys_route))
|
||||||
.route("/_matrix/key/v2/server/:key_id", get(server::get_server_keys_deprecated_route))
|
.route(
|
||||||
|
"/_matrix/key/v2/server/:key_id",
|
||||||
|
get(server::get_server_keys_deprecated_route),
|
||||||
|
)
|
||||||
.ruma_route(&server::get_public_rooms_route)
|
.ruma_route(&server::get_public_rooms_route)
|
||||||
.ruma_route(&server::get_public_rooms_filtered_route)
|
.ruma_route(&server::get_public_rooms_filtered_route)
|
||||||
.ruma_route(&server::send_transaction_message_route)
|
.ruma_route(&server::send_transaction_message_route)
|
||||||
|
@ -284,6 +287,10 @@ async fn redirect_legacy_preview(uri: Uri) -> impl IntoResponse {
|
||||||
Redirect::temporary(&uri)
|
Redirect::temporary(&uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn legacy_media_disabled() -> impl IntoResponse { err!(Request(Forbidden("Unauthenticated media is disabled."))) }
|
async fn legacy_media_disabled() -> impl IntoResponse {
|
||||||
|
err!(Request(Forbidden("Unauthenticated media is disabled.")))
|
||||||
|
}
|
||||||
|
|
||||||
async fn federation_disabled() -> impl IntoResponse { err!(Request(Forbidden("Federation is disabled."))) }
|
async fn federation_disabled() -> impl IntoResponse {
|
||||||
|
err!(Request(Forbidden("Federation is disabled.")))
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use axum::{async_trait, body::Body, extract::FromRequest};
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use conduwuit::{debug, debug_warn, err, trace, utils::string::EMPTY, Error, Result};
|
use conduwuit::{debug, debug_warn, err, trace, utils::string::EMPTY, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::IncomingRequest, CanonicalJsonObject, CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedServerName,
|
api::IncomingRequest, CanonicalJsonObject, CanonicalJsonValue, DeviceId, OwnedDeviceId,
|
||||||
OwnedUserId, ServerName, UserId,
|
OwnedServerName, OwnedUserId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use service::Services;
|
use service::Services;
|
||||||
|
|
||||||
|
@ -43,7 +43,9 @@ where
|
||||||
T: IncomingRequest + Send + Sync + 'static,
|
T: IncomingRequest + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn sender(&self) -> (&UserId, &DeviceId) { (self.sender_user(), self.sender_device()) }
|
pub(crate) fn sender(&self) -> (&UserId, &DeviceId) {
|
||||||
|
(self.sender_user(), self.sender_device())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn sender_user(&self) -> &UserId {
|
pub(crate) fn sender_user(&self) -> &UserId {
|
||||||
|
@ -83,7 +85,10 @@ where
|
||||||
{
|
{
|
||||||
type Rejection = Error;
|
type Rejection = Error;
|
||||||
|
|
||||||
async fn from_request(request: hyper::Request<Body>, services: &State) -> Result<Self, Self::Rejection> {
|
async fn from_request(
|
||||||
|
request: hyper::Request<Body>,
|
||||||
|
services: &State,
|
||||||
|
) -> Result<Self, Self::Rejection> {
|
||||||
let mut request = request::from(services, request).await?;
|
let mut request = request::from(services, request).await?;
|
||||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
|
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
|
||||||
|
|
||||||
|
@ -96,7 +101,10 @@ where
|
||||||
&& !request.parts.uri.path().contains("/media/")
|
&& !request.parts.uri.path().contains("/media/")
|
||||||
{
|
{
|
||||||
trace!("json_body from_request: {:?}", json_body.clone());
|
trace!("json_body from_request: {:?}", json_body.clone());
|
||||||
debug_warn!("received a POST request with an empty body, defaulting/assuming to {{}} like Synapse does");
|
debug_warn!(
|
||||||
|
"received a POST request with an empty body, defaulting/assuming to {{}} like \
|
||||||
|
Synapse does"
|
||||||
|
);
|
||||||
json_body = Some(CanonicalJsonValue::Object(CanonicalJsonObject::new()));
|
json_body = Some(CanonicalJsonValue::Object(CanonicalJsonObject::new()));
|
||||||
}
|
}
|
||||||
let auth = auth::auth(services, &mut request, json_body.as_ref(), &T::METADATA).await?;
|
let auth = auth::auth(services, &mut request, json_body.as_ref(), &T::METADATA).await?;
|
||||||
|
@ -112,14 +120,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_body<T>(
|
fn make_body<T>(
|
||||||
services: &Services, request: &mut Request, json_body: Option<&mut CanonicalJsonValue>, auth: &Auth,
|
services: &Services,
|
||||||
|
request: &mut Request,
|
||||||
|
json_body: Option<&mut CanonicalJsonValue>,
|
||||||
|
auth: &Auth,
|
||||||
) -> Result<T>
|
) -> Result<T>
|
||||||
where
|
where
|
||||||
T: IncomingRequest,
|
T: IncomingRequest,
|
||||||
{
|
{
|
||||||
let body = take_body(services, request, json_body, auth);
|
let body = take_body(services, request, json_body, auth);
|
||||||
let http_request = into_http_request(request, body);
|
let http_request = into_http_request(request, body);
|
||||||
T::try_from_http_request(http_request, &request.path).map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))
|
T::try_from_http_request(http_request, &request.path)
|
||||||
|
.map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_http_request(request: &Request, body: Bytes) -> hyper::Request<Bytes> {
|
fn into_http_request(request: &Request, body: Bytes) -> hyper::Request<Bytes> {
|
||||||
|
@ -141,7 +153,10 @@ fn into_http_request(request: &Request, body: Bytes) -> hyper::Request<Bytes> {
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
fn take_body(
|
fn take_body(
|
||||||
services: &Services, request: &mut Request, json_body: Option<&mut CanonicalJsonValue>, auth: &Auth,
|
services: &Services,
|
||||||
|
request: &mut Request,
|
||||||
|
json_body: Option<&mut CanonicalJsonValue>,
|
||||||
|
auth: &Auth,
|
||||||
) -> Bytes {
|
) -> Bytes {
|
||||||
let Some(CanonicalJsonValue::Object(json_body)) = json_body else {
|
let Some(CanonicalJsonValue::Object(json_body)) = json_body else {
|
||||||
return mem::take(&mut request.body);
|
return mem::take(&mut request.body);
|
||||||
|
|
|
@ -10,7 +10,9 @@ use ruma::{
|
||||||
client::{
|
client::{
|
||||||
directory::get_public_rooms,
|
directory::get_public_rooms,
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
profile::{get_avatar_url, get_display_name, get_profile, get_profile_key, get_timezone_key},
|
profile::{
|
||||||
|
get_avatar_url, get_display_name, get_profile, get_profile_key, get_timezone_key,
|
||||||
|
},
|
||||||
voip::get_turn_server_info,
|
voip::get_turn_server_info,
|
||||||
},
|
},
|
||||||
federation::openid::get_openid_userinfo,
|
federation::openid::get_openid_userinfo,
|
||||||
|
@ -42,12 +44,15 @@ pub(super) struct Auth {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn auth(
|
pub(super) async fn auth(
|
||||||
services: &Services, request: &mut Request, json_body: Option<&CanonicalJsonValue>, metadata: &Metadata,
|
services: &Services,
|
||||||
|
request: &mut Request,
|
||||||
|
json_body: Option<&CanonicalJsonValue>,
|
||||||
|
metadata: &Metadata,
|
||||||
) -> Result<Auth> {
|
) -> Result<Auth> {
|
||||||
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
||||||
let token = match &bearer {
|
let token = match &bearer {
|
||||||
Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()),
|
| Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()),
|
||||||
None => request.query.access_token.as_deref(),
|
| None => request.query.access_token.as_deref(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let token = if let Some(token) = token {
|
let token = if let Some(token) = token {
|
||||||
|
@ -64,56 +69,64 @@ pub(super) async fn auth(
|
||||||
|
|
||||||
if metadata.authentication == AuthScheme::None {
|
if metadata.authentication == AuthScheme::None {
|
||||||
match metadata {
|
match metadata {
|
||||||
&get_public_rooms::v3::Request::METADATA => {
|
| &get_public_rooms::v3::Request::METADATA => {
|
||||||
if !services
|
if !services
|
||||||
.globals
|
.globals
|
||||||
.config
|
.config
|
||||||
.allow_public_room_directory_without_auth
|
.allow_public_room_directory_without_auth
|
||||||
{
|
{
|
||||||
match token {
|
match token {
|
||||||
Token::Appservice(_) | Token::User(_) => {
|
| Token::Appservice(_) | Token::User(_) => {
|
||||||
// we should have validated the token above
|
// we should have validated the token above
|
||||||
// already
|
// already
|
||||||
},
|
},
|
||||||
Token::None | Token::Invalid => {
|
| Token::None | Token::Invalid => {
|
||||||
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing or invalid access token."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::MissingToken,
|
||||||
|
"Missing or invalid access token.",
|
||||||
|
));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&get_profile::v3::Request::METADATA
|
| &get_profile::v3::Request::METADATA
|
||||||
| &get_profile_key::unstable::Request::METADATA
|
| &get_profile_key::unstable::Request::METADATA
|
||||||
| &get_display_name::v3::Request::METADATA
|
| &get_display_name::v3::Request::METADATA
|
||||||
| &get_avatar_url::v3::Request::METADATA
|
| &get_avatar_url::v3::Request::METADATA
|
||||||
| &get_timezone_key::unstable::Request::METADATA => {
|
| &get_timezone_key::unstable::Request::METADATA => {
|
||||||
if services.globals.config.require_auth_for_profile_requests {
|
if services.globals.config.require_auth_for_profile_requests {
|
||||||
match token {
|
match token {
|
||||||
Token::Appservice(_) | Token::User(_) => {
|
| Token::Appservice(_) | Token::User(_) => {
|
||||||
// we should have validated the token above
|
// we should have validated the token above
|
||||||
// already
|
// already
|
||||||
},
|
},
|
||||||
Token::None | Token::Invalid => {
|
| Token::None | Token::Invalid => {
|
||||||
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing or invalid access token."));
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::MissingToken,
|
||||||
|
"Missing or invalid access token.",
|
||||||
|
));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
| _ => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match (metadata.authentication, token) {
|
match (metadata.authentication, token) {
|
||||||
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info).await?),
|
| (AuthScheme::AccessToken, Token::Appservice(info)) =>
|
||||||
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
|
Ok(auth_appservice(services, request, info).await?),
|
||||||
Ok(Auth {
|
| (
|
||||||
origin: None,
|
AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken,
|
||||||
sender_user: None,
|
Token::Appservice(info),
|
||||||
sender_device: None,
|
) => Ok(Auth {
|
||||||
appservice_info: Some(*info),
|
origin: None,
|
||||||
})
|
sender_user: None,
|
||||||
},
|
sender_device: None,
|
||||||
(AuthScheme::AccessToken, Token::None) => match metadata {
|
appservice_info: Some(*info),
|
||||||
&get_turn_server_info::v3::Request::METADATA => {
|
}),
|
||||||
|
| (AuthScheme::AccessToken, Token::None) => match metadata {
|
||||||
|
| &get_turn_server_info::v3::Request::METADATA => {
|
||||||
if services.globals.config.turn_allow_guests {
|
if services.globals.config.turn_allow_guests {
|
||||||
Ok(Auth {
|
Ok(Auth {
|
||||||
origin: None,
|
origin: None,
|
||||||
|
@ -125,9 +138,9 @@ pub(super) async fn auth(
|
||||||
Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))
|
Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token.")),
|
| _ => Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token.")),
|
||||||
},
|
},
|
||||||
(
|
| (
|
||||||
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
|
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
|
||||||
Token::User((user_id, device_id)),
|
Token::User((user_id, device_id)),
|
||||||
) => Ok(Auth {
|
) => Ok(Auth {
|
||||||
|
@ -136,26 +149,33 @@ pub(super) async fn auth(
|
||||||
sender_device: Some(device_id),
|
sender_device: Some(device_id),
|
||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
}),
|
}),
|
||||||
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(services, request, json_body).await?),
|
| (AuthScheme::ServerSignatures, Token::None) =>
|
||||||
(AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional, Token::None) => Ok(Auth {
|
Ok(auth_server(services, request, json_body).await?),
|
||||||
|
| (
|
||||||
|
AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional,
|
||||||
|
Token::None,
|
||||||
|
) => Ok(Auth {
|
||||||
sender_user: None,
|
sender_user: None,
|
||||||
sender_device: None,
|
sender_device: None,
|
||||||
origin: None,
|
origin: None,
|
||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
}),
|
}),
|
||||||
(AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) => Err(Error::BadRequest(
|
| (AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) =>
|
||||||
ErrorKind::Unauthorized,
|
Err(Error::BadRequest(
|
||||||
"Only server signatures should be used on this endpoint.",
|
ErrorKind::Unauthorized,
|
||||||
)),
|
"Only server signatures should be used on this endpoint.",
|
||||||
(AuthScheme::AppserviceToken, Token::User(_)) => Err(Error::BadRequest(
|
)),
|
||||||
|
| (AuthScheme::AppserviceToken, Token::User(_)) => Err(Error::BadRequest(
|
||||||
ErrorKind::Unauthorized,
|
ErrorKind::Unauthorized,
|
||||||
"Only appservice access tokens should be used on this endpoint.",
|
"Only appservice access tokens should be used on this endpoint.",
|
||||||
)),
|
)),
|
||||||
(AuthScheme::None, Token::Invalid) => {
|
| (AuthScheme::None, Token::Invalid) => {
|
||||||
// OpenID federation endpoint uses a query param with the same name, drop this
|
// OpenID federation endpoint uses a query param with the same name, drop this
|
||||||
// once query params for user auth are removed from the spec. This is
|
// once query params for user auth are removed from the spec. This is
|
||||||
// required to make integration manager work.
|
// required to make integration manager work.
|
||||||
if request.query.access_token.is_some() && metadata == &get_openid_userinfo::v1::Request::METADATA {
|
if request.query.access_token.is_some()
|
||||||
|
&& metadata == &get_openid_userinfo::v1::Request::METADATA
|
||||||
|
{
|
||||||
Ok(Auth {
|
Ok(Auth {
|
||||||
origin: None,
|
origin: None,
|
||||||
sender_user: None,
|
sender_user: None,
|
||||||
|
@ -164,25 +184,29 @@ pub(super) async fn auth(
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(
|
Err(Error::BadRequest(
|
||||||
ErrorKind::UnknownToken {
|
ErrorKind::UnknownToken { soft_logout: false },
|
||||||
soft_logout: false,
|
|
||||||
},
|
|
||||||
"Unknown access token.",
|
"Unknown access token.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(_, Token::Invalid) => Err(Error::BadRequest(
|
| (_, Token::Invalid) => Err(Error::BadRequest(
|
||||||
ErrorKind::UnknownToken {
|
ErrorKind::UnknownToken { soft_logout: false },
|
||||||
soft_logout: false,
|
|
||||||
},
|
|
||||||
"Unknown access token.",
|
"Unknown access token.",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn auth_appservice(services: &Services, request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
|
async fn auth_appservice(
|
||||||
let user_id_default =
|
services: &Services,
|
||||||
|| UserId::parse_with_server_name(info.registration.sender_localpart.as_str(), services.globals.server_name());
|
request: &Request,
|
||||||
|
info: Box<RegistrationInfo>,
|
||||||
|
) -> Result<Auth> {
|
||||||
|
let user_id_default = || {
|
||||||
|
UserId::parse_with_server_name(
|
||||||
|
info.registration.sender_localpart.as_str(),
|
||||||
|
services.globals.server_name(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let Ok(user_id) = request
|
let Ok(user_id) = request
|
||||||
.query
|
.query
|
||||||
|
@ -205,7 +229,11 @@ async fn auth_appservice(services: &Services, request: &Request, info: Box<Regis
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn auth_server(services: &Services, request: &mut Request, body: Option<&CanonicalJsonValue>) -> Result<Auth> {
|
async fn auth_server(
|
||||||
|
services: &Services,
|
||||||
|
request: &mut Request,
|
||||||
|
body: Option<&CanonicalJsonValue>,
|
||||||
|
) -> Result<Auth> {
|
||||||
type Member = (String, CanonicalJsonValue);
|
type Member = (String, CanonicalJsonValue);
|
||||||
type Object = CanonicalJsonObject;
|
type Object = CanonicalJsonObject;
|
||||||
type Value = CanonicalJsonValue;
|
type Value = CanonicalJsonValue;
|
||||||
|
@ -222,7 +250,8 @@ async fn auth_server(services: &Services, request: &mut Request, body: Option<&C
|
||||||
.expect("all requests have a path")
|
.expect("all requests have a path")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let signature: [Member; 1] = [(x_matrix.key.as_str().into(), Value::String(x_matrix.sig.to_string()))];
|
let signature: [Member; 1] =
|
||||||
|
[(x_matrix.key.as_str().into(), Value::String(x_matrix.sig.to_string()))];
|
||||||
|
|
||||||
let signatures: [Member; 1] = [(origin.as_str().into(), Value::Object(signature.into()))];
|
let signatures: [Member; 1] = [(origin.as_str().into(), Value::Object(signature.into()))];
|
||||||
|
|
||||||
|
@ -261,8 +290,8 @@ async fn auth_server(services: &Services, request: &mut Request, body: Option<&C
|
||||||
debug_error!("Failed to verify federation request from {origin}: {e}");
|
debug_error!("Failed to verify federation request from {origin}: {e}");
|
||||||
if request.parts.uri.to_string().contains('@') {
|
if request.parts.uri.to_string().contains('@') {
|
||||||
warn!(
|
warn!(
|
||||||
"Request uri contained '@' character. Make sure your reverse proxy gives conduwuit the raw uri \
|
"Request uri contained '@' character. Make sure your reverse proxy gives \
|
||||||
(apache: use nocanon)"
|
conduwuit the raw uri (apache: use nocanon)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +323,9 @@ fn auth_server_checks(services: &Services, x_matrix: &XMatrix) -> Result<()> {
|
||||||
.forbidden_remote_server_names
|
.forbidden_remote_server_names
|
||||||
.contains(origin)
|
.contains(origin)
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden(debug_warn!("Federation requests from {origin} denied."))));
|
return Err!(Request(Forbidden(debug_warn!(
|
||||||
|
"Federation requests from {origin} denied."
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -307,9 +338,9 @@ async fn parse_x_matrix(request: &mut Request) -> Result<XMatrix> {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
let msg = match e.reason() {
|
let msg = match e.reason() {
|
||||||
TypedHeaderRejectionReason::Missing => "Missing Authorization header.",
|
| TypedHeaderRejectionReason::Missing => "Missing Authorization header.",
|
||||||
TypedHeaderRejectionReason::Error(_) => "Invalid X-Matrix signatures.",
|
| TypedHeaderRejectionReason::Error(_) => "Invalid X-Matrix signatures.",
|
||||||
_ => "Unknown header-related error",
|
| _ => "Unknown header-related error",
|
||||||
};
|
};
|
||||||
|
|
||||||
err!(Request(Forbidden(warn!("{msg}: {e}"))))
|
err!(Request(Forbidden(warn!("{msg}: {e}"))))
|
||||||
|
|
|
@ -66,14 +66,14 @@ ruma_handler!(T1, T2, T3, T4);
|
||||||
|
|
||||||
const fn method_to_filter(method: &Method) -> MethodFilter {
|
const fn method_to_filter(method: &Method) -> MethodFilter {
|
||||||
match *method {
|
match *method {
|
||||||
Method::DELETE => MethodFilter::DELETE,
|
| Method::DELETE => MethodFilter::DELETE,
|
||||||
Method::GET => MethodFilter::GET,
|
| Method::GET => MethodFilter::GET,
|
||||||
Method::HEAD => MethodFilter::HEAD,
|
| Method::HEAD => MethodFilter::HEAD,
|
||||||
Method::OPTIONS => MethodFilter::OPTIONS,
|
| Method::OPTIONS => MethodFilter::OPTIONS,
|
||||||
Method::PATCH => MethodFilter::PATCH,
|
| Method::PATCH => MethodFilter::PATCH,
|
||||||
Method::POST => MethodFilter::POST,
|
| Method::POST => MethodFilter::POST,
|
||||||
Method::PUT => MethodFilter::PUT,
|
| Method::PUT => MethodFilter::PUT,
|
||||||
Method::TRACE => MethodFilter::TRACE,
|
| Method::TRACE => MethodFilter::TRACE,
|
||||||
_ => panic!("Unsupported HTTP method"),
|
| _ => panic!("Unsupported HTTP method"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,17 @@ pub(super) struct Request {
|
||||||
pub(super) parts: Parts,
|
pub(super) parts: Parts,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn from(services: &Services, request: hyper::Request<axum::body::Body>) -> Result<Request> {
|
pub(super) async fn from(
|
||||||
|
services: &Services,
|
||||||
|
request: hyper::Request<axum::body::Body>,
|
||||||
|
) -> Result<Request> {
|
||||||
let limited = request.with_limited_body();
|
let limited = request.with_limited_body();
|
||||||
let (mut parts, body) = limited.into_parts();
|
let (mut parts, body) = limited.into_parts();
|
||||||
|
|
||||||
let path: Path<Vec<String>> = parts.extract().await?;
|
let path: Path<Vec<String>> = parts.extract().await?;
|
||||||
let query = parts.uri.query().unwrap_or_default();
|
let query = parts.uri.query().unwrap_or_default();
|
||||||
let query =
|
let query = serde_html_form::from_str(query)
|
||||||
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
.map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
||||||
|
|
||||||
let max_body_size = services.globals.config.max_request_size;
|
let max_body_size = services.globals.config.max_request_size;
|
||||||
|
|
||||||
|
@ -35,10 +38,5 @@ pub(super) async fn from(services: &Services, request: hyper::Request<axum::body
|
||||||
.await
|
.await
|
||||||
.map_err(|e| err!(Request(TooLarge("Request body too large: {e}"))))?;
|
.map_err(|e| err!(Request(TooLarge("Request body too large: {e}"))))?;
|
||||||
|
|
||||||
Ok(Request {
|
Ok(Request { path, query, body, parts })
|
||||||
path,
|
|
||||||
query,
|
|
||||||
body,
|
|
||||||
parts,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,7 @@ pub fn create(services: Arc<Services>) -> (State, Guard) {
|
||||||
services: Arc::into_raw(services.clone()),
|
services: Arc::into_raw(services.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let guard = Guard {
|
let guard = Guard { services };
|
||||||
services,
|
|
||||||
};
|
|
||||||
|
|
||||||
(state, guard)
|
(state, guard)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ use crate::Ruma;
|
||||||
/// Retrieves events from before the sender joined the room, if the room's
|
/// Retrieves events from before the sender joined the room, if the room's
|
||||||
/// history visibility allows.
|
/// history visibility allows.
|
||||||
pub(crate) async fn get_backfill_route(
|
pub(crate) async fn get_backfill_route(
|
||||||
State(services): State<crate::State>, ref body: Ruma<get_backfill::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
ref body: Ruma<get_backfill::v1::Request>,
|
||||||
) -> Result<get_backfill::v1::Response> {
|
) -> Result<get_backfill::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
|
|
|
@ -12,7 +12,8 @@ use crate::Ruma;
|
||||||
/// - Only works if a user of this server is currently invited or joined the
|
/// - Only works if a user of this server is currently invited or joined the
|
||||||
/// room
|
/// room
|
||||||
pub(crate) async fn get_event_route(
|
pub(crate) async fn get_event_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_event::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_event::v1::Request>,
|
||||||
) -> Result<get_event::v1::Response> {
|
) -> Result<get_event::v1::Response> {
|
||||||
let event = services
|
let event = services
|
||||||
.rooms
|
.rooms
|
||||||
|
|
|
@ -17,7 +17,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// - This does not include the event itself
|
/// - This does not include the event itself
|
||||||
pub(crate) async fn get_event_authorization_route(
|
pub(crate) async fn get_event_authorization_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_event_authorization::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_event_authorization::v1::Request>,
|
||||||
) -> Result<get_event_authorization::v1::Response> {
|
) -> Result<get_event_authorization::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
|
@ -40,8 +41,8 @@ pub(crate) async fn get_event_authorization_route(
|
||||||
.and_then(|val| val.as_str())
|
.and_then(|val| val.as_str())
|
||||||
.ok_or_else(|| Error::bad_database("Invalid event in database."))?;
|
.ok_or_else(|| Error::bad_database("Invalid event in database."))?;
|
||||||
|
|
||||||
let room_id =
|
let room_id = <&RoomId>::try_from(room_id_str)
|
||||||
<&RoomId>::try_from(room_id_str).map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
|
.map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
|
||||||
|
|
||||||
let auth_chain = services
|
let auth_chain = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -53,7 +54,5 @@ pub(crate) async fn get_event_authorization_route(
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_event_authorization::v1::Response {
|
Ok(get_event_authorization::v1::Response { auth_chain })
|
||||||
auth_chain,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// Retrieves events that the sender is missing.
|
/// Retrieves events that the sender is missing.
|
||||||
pub(crate) async fn get_missing_events_route(
|
pub(crate) async fn get_missing_events_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_missing_events::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_missing_events::v1::Request>,
|
||||||
) -> Result<get_missing_events::v1::Response> {
|
) -> Result<get_missing_events::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
|
@ -87,7 +88,5 @@ pub(crate) async fn get_missing_events_route(
|
||||||
i = i.saturating_add(1);
|
i = i.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_missing_events::v1::Response {
|
Ok(get_missing_events::v1::Response { events })
|
||||||
events,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ use crate::{Error, Result, Ruma};
|
||||||
/// Gets the space tree in a depth-first manner to locate child rooms of a given
|
/// Gets the space tree in a depth-first manner to locate child rooms of a given
|
||||||
/// space.
|
/// space.
|
||||||
pub(crate) async fn get_hierarchy_route(
|
pub(crate) async fn get_hierarchy_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_hierarchy::v1::Request>,
|
||||||
) -> Result<get_hierarchy::v1::Response> {
|
) -> Result<get_hierarchy::v1::Response> {
|
||||||
if services.rooms.metadata.exists(&body.room_id).await {
|
if services.rooms.metadata.exists(&body.room_id).await {
|
||||||
services
|
services
|
||||||
|
|
|
@ -16,7 +16,8 @@ use crate::Ruma;
|
||||||
/// Invites a remote user to a room.
|
/// Invites a remote user to a room.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
||||||
pub(crate) async fn create_invite_route(
|
pub(crate) async fn create_invite_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_invite::v2::Request>,
|
body: Ruma<create_invite::v2::Request>,
|
||||||
) -> Result<create_invite::v2::Response> {
|
) -> Result<create_invite::v2::Response> {
|
||||||
// ACL check origin
|
// ACL check origin
|
||||||
|
@ -28,9 +29,7 @@ pub(crate) async fn create_invite_route(
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.room_version) {
|
if !services.server.supported_room_version(&body.room_version) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion {
|
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
||||||
room_version: body.room_version.clone(),
|
|
||||||
},
|
|
||||||
"Server does not support this room version.",
|
"Server does not support this room version.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -102,11 +101,14 @@ pub(crate) async fn create_invite_route(
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
||||||
|
|
||||||
if services.rooms.metadata.is_banned(&body.room_id).await && !services.users.is_admin(&invited_user).await {
|
if services.rooms.metadata.is_banned(&body.room_id).await
|
||||||
|
&& !services.users.is_admin(&invited_user).await
|
||||||
|
{
|
||||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services.globals.block_non_admin_invites() && !services.users.is_admin(&invited_user).await {
|
if services.globals.block_non_admin_invites() && !services.users.is_admin(&invited_user).await
|
||||||
|
{
|
||||||
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@ use ruma::{
|
||||||
/// this will be valid forever.
|
/// this will be valid forever.
|
||||||
// Response type for this endpoint is Json because we need to calculate a
|
// Response type for this endpoint is Json because we need to calculate a
|
||||||
// signature for the response
|
// signature for the response
|
||||||
pub(crate) async fn get_server_keys_route(State(services): State<crate::State>) -> Result<impl IntoResponse> {
|
pub(crate) async fn get_server_keys_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
let server_name = services.globals.server_name();
|
let server_name = services.globals.server_name();
|
||||||
let active_key_id = services.server_keys.active_key_id();
|
let active_key_id = services.server_keys.active_key_id();
|
||||||
let mut all_keys = services.server_keys.verify_keys_for(server_name).await;
|
let mut all_keys = services.server_keys.verify_keys_for(server_name).await;
|
||||||
|
@ -72,6 +74,8 @@ fn expires_ts() -> MilliSecondsSinceUnixEpoch {
|
||||||
///
|
///
|
||||||
/// - Matrix does not support invalidating public keys, so the key returned by
|
/// - Matrix does not support invalidating public keys, so the key returned by
|
||||||
/// this will be valid forever.
|
/// this will be valid forever.
|
||||||
pub(crate) async fn get_server_keys_deprecated_route(State(services): State<crate::State>) -> impl IntoResponse {
|
pub(crate) async fn get_server_keys_deprecated_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
get_server_keys_route(State(services)).await
|
get_server_keys_route(State(services)).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@ use crate::{
|
||||||
///
|
///
|
||||||
/// Creates a join template.
|
/// Creates a join template.
|
||||||
pub(crate) async fn create_join_event_template_route(
|
pub(crate) async fn create_join_event_template_route(
|
||||||
State(services): State<crate::State>, body: Ruma<prepare_join_event::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<prepare_join_event::v1::Request>,
|
||||||
) -> Result<prepare_join_event::v1::Response> {
|
) -> Result<prepare_join_event::v1::Response> {
|
||||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||||
|
@ -47,8 +48,8 @@ pub(crate) async fn create_join_event_template_route(
|
||||||
.contains(body.origin())
|
.contains(body.origin())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} for remote user {} tried joining room ID {} which has a server name that is globally \
|
"Server {} for remote user {} tried joining room ID {} which has a server name that \
|
||||||
forbidden. Rejecting.",
|
is globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.origin(),
|
||||||
&body.user_id,
|
&body.user_id,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
|
@ -72,9 +73,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||||
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
||||||
if !body.ver.contains(&room_version_id) {
|
if !body.ver.contains(&room_version_id) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion {
|
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||||
room_version: room_version_id,
|
|
||||||
},
|
|
||||||
"Room version not supported.",
|
"Room version not supported.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -86,16 +85,25 @@ pub(crate) async fn create_join_event_template_route(
|
||||||
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||||
// room version does not support restricted join rules
|
// room version does not support restricted join rules
|
||||||
None
|
None
|
||||||
} else if user_can_perform_restricted_join(&services, &body.user_id, &body.room_id, &room_version_id).await? {
|
} else if user_can_perform_restricted_join(
|
||||||
|
&services,
|
||||||
|
&body.user_id,
|
||||||
|
&body.room_id,
|
||||||
|
&room_version_id,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
let Some(auth_user) = services
|
let Some(auth_user) = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.local_users_in_room(&body.room_id)
|
.local_users_in_room(&body.room_id)
|
||||||
.filter(|user| {
|
.filter(|user| {
|
||||||
services
|
services.rooms.state_accessor.user_can_invite(
|
||||||
.rooms
|
&body.room_id,
|
||||||
.state_accessor
|
user,
|
||||||
.user_can_invite(&body.room_id, user, &body.user_id, &state_lock)
|
&body.user_id,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
.next()
|
.next()
|
||||||
|
@ -116,13 +124,10 @@ pub(crate) async fn create_join_event_template_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.create_hash_and_sign_event(
|
.create_hash_and_sign_event(
|
||||||
PduBuilder::state(
|
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||||
body.user_id.to_string(),
|
join_authorized_via_users_server,
|
||||||
&RoomMemberEventContent {
|
..RoomMemberEventContent::new(MembershipState::Join)
|
||||||
join_authorized_via_users_server,
|
}),
|
||||||
..RoomMemberEventContent::new(MembershipState::Join)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
&body.user_id,
|
&body.user_id,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
@ -142,7 +147,10 @@ pub(crate) async fn create_join_event_template_route(
|
||||||
|
|
||||||
/// Checks whether the given user can join the given room via a restricted join.
|
/// Checks whether the given user can join the given room via a restricted join.
|
||||||
pub(crate) async fn user_can_perform_restricted_join(
|
pub(crate) async fn user_can_perform_restricted_join(
|
||||||
services: &Services, user_id: &UserId, room_id: &RoomId, room_version_id: &RoomVersionId,
|
services: &Services,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
|
|
||||||
|
@ -159,13 +167,19 @@ pub(crate) async fn user_can_perform_restricted_join(
|
||||||
let Ok(join_rules_event_content) = services
|
let Ok(join_rules_event_content) = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get_content::<RoomJoinRulesEventContent>(room_id, &StateEventType::RoomJoinRules, "")
|
.room_state_get_content::<RoomJoinRulesEventContent>(
|
||||||
|
room_id,
|
||||||
|
&StateEventType::RoomJoinRules,
|
||||||
|
"",
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
else {
|
else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let (JoinRule::Restricted(r) | JoinRule::KnockRestricted(r)) = join_rules_event_content.join_rule else {
|
let (JoinRule::Restricted(r) | JoinRule::KnockRestricted(r)) =
|
||||||
|
join_rules_event_content.join_rule
|
||||||
|
else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -195,12 +209,15 @@ pub(crate) async fn user_can_perform_restricted_join(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn maybe_strip_event_id(pdu_json: &mut CanonicalJsonObject, room_version_id: &RoomVersionId) -> Result {
|
pub(crate) fn maybe_strip_event_id(
|
||||||
|
pdu_json: &mut CanonicalJsonObject,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
|
) -> Result {
|
||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
|
|
||||||
match room_version_id {
|
match room_version_id {
|
||||||
V1 | V2 => Ok(()),
|
| V1 | V2 => Ok(()),
|
||||||
_ => {
|
| _ => {
|
||||||
pdu_json.remove("event_id");
|
pdu_json.remove("event_id");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,8 @@ use crate::{service::pdu::PduBuilder, Ruma};
|
||||||
///
|
///
|
||||||
/// Creates a leave template.
|
/// Creates a leave template.
|
||||||
pub(crate) async fn create_leave_event_template_route(
|
pub(crate) async fn create_leave_event_template_route(
|
||||||
State(services): State<crate::State>, body: Ruma<prepare_leave_event::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<prepare_leave_event::v1::Request>,
|
||||||
) -> Result<prepare_leave_event::v1::Response> {
|
) -> Result<prepare_leave_event::v1::Response> {
|
||||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||||
|
@ -37,7 +38,10 @@ pub(crate) async fn create_leave_event_template_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.create_hash_and_sign_event(
|
.create_hash_and_sign_event(
|
||||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent::new(MembershipState::Leave)),
|
PduBuilder::state(
|
||||||
|
body.user_id.to_string(),
|
||||||
|
&RoomMemberEventContent::new(MembershipState::Leave),
|
||||||
|
),
|
||||||
&body.user_id,
|
&body.user_id,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
|
|
|
@ -16,7 +16,8 @@ use crate::Ruma;
|
||||||
/// Load media from our server.
|
/// Load media from our server.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
pub(crate) async fn get_content_route(
|
pub(crate) async fn get_content_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content::v1::Request>,
|
body: Ruma<get_content::v1::Request>,
|
||||||
) -> Result<get_content::v1::Response> {
|
) -> Result<get_content::v1::Response> {
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
|
@ -33,7 +34,8 @@ pub(crate) async fn get_content_route(
|
||||||
return Err!(Request(NotFound("Media not found.")));
|
return Err!(Request(NotFound("Media not found.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
let content_disposition =
|
||||||
|
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
||||||
let content = Content {
|
let content = Content {
|
||||||
file: content.expect("entire file contents"),
|
file: content.expect("entire file contents"),
|
||||||
content_type: content_type.map(Into::into),
|
content_type: content_type.map(Into::into),
|
||||||
|
@ -51,7 +53,8 @@ pub(crate) async fn get_content_route(
|
||||||
/// Load media thumbnail from our server.
|
/// Load media thumbnail from our server.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||||
pub(crate) async fn get_content_thumbnail_route(
|
pub(crate) async fn get_content_thumbnail_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||||
) -> Result<get_content_thumbnail::v1::Response> {
|
) -> Result<get_content_thumbnail::v1::Response> {
|
||||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||||
|
@ -69,7 +72,8 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||||
return Err!(Request(NotFound("Media not found.")));
|
return Err!(Request(NotFound("Media not found.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_disposition = make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
let content_disposition =
|
||||||
|
make_content_disposition(content_disposition.as_ref(), content_type.as_deref(), None);
|
||||||
let content = Content {
|
let content = Content {
|
||||||
file: content.expect("entire file contents"),
|
file: content.expect("entire file contents"),
|
||||||
content_type: content_type.map(Into::into),
|
content_type: content_type.map(Into::into),
|
||||||
|
|
|
@ -7,7 +7,8 @@ use crate::{Result, Ruma};
|
||||||
///
|
///
|
||||||
/// Get information about the user that generated the OpenID token.
|
/// Get information about the user that generated the OpenID token.
|
||||||
pub(crate) async fn get_openid_userinfo_route(
|
pub(crate) async fn get_openid_userinfo_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_openid_userinfo::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_openid_userinfo::v1::Request>,
|
||||||
) -> Result<get_openid_userinfo::v1::Response> {
|
) -> Result<get_openid_userinfo::v1::Response> {
|
||||||
Ok(get_openid_userinfo::v1::Response::new(
|
Ok(get_openid_userinfo::v1::Response::new(
|
||||||
services
|
services
|
||||||
|
|
|
@ -15,7 +15,8 @@ use crate::{Error, Result, Ruma};
|
||||||
/// Lists the public rooms on this server.
|
/// Lists the public rooms on this server.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_filtered_route(
|
pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||||
) -> Result<get_public_rooms_filtered::v1::Response> {
|
) -> Result<get_public_rooms_filtered::v1::Response> {
|
||||||
if !services
|
if !services
|
||||||
|
@ -35,7 +36,9 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
&body.room_network,
|
&body.room_network,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list."))?;
|
.map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(get_public_rooms_filtered::v1::Response {
|
Ok(get_public_rooms_filtered::v1::Response {
|
||||||
chunk: response.chunk,
|
chunk: response.chunk,
|
||||||
|
@ -50,7 +53,8 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||||
/// Lists the public rooms on this server.
|
/// Lists the public rooms on this server.
|
||||||
#[tracing::instrument(skip_all, fields(%client), "publicrooms")]
|
#[tracing::instrument(skip_all, fields(%client), "publicrooms")]
|
||||||
pub(crate) async fn get_public_rooms_route(
|
pub(crate) async fn get_public_rooms_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<get_public_rooms::v1::Request>,
|
body: Ruma<get_public_rooms::v1::Request>,
|
||||||
) -> Result<get_public_rooms::v1::Response> {
|
) -> Result<get_public_rooms::v1::Response> {
|
||||||
if !services
|
if !services
|
||||||
|
@ -69,7 +73,9 @@ pub(crate) async fn get_public_rooms_route(
|
||||||
&body.room_network,
|
&body.room_network,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list."))?;
|
.map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(get_public_rooms::v1::Response {
|
Ok(get_public_rooms::v1::Response {
|
||||||
chunk: response.chunk,
|
chunk: response.chunk,
|
||||||
|
|
|
@ -19,7 +19,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// Resolve a room alias to a room id.
|
/// Resolve a room alias to a room id.
|
||||||
pub(crate) async fn get_room_information_route(
|
pub(crate) async fn get_room_information_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_room_information::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_room_information::v1::Request>,
|
||||||
) -> Result<get_room_information::v1::Response> {
|
) -> Result<get_room_information::v1::Response> {
|
||||||
let room_id = services
|
let room_id = services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -50,10 +51,7 @@ pub(crate) async fn get_room_information_route(
|
||||||
servers.insert(0, services.globals.server_name().to_owned());
|
servers.insert(0, services.globals.server_name().to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_room_information::v1::Response {
|
Ok(get_room_information::v1::Response { room_id, servers })
|
||||||
room_id,
|
|
||||||
servers,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/federation/v1/query/profile`
|
/// # `GET /_matrix/federation/v1/query/profile`
|
||||||
|
@ -61,7 +59,8 @@ pub(crate) async fn get_room_information_route(
|
||||||
///
|
///
|
||||||
/// Gets information on a profile.
|
/// Gets information on a profile.
|
||||||
pub(crate) async fn get_profile_information_route(
|
pub(crate) async fn get_profile_information_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_profile_information::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_profile_information::v1::Request>,
|
||||||
) -> Result<get_profile_information::v1::Response> {
|
) -> Result<get_profile_information::v1::Response> {
|
||||||
if !services
|
if !services
|
||||||
.globals
|
.globals
|
||||||
|
@ -88,14 +87,14 @@ pub(crate) async fn get_profile_information_route(
|
||||||
let mut custom_profile_fields = BTreeMap::new();
|
let mut custom_profile_fields = BTreeMap::new();
|
||||||
|
|
||||||
match &body.field {
|
match &body.field {
|
||||||
Some(ProfileField::DisplayName) => {
|
| Some(ProfileField::DisplayName) => {
|
||||||
displayname = services.users.displayname(&body.user_id).await.ok();
|
displayname = services.users.displayname(&body.user_id).await.ok();
|
||||||
},
|
},
|
||||||
Some(ProfileField::AvatarUrl) => {
|
| Some(ProfileField::AvatarUrl) => {
|
||||||
avatar_url = services.users.avatar_url(&body.user_id).await.ok();
|
avatar_url = services.users.avatar_url(&body.user_id).await.ok();
|
||||||
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
||||||
},
|
},
|
||||||
Some(custom_field) => {
|
| Some(custom_field) => {
|
||||||
if let Ok(value) = services
|
if let Ok(value) = services
|
||||||
.users
|
.users
|
||||||
.profile_key(&body.user_id, custom_field.as_str())
|
.profile_key(&body.user_id, custom_field.as_str())
|
||||||
|
@ -104,7 +103,7 @@ pub(crate) async fn get_profile_information_route(
|
||||||
custom_profile_fields.insert(custom_field.to_string(), value);
|
custom_profile_fields.insert(custom_field.to_string(), value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
| None => {
|
||||||
displayname = services.users.displayname(&body.user_id).await.ok();
|
displayname = services.users.displayname(&body.user_id).await.ok();
|
||||||
avatar_url = services.users.avatar_url(&body.user_id).await.ok();
|
avatar_url = services.users.avatar_url(&body.user_id).await.ok();
|
||||||
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
||||||
|
|
|
@ -2,15 +2,18 @@ use std::{collections::BTreeMap, net::IpAddr, time::Instant};
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{debug, debug_warn, err, error, result::LogErr, trace, utils::ReadyExt, warn, Err, Error, Result};
|
use conduwuit::{
|
||||||
|
debug, debug_warn, err, error, result::LogErr, trace, utils::ReadyExt, warn, Err, Error,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::error::ErrorKind,
|
client::error::ErrorKind,
|
||||||
federation::transactions::{
|
federation::transactions::{
|
||||||
edu::{
|
edu::{
|
||||||
DeviceListUpdateContent, DirectDeviceContent, Edu, PresenceContent, ReceiptContent,
|
DeviceListUpdateContent, DirectDeviceContent, Edu, PresenceContent,
|
||||||
SigningKeyUpdateContent, TypingContent,
|
ReceiptContent, SigningKeyUpdateContent, TypingContent,
|
||||||
},
|
},
|
||||||
send_transaction_message,
|
send_transaction_message,
|
||||||
},
|
},
|
||||||
|
@ -38,7 +41,8 @@ type ResolvedMap = BTreeMap<OwnedEventId, Result<()>>;
|
||||||
/// Push EDUs and PDUs to this server.
|
/// Push EDUs and PDUs to this server.
|
||||||
#[tracing::instrument(skip_all, fields(%client, origin = body.origin().as_str()), name = "send")]
|
#[tracing::instrument(skip_all, fields(%client, origin = body.origin().as_str()), name = "send")]
|
||||||
pub(crate) async fn send_transaction_message_route(
|
pub(crate) async fn send_transaction_message_route(
|
||||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
State(services): State<crate::State>,
|
||||||
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<send_transaction_message::v1::Request>,
|
body: Ruma<send_transaction_message::v1::Request>,
|
||||||
) -> Result<send_transaction_message::v1::Response> {
|
) -> Result<send_transaction_message::v1::Response> {
|
||||||
if body.origin() != body.body.origin {
|
if body.origin() != body.body.origin {
|
||||||
|
@ -69,7 +73,8 @@ pub(crate) async fn send_transaction_message_route(
|
||||||
"Starting txn",
|
"Starting txn",
|
||||||
);
|
);
|
||||||
|
|
||||||
let resolved_map = handle_pdus(&services, &client, &body.pdus, body.origin(), &txn_start_time).await?;
|
let resolved_map =
|
||||||
|
handle_pdus(&services, &client, &body.pdus, body.origin(), &txn_start_time).await?;
|
||||||
handle_edus(&services, &client, &body.edus, body.origin()).await;
|
handle_edus(&services, &client, &body.edus, body.origin()).await;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -90,13 +95,17 @@ pub(crate) async fn send_transaction_message_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_pdus(
|
async fn handle_pdus(
|
||||||
services: &Services, _client: &IpAddr, pdus: &[Box<RawJsonValue>], origin: &ServerName, txn_start_time: &Instant,
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
pdus: &[Box<RawJsonValue>],
|
||||||
|
origin: &ServerName,
|
||||||
|
txn_start_time: &Instant,
|
||||||
) -> Result<ResolvedMap> {
|
) -> Result<ResolvedMap> {
|
||||||
let mut parsed_pdus = Vec::with_capacity(pdus.len());
|
let mut parsed_pdus = Vec::with_capacity(pdus.len());
|
||||||
for pdu in pdus {
|
for pdu in pdus {
|
||||||
parsed_pdus.push(match services.rooms.event_handler.parse_incoming_pdu(pdu).await {
|
parsed_pdus.push(match services.rooms.event_handler.parse_incoming_pdu(pdu).await {
|
||||||
Ok(t) => t,
|
| Ok(t) => t,
|
||||||
Err(e) => {
|
| Err(e) => {
|
||||||
debug_warn!("Could not parse PDU: {e}");
|
debug_warn!("Could not parse PDU: {e}");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
@ -145,26 +154,45 @@ async fn handle_pdus(
|
||||||
Ok(resolved_map)
|
Ok(resolved_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edus(services: &Services, client: &IpAddr, edus: &[Raw<Edu>], origin: &ServerName) {
|
async fn handle_edus(
|
||||||
|
services: &Services,
|
||||||
|
client: &IpAddr,
|
||||||
|
edus: &[Raw<Edu>],
|
||||||
|
origin: &ServerName,
|
||||||
|
) {
|
||||||
for edu in edus
|
for edu in edus
|
||||||
.iter()
|
.iter()
|
||||||
.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(presence) => handle_edu_presence(services, client, origin, presence).await,
|
| Edu::Presence(presence) => {
|
||||||
Edu::Receipt(receipt) => handle_edu_receipt(services, client, origin, receipt).await,
|
handle_edu_presence(services, client, origin, presence).await;
|
||||||
Edu::Typing(typing) => handle_edu_typing(services, client, origin, typing).await,
|
},
|
||||||
Edu::DeviceListUpdate(content) => handle_edu_device_list_update(services, client, origin, content).await,
|
| Edu::Receipt(receipt) =>
|
||||||
Edu::DirectToDevice(content) => handle_edu_direct_to_device(services, client, origin, content).await,
|
handle_edu_receipt(services, client, origin, receipt).await,
|
||||||
Edu::SigningKeyUpdate(content) => handle_edu_signing_key_update(services, client, origin, content).await,
|
| Edu::Typing(typing) => handle_edu_typing(services, client, origin, typing).await,
|
||||||
Edu::_Custom(ref _custom) => {
|
| Edu::DeviceListUpdate(content) => {
|
||||||
|
handle_edu_device_list_update(services, client, origin, content).await;
|
||||||
|
},
|
||||||
|
| Edu::DirectToDevice(content) => {
|
||||||
|
handle_edu_direct_to_device(services, client, origin, content).await;
|
||||||
|
},
|
||||||
|
| Edu::SigningKeyUpdate(content) => {
|
||||||
|
handle_edu_signing_key_update(services, client, origin, content).await;
|
||||||
|
},
|
||||||
|
| Edu::_Custom(ref _custom) => {
|
||||||
debug_warn!(?edus, "received custom/unknown EDU");
|
debug_warn!(?edus, "received custom/unknown EDU");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_presence(services: &Services, _client: &IpAddr, origin: &ServerName, presence: PresenceContent) {
|
async fn handle_edu_presence(
|
||||||
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
presence: PresenceContent,
|
||||||
|
) {
|
||||||
if !services.globals.allow_incoming_presence() {
|
if !services.globals.allow_incoming_presence() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -193,7 +221,12 @@ async fn handle_edu_presence(services: &Services, _client: &IpAddr, origin: &Ser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_receipt(services: &Services, _client: &IpAddr, origin: &ServerName, receipt: ReceiptContent) {
|
async fn handle_edu_receipt(
|
||||||
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
receipt: ReceiptContent,
|
||||||
|
) {
|
||||||
if !services.globals.allow_incoming_read_receipts() {
|
if !services.globals.allow_incoming_read_receipts() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -230,7 +263,8 @@ async fn handle_edu_receipt(services: &Services, _client: &IpAddr, origin: &Serv
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
for event_id in &user_updates.event_ids {
|
for event_id in &user_updates.event_ids {
|
||||||
let user_receipts = BTreeMap::from([(user_id.clone(), user_updates.data.clone())]);
|
let user_receipts =
|
||||||
|
BTreeMap::from([(user_id.clone(), user_updates.data.clone())]);
|
||||||
let receipts = BTreeMap::from([(ReceiptType::Read, user_receipts)]);
|
let receipts = BTreeMap::from([(ReceiptType::Read, user_receipts)]);
|
||||||
let receipt_content = BTreeMap::from([(event_id.to_owned(), receipts)]);
|
let receipt_content = BTreeMap::from([(event_id.to_owned(), receipts)]);
|
||||||
let event = ReceiptEvent {
|
let event = ReceiptEvent {
|
||||||
|
@ -255,7 +289,12 @@ async fn handle_edu_receipt(services: &Services, _client: &IpAddr, origin: &Serv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_typing(services: &Services, _client: &IpAddr, origin: &ServerName, typing: TypingContent) {
|
async fn handle_edu_typing(
|
||||||
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
typing: TypingContent,
|
||||||
|
) {
|
||||||
if !services.globals.config.allow_incoming_typing {
|
if !services.globals.config.allow_incoming_typing {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -321,12 +360,12 @@ async fn handle_edu_typing(services: &Services, _client: &IpAddr, origin: &Serve
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_device_list_update(
|
async fn handle_edu_device_list_update(
|
||||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: DeviceListUpdateContent,
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
content: DeviceListUpdateContent,
|
||||||
) {
|
) {
|
||||||
let DeviceListUpdateContent {
|
let DeviceListUpdateContent { user_id, .. } = content;
|
||||||
user_id,
|
|
||||||
..
|
|
||||||
} = content;
|
|
||||||
|
|
||||||
if user_id.server_name() != origin {
|
if user_id.server_name() != origin {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
|
@ -340,14 +379,12 @@ async fn handle_edu_device_list_update(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_direct_to_device(
|
async fn handle_edu_direct_to_device(
|
||||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: DirectDeviceContent,
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
content: DirectDeviceContent,
|
||||||
) {
|
) {
|
||||||
let DirectDeviceContent {
|
let DirectDeviceContent { sender, ev_type, message_id, messages } = content;
|
||||||
sender,
|
|
||||||
ev_type,
|
|
||||||
message_id,
|
|
||||||
messages,
|
|
||||||
} = content;
|
|
||||||
|
|
||||||
if sender.server_name() != origin {
|
if sender.server_name() != origin {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
|
@ -369,23 +406,28 @@ async fn handle_edu_direct_to_device(
|
||||||
|
|
||||||
for (target_user_id, map) in &messages {
|
for (target_user_id, map) in &messages {
|
||||||
for (target_device_id_maybe, event) in map {
|
for (target_device_id_maybe, event) in map {
|
||||||
let Ok(event) = event
|
let Ok(event) = event.deserialize_as().map_err(|e| {
|
||||||
.deserialize_as()
|
err!(Request(InvalidParam(error!("To-Device event is invalid: {e}"))))
|
||||||
.map_err(|e| err!(Request(InvalidParam(error!("To-Device event is invalid: {e}")))))
|
}) else {
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ev_type = ev_type.to_string();
|
let ev_type = ev_type.to_string();
|
||||||
match target_device_id_maybe {
|
match target_device_id_maybe {
|
||||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.add_to_device_event(&sender, target_user_id, target_device_id, &ev_type, event)
|
.add_to_device_event(
|
||||||
|
&sender,
|
||||||
|
target_user_id,
|
||||||
|
target_device_id,
|
||||||
|
&ev_type,
|
||||||
|
event,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
},
|
},
|
||||||
|
|
||||||
DeviceIdOrAllDevices::AllDevices => {
|
| DeviceIdOrAllDevices::AllDevices => {
|
||||||
let (sender, ev_type, event) = (&sender, &ev_type, &event);
|
let (sender, ev_type, event) = (&sender, &ev_type, &event);
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
|
@ -412,13 +454,12 @@ async fn handle_edu_direct_to_device(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_edu_signing_key_update(
|
async fn handle_edu_signing_key_update(
|
||||||
services: &Services, _client: &IpAddr, origin: &ServerName, content: SigningKeyUpdateContent,
|
services: &Services,
|
||||||
|
_client: &IpAddr,
|
||||||
|
origin: &ServerName,
|
||||||
|
content: SigningKeyUpdateContent,
|
||||||
) {
|
) {
|
||||||
let SigningKeyUpdateContent {
|
let SigningKeyUpdateContent { user_id, master_key, self_signing_key } = content;
|
||||||
user_id,
|
|
||||||
master_key,
|
|
||||||
self_signing_key,
|
|
||||||
} = content;
|
|
||||||
|
|
||||||
if user_id.server_name() != origin {
|
if user_id.server_name() != origin {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
|
|
|
@ -16,7 +16,8 @@ use ruma::{
|
||||||
room::member::{MembershipState, RoomMemberEventContent},
|
room::member::{MembershipState, RoomMemberEventContent},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
},
|
},
|
||||||
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName,
|
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
|
||||||
|
ServerName,
|
||||||
};
|
};
|
||||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||||
use service::Services;
|
use service::Services;
|
||||||
|
@ -25,7 +26,10 @@ use crate::Ruma;
|
||||||
|
|
||||||
/// helper method for /send_join v1 and v2
|
/// helper method for /send_join v1 and v2
|
||||||
async fn create_join_event(
|
async fn create_join_event(
|
||||||
services: &Services, origin: &ServerName, room_id: &RoomId, pdu: &RawJsonValue,
|
services: &Services,
|
||||||
|
origin: &ServerName,
|
||||||
|
room_id: &RoomId,
|
||||||
|
pdu: &RawJsonValue,
|
||||||
) -> Result<create_join_event::v1::RoomState> {
|
) -> Result<create_join_event::v1::RoomState> {
|
||||||
if !services.rooms.metadata.exists(room_id).await {
|
if !services.rooms.metadata.exists(room_id).await {
|
||||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||||
|
@ -146,7 +150,8 @@ async fn create_join_event(
|
||||||
|
|
||||||
if !services.globals.user_is_local(&authorising_user) {
|
if !services.globals.user_is_local(&authorising_user) {
|
||||||
return Err!(Request(InvalidParam(
|
return Err!(Request(InvalidParam(
|
||||||
"Cannot authorise membership event through {authorising_user} as they do not belong to this homeserver"
|
"Cannot authorise membership event through {authorising_user} as they do not \
|
||||||
|
belong to this homeserver"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,12 +162,19 @@ async fn create_join_event(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err!(Request(InvalidParam(
|
return Err!(Request(InvalidParam(
|
||||||
"Authorising user {authorising_user} is not in the room you are trying to join, they cannot authorise \
|
"Authorising user {authorising_user} is not in the room you are trying to join, \
|
||||||
your join."
|
they cannot authorise your join."
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !super::user_can_perform_restricted_join(services, &state_key, room_id, &room_version_id).await? {
|
if !super::user_can_perform_restricted_join(
|
||||||
|
services,
|
||||||
|
&state_key,
|
||||||
|
room_id,
|
||||||
|
&room_version_id,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
return Err!(Request(UnableToAuthorizeJoin(
|
return Err!(Request(UnableToAuthorizeJoin(
|
||||||
"Joining user did not pass restricted room's rules."
|
"Joining user did not pass restricted room's rules."
|
||||||
)));
|
)));
|
||||||
|
@ -228,7 +240,9 @@ async fn create_join_event(
|
||||||
.event_ids_iter(room_id, starting_events)
|
.event_ids_iter(room_id, starting_events)
|
||||||
.await?
|
.await?
|
||||||
.map(Ok)
|
.map(Ok)
|
||||||
.broad_and_then(|event_id| async move { services.rooms.timeline.get_pdu_json(&event_id).await })
|
.broad_and_then(|event_id| async move {
|
||||||
|
services.rooms.timeline.get_pdu_json(&event_id).await
|
||||||
|
})
|
||||||
.broad_and_then(|pdu| {
|
.broad_and_then(|pdu| {
|
||||||
services
|
services
|
||||||
.sending
|
.sending
|
||||||
|
@ -252,7 +266,8 @@ async fn create_join_event(
|
||||||
///
|
///
|
||||||
/// Submits a signed join event.
|
/// Submits a signed join event.
|
||||||
pub(crate) async fn create_join_event_v1_route(
|
pub(crate) async fn create_join_event_v1_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_join_event::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_join_event::v1::Request>,
|
||||||
) -> Result<create_join_event::v1::Response> {
|
) -> Result<create_join_event::v1::Response> {
|
||||||
if services
|
if services
|
||||||
.globals
|
.globals
|
||||||
|
@ -261,8 +276,8 @@ pub(crate) async fn create_join_event_v1_route(
|
||||||
.contains(body.origin())
|
.contains(body.origin())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} tried joining room ID {} through us who has a server name that is globally forbidden. \
|
"Server {} tried joining room ID {} through us who has a server name that is \
|
||||||
Rejecting.",
|
globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.origin(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
);
|
);
|
||||||
|
@ -277,8 +292,8 @@ pub(crate) async fn create_join_event_v1_route(
|
||||||
.contains(&server.to_owned())
|
.contains(&server.to_owned())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} tried joining room ID {} through us which has a server name that is globally forbidden. \
|
"Server {} tried joining room ID {} through us which has a server name that is \
|
||||||
Rejecting.",
|
globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.origin(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
);
|
);
|
||||||
|
@ -292,16 +307,15 @@ pub(crate) async fn create_join_event_v1_route(
|
||||||
.boxed()
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(create_join_event::v1::Response {
|
Ok(create_join_event::v1::Response { room_state })
|
||||||
room_state,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`
|
/// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`
|
||||||
///
|
///
|
||||||
/// Submits a signed join event.
|
/// Submits a signed join event.
|
||||||
pub(crate) async fn create_join_event_v2_route(
|
pub(crate) async fn create_join_event_v2_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_join_event::v2::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_join_event::v2::Request>,
|
||||||
) -> Result<create_join_event::v2::Response> {
|
) -> Result<create_join_event::v2::Response> {
|
||||||
if services
|
if services
|
||||||
.globals
|
.globals
|
||||||
|
@ -320,8 +334,8 @@ pub(crate) async fn create_join_event_v2_route(
|
||||||
.contains(&server.to_owned())
|
.contains(&server.to_owned())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} tried joining room ID {} through us which has a server name that is globally forbidden. \
|
"Server {} tried joining room ID {} through us which has a server name that is \
|
||||||
Rejecting.",
|
globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.origin(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
);
|
);
|
||||||
|
@ -331,13 +345,10 @@ pub(crate) async fn create_join_event_v2_route(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_join_event::v1::RoomState {
|
let create_join_event::v1::RoomState { auth_chain, state, event } =
|
||||||
auth_chain,
|
create_join_event(&services, body.origin(), &body.room_id, &body.pdu)
|
||||||
state,
|
.boxed()
|
||||||
event,
|
.await?;
|
||||||
} = create_join_event(&services, body.origin(), &body.room_id, &body.pdu)
|
|
||||||
.boxed()
|
|
||||||
.await?;
|
|
||||||
let room_state = create_join_event::v2::RoomState {
|
let room_state = create_join_event::v2::RoomState {
|
||||||
members_omitted: false,
|
members_omitted: false,
|
||||||
auth_chain,
|
auth_chain,
|
||||||
|
@ -346,7 +357,5 @@ pub(crate) async fn create_join_event_v2_route(
|
||||||
servers_in_room: None,
|
servers_in_room: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(create_join_event::v2::Response {
|
Ok(create_join_event::v2::Response { room_state })
|
||||||
room_state,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ use crate::{
|
||||||
///
|
///
|
||||||
/// Submits a signed leave event.
|
/// Submits a signed leave event.
|
||||||
pub(crate) async fn create_leave_event_v1_route(
|
pub(crate) async fn create_leave_event_v1_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_leave_event::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_leave_event::v1::Request>,
|
||||||
) -> Result<create_leave_event::v1::Response> {
|
) -> Result<create_leave_event::v1::Response> {
|
||||||
create_leave_event(&services, body.origin(), &body.room_id, &body.pdu).await?;
|
create_leave_event(&services, body.origin(), &body.room_id, &body.pdu).await?;
|
||||||
|
|
||||||
|
@ -32,14 +33,20 @@ pub(crate) async fn create_leave_event_v1_route(
|
||||||
///
|
///
|
||||||
/// Submits a signed leave event.
|
/// Submits a signed leave event.
|
||||||
pub(crate) async fn create_leave_event_v2_route(
|
pub(crate) async fn create_leave_event_v2_route(
|
||||||
State(services): State<crate::State>, body: Ruma<create_leave_event::v2::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<create_leave_event::v2::Request>,
|
||||||
) -> Result<create_leave_event::v2::Response> {
|
) -> Result<create_leave_event::v2::Response> {
|
||||||
create_leave_event(&services, body.origin(), &body.room_id, &body.pdu).await?;
|
create_leave_event(&services, body.origin(), &body.room_id, &body.pdu).await?;
|
||||||
|
|
||||||
Ok(create_leave_event::v2::Response::new())
|
Ok(create_leave_event::v2::Response::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_leave_event(services: &Services, origin: &ServerName, room_id: &RoomId, pdu: &RawJsonValue) -> Result {
|
async fn create_leave_event(
|
||||||
|
services: &Services,
|
||||||
|
origin: &ServerName,
|
||||||
|
room_id: &RoomId,
|
||||||
|
pdu: &RawJsonValue,
|
||||||
|
) -> Result {
|
||||||
if !services.rooms.metadata.exists(room_id).await {
|
if !services.rooms.metadata.exists(room_id).await {
|
||||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ use crate::Ruma;
|
||||||
///
|
///
|
||||||
/// Retrieves a snapshot of a room's state at a given event.
|
/// Retrieves a snapshot of a room's state at a given event.
|
||||||
pub(crate) async fn get_room_state_route(
|
pub(crate) async fn get_room_state_route(
|
||||||
State(services): State<crate::State>, body: Ruma<get_room_state::v1::Request>,
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<get_room_state::v1::Request>,
|
||||||
) -> Result<get_room_state::v1::Response> {
|
) -> Result<get_room_state::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
|
@ -69,8 +70,5 @@ pub(crate) async fn get_room_state_route(
|
||||||
.try_collect()
|
.try_collect()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(get_room_state::v1::Response {
|
Ok(get_room_state::v1::Response { auth_chain, pdus })
|
||||||
auth_chain,
|
|
||||||
pdus,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue