ran cargo fmt

This commit is contained in:
tezlm 2024-08-22 15:13:38 -07:00
parent 6e105a8a41
commit 73ef497609
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
4 changed files with 350 additions and 192 deletions

View file

@ -75,18 +75,28 @@ impl service::rooms::alias::Data for KeyValueDatabase {
}) })
.transpose() .transpose()
} }
fn all_local_aliases<'a>(&'a self) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, String)>> + 'a> {
Box::new(self.alias_roomid.iter().map(|(room_alias_bytes, room_id_bytes)| {
let room_alias_localpart = utils::string_from_bytes(&room_alias_bytes)
.map_err(|_| Error::bad_database("Invalid alias bytes in aliasid_alias."))?;
let room_id = utils::string_from_bytes(&room_id_bytes) fn all_local_aliases<'a>(
.map_err(|_| Error::bad_database("Invalid room_id bytes in aliasid_alias."))? &'a self,
.try_into() ) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, String)>> + 'a> {
.map_err(|_| Error::bad_database("Invalid room_id in aliasid_alias."))?; Box::new(
self.alias_roomid
.iter()
.map(|(room_alias_bytes, room_id_bytes)| {
let room_alias_localpart = utils::string_from_bytes(&room_alias_bytes)
.map_err(|_| {
Error::bad_database("Invalid alias bytes in aliasid_alias.")
})?;
Ok((room_id, room_alias_localpart)) let room_id = utils::string_from_bytes(&room_id_bytes)
})) .map_err(|_| {
Error::bad_database("Invalid room_id bytes in aliasid_alias.")
})?
.try_into()
.map_err(|_| Error::bad_database("Invalid room_id in aliasid_alias."))?;
Ok((room_id, room_alias_localpart))
}),
)
} }
} }

View file

@ -3,7 +3,9 @@ use std::{collections::BTreeMap, convert::TryFrom, sync::Arc, time::Instant};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use regex::Regex; use regex::Regex;
use ruma::{ use ruma::{
api::appservice::Registration, events::{ api::appservice::Registration,
events::{
relation::InReplyTo,
room::{ room::{
canonical_alias::RoomCanonicalAliasEventContent, canonical_alias::RoomCanonicalAliasEventContent,
create::RoomCreateEventContent, create::RoomCreateEventContent,
@ -11,15 +13,15 @@ use ruma::{
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
join_rules::{JoinRule, RoomJoinRulesEventContent}, join_rules::{JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, RoomMemberEventContent}, member::{MembershipState, RoomMemberEventContent},
message::{RoomMessageEventContent, Relation::Reply}, message::{Relation::Reply, RoomMessageEventContent},
name::RoomNameEventContent, name::RoomNameEventContent,
power_levels::RoomPowerLevelsEventContent, power_levels::RoomPowerLevelsEventContent,
topic::RoomTopicEventContent, topic::RoomTopicEventContent,
}, },
TimelineEventType, relation::InReplyTo, TimelineEventType,
}, },
MilliSecondsSinceUnixEpoch, EventId, OwnedRoomAliasId, RoomAliasId, EventId, MilliSecondsSinceUnixEpoch, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomAliasId,
RoomId, OwnedRoomId, RoomVersionId, ServerName, UserId, OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
}; };
use serde_json::value::to_raw_value; use serde_json::value::to_raw_value;
use tokio::sync::{mpsc, Mutex, RwLock}; use tokio::sync::{mpsc, Mutex, RwLock};
@ -43,23 +45,23 @@ enum AdminCommand {
#[command(subcommand)] #[command(subcommand)]
/// Commands for managing appservices /// Commands for managing appservices
Appservice(AppserviceCommand), Appservice(AppserviceCommand),
#[command(subcommand)] #[command(subcommand)]
/// Commands for managing local users /// Commands for managing local users
User(UserCommand), User(UserCommand),
#[command(subcommand)] #[command(subcommand)]
/// Commands for managing rooms /// Commands for managing rooms
Room(RoomCommand), Room(RoomCommand),
#[command(subcommand)] #[command(subcommand)]
/// Commands for managing federation /// Commands for managing federation
Federation(FederationCommand), Federation(FederationCommand),
#[command(subcommand)] #[command(subcommand)]
/// Commands for managing the server /// Commands for managing the server
Server(ServerCommand), Server(ServerCommand),
#[command(subcommand)] #[command(subcommand)]
// TODO: should i split out debug commands to a separate thing? the // TODO: should i split out debug commands to a separate thing? the
// debug commands seem like they could fit in the other categories fine // debug commands seem like they could fit in the other categories fine
@ -129,7 +131,7 @@ enum UserCommand {
/// ///
/// User will not be removed from all rooms by default. /// User will not be removed from all rooms by default.
/// Use --leave-rooms to force the user to leave all rooms /// Use --leave-rooms to force the user to leave all rooms
Deactivate{ Deactivate {
#[arg(short, long)] #[arg(short, long)]
leave_rooms: bool, leave_rooms: bool,
user_id: Box<UserId>, user_id: Box<UserId>,
@ -157,7 +159,7 @@ enum UserCommand {
/// Also deactivate admin accounts /// Also deactivate admin accounts
force: bool, force: bool,
}, },
/// List local users in the database /// List local users in the database
List, List,
} }
@ -166,9 +168,7 @@ enum UserCommand {
#[derive(Subcommand)] #[derive(Subcommand)]
enum RoomCommand { enum RoomCommand {
/// List all rooms the server knows about /// List all rooms the server knows about
List { List { page: Option<usize> },
page: Option<usize>,
},
#[command(subcommand)] #[command(subcommand)]
/// Manage rooms' aliases /// Manage rooms' aliases
@ -187,20 +187,20 @@ enum RoomAliasCommand {
#[arg(short, long)] #[arg(short, long)]
/// Set the alias even if a room is already using it /// Set the alias even if a room is already using it
force: bool, force: bool,
/// The room id to set the alias on /// The room id to set the alias on
room_id: Box<RoomId>, room_id: Box<RoomId>,
/// The alias localpart to use (`alias`, not `#alias:servername.tld`) /// The alias localpart to use (`alias`, not `#alias:servername.tld`)
room_alias_localpart: Box<String>, room_alias_localpart: Box<String>,
}, },
/// Remove an alias /// Remove an alias
Remove { Remove {
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`) /// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
room_alias_localpart: Box<String>, room_alias_localpart: Box<String>,
}, },
/// Show which room is using an alias /// Show which room is using an alias
Which { Which {
/// The alias localpart to look up (`alias`, not `#alias:servername.tld`) /// The alias localpart to look up (`alias`, not `#alias:servername.tld`)
@ -222,7 +222,7 @@ enum RoomDirectoryCommand {
/// The room id of the room to publish /// The room id of the room to publish
room_id: Box<RoomId>, room_id: Box<RoomId>,
}, },
/// Unpublish a room to the room directory /// Unpublish a room to the room directory
Unpublish { Unpublish {
/// The room id of the room to unpublish /// The room id of the room to unpublish
@ -231,9 +231,7 @@ enum RoomDirectoryCommand {
/// List rooms that are published /// List rooms that are published
// TODO: is this really necessary? // TODO: is this really necessary?
List { List { page: Option<usize> },
page: Option<usize>,
},
} }
#[cfg_attr(test, derive(Debug))] #[cfg_attr(test, derive(Debug))]
@ -241,13 +239,13 @@ enum RoomDirectoryCommand {
enum FederationCommand { enum FederationCommand {
/// List all rooms we are currently handling an incoming pdu from /// List all rooms we are currently handling an incoming pdu from
IncomingFederation, IncomingFederation,
/// Disables incoming federation handling for a room. /// Disables incoming federation handling for a room.
DisableRoom { room_id: Box<RoomId> }, DisableRoom { room_id: Box<RoomId> },
/// Enables incoming federation handling for a room again. /// Enables incoming federation handling for a room again.
EnableRoom { room_id: Box<RoomId> }, EnableRoom { room_id: Box<RoomId> },
#[command(verbatim_doc_comment)] #[command(verbatim_doc_comment)]
/// Verify json signatures /// Verify json signatures
/// [commandbody] /// [commandbody]
@ -298,7 +296,7 @@ enum DebugCommand {
enum ServerCommand { enum ServerCommand {
/// Show configuration values /// Show configuration values
ShowConfig, ShowConfig,
/// Print database memory usage statistics /// Print database memory usage statistics
MemoryUsage, MemoryUsage,
@ -366,7 +364,7 @@ impl Service {
if let Some(reply) = reply { if let Some(reply) = reply {
message_content.relates_to = Some(Reply { in_reply_to: InReplyTo { event_id: reply.into() } }) message_content.relates_to = Some(Reply { in_reply_to: InReplyTo { event_id: reply.into() } })
} }
services() services()
.rooms .rooms
.timeline .timeline
@ -394,9 +392,18 @@ impl Service {
} }
} }
pub fn process_message(&self, room_message: String, event_id: Arc<EventId>, sender: OwnedUserId) { pub fn process_message(
&self,
room_message: String,
event_id: Arc<EventId>,
sender: OwnedUserId,
) {
self.sender self.sender
.send(AdminRoomEvent::ProcessMessage(room_message, event_id, sender)) .send(AdminRoomEvent::ProcessMessage(
room_message,
event_id,
sender,
))
.unwrap(); .unwrap();
} }
@ -407,7 +414,11 @@ impl Service {
} }
// Parse and process a message from the admin room // Parse and process a message from the admin room
async fn process_admin_message(&self, room_message: String, sender: &UserId) -> RoomMessageEventContent { async fn process_admin_message(
&self,
room_message: String,
sender: &UserId,
) -> RoomMessageEventContent {
let mut lines = room_message.lines().filter(|l| !l.trim().is_empty()); let mut lines = room_message.lines().filter(|l| !l.trim().is_empty());
let command_line = lines.next().expect("each string has at least one line"); let command_line = lines.next().expect("each string has at least one line");
let body: Vec<_> = lines.collect(); let body: Vec<_> = lines.collect();
@ -423,7 +434,10 @@ impl Service {
} }
}; };
match self.process_admin_command(admin_command, body, sender).await { match self
.process_admin_command(admin_command, body, sender)
.await
{
Ok(reply_message) => reply_message, Ok(reply_message) => reply_message,
Err(error) => { Err(error) => {
let markdown_message = format!( let markdown_message = format!(
@ -471,13 +485,16 @@ impl Service {
let reply_message_content = match command { let reply_message_content = match command {
AdminCommand::Appservice(command) => match command { AdminCommand::Appservice(command) => match command {
AppserviceCommand::Register => { AppserviceCommand::Register => {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" if body.len() > 2
&& body[0].trim().starts_with("```")
&& body.last().unwrap().trim() == "```"
{ {
let appservice_config = body[1..body.len() - 1].join("\n"); let appservice_config = body[1..body.len() - 1].join("\n");
let parsed_config = let parsed_config =
serde_yaml::from_str::<Registration>(&appservice_config); serde_yaml::from_str::<Registration>(&appservice_config);
match parsed_config { match parsed_config {
Ok(yaml) => match services().appservice.register_appservice(yaml).await { Ok(yaml) => match services().appservice.register_appservice(yaml).await
{
Ok(id) => RoomMessageEventContent::text_plain(format!( Ok(id) => RoomMessageEventContent::text_plain(format!(
"Appservice registered with ID: {id}." "Appservice registered with ID: {id}."
)), )),
@ -507,18 +524,20 @@ impl Service {
"Failed to unregister appservice: {e}" "Failed to unregister appservice: {e}"
)), )),
}, },
AppserviceCommand::Show { appservice_identifier } => { AppserviceCommand::Show {
appservice_identifier,
} => {
match services() match services()
.appservice .appservice
.get_registration(&appservice_identifier) .get_registration(&appservice_identifier)
.await { .await
{
Some(registration) => { Some(registration) => {
let config_str = serde_yaml::to_string(&registration) let config_str = serde_yaml::to_string(&registration)
.expect("config should've been validated on register"); .expect("config should've been validated on register");
let output = format!( let output = format!(
"Config for {}:\n\n```yaml\n{}\n```", "Config for {}:\n\n```yaml\n{}\n```",
appservice_identifier, appservice_identifier, config_str,
config_str,
); );
let output_html = format!( let output_html = format!(
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>", "Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
@ -526,28 +545,22 @@ impl Service {
escape_html(&config_str), escape_html(&config_str),
); );
RoomMessageEventContent::text_html(output, output_html) RoomMessageEventContent::text_html(output, output_html)
}, }
None => RoomMessageEventContent::text_plain("Appservice does not exist."), None => RoomMessageEventContent::text_plain("Appservice does not exist."),
} }
} }
AppserviceCommand::List => { AppserviceCommand::List => {
let appservices = services() let appservices = services().appservice.iter_ids().await;
.appservice
.iter_ids()
.await;
let count = appservices.len(); let count = appservices.len();
let output = format!( let output = format!("Appservices ({}): {}", count, appservices.join(", "));
"Appservices ({}): {}",
count,
appservices.join(", ")
);
RoomMessageEventContent::text_plain(output) RoomMessageEventContent::text_plain(output)
} }
} },
AdminCommand::User(command) => match command { AdminCommand::User(command) => match command {
UserCommand::List => match services().users.list_local_users() { UserCommand::List => match services().users.list_local_users() {
Ok(users) => { Ok(users) => {
let mut msg: String = format!("Found {} local user account(s):\n", users.len()); let mut msg: String =
format!("Found {} local user account(s):\n", users.len());
msg += &users.join("\n"); msg += &users.join("\n");
RoomMessageEventContent::text_plain(&msg) RoomMessageEventContent::text_plain(&msg)
} }
@ -615,7 +628,10 @@ impl Service {
"Created user with user_id: {user_id} and password: {password}" "Created user with user_id: {user_id} and password: {password}"
)) ))
} }
UserCommand::Deactivate { leave_rooms, user_id } => { UserCommand::Deactivate {
leave_rooms,
user_id,
} => {
let user_id = Arc::<UserId>::from(user_id); let user_id = Arc::<UserId>::from(user_id);
if services().users.exists(&user_id)? { if services().users.exists(&user_id)? {
RoomMessageEventContent::text_plain(format!( RoomMessageEventContent::text_plain(format!(
@ -677,9 +693,11 @@ impl Service {
"Couldn't reset the password for user {user_id}: {e}" "Couldn't reset the password for user {user_id}: {e}"
)), )),
} }
}, }
UserCommand::DeactivateAll { leave_rooms, force } => { UserCommand::DeactivateAll { leave_rooms, force } => {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" if body.len() > 2
&& body[0].trim().starts_with("```")
&& body.last().unwrap().trim() == "```"
{ {
let usernames = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>(); let usernames = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
@ -736,37 +754,57 @@ impl Service {
"Expected code block in command body. Add --help for details.", "Expected code block in command body. Add --help for details.",
) )
} }
}, }
} },
AdminCommand::Room(command) => match command { AdminCommand::Room(command) => match command {
RoomCommand::List { page } => { RoomCommand::List { 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 = services().rooms.metadata.iter_ids() let mut rooms = services()
.rooms
.metadata
.iter_ids()
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.map(|id| ( .map(|id| {
id.clone(), (
services().rooms.state_cache.room_joined_count(&id).ok().flatten().unwrap_or(0), id.clone(),
services().rooms.state_accessor.get_name(&id).ok().flatten().unwrap_or(id.to_string()), services()
)) .rooms
.state_cache
.room_joined_count(&id)
.ok()
.flatten()
.unwrap_or(0),
services()
.rooms
.state_accessor
.get_name(&id)
.ok()
.flatten()
.unwrap_or(id.to_string()),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
rooms.sort_by_key(|r| r.1); rooms.sort_by_key(|r| r.1);
rooms.reverse(); rooms.reverse();
let rooms: Vec<_> = rooms.into_iter() let rooms: Vec<_> = rooms
.into_iter()
.skip(page.saturating_sub(1) * PAGE_SIZE) .skip(page.saturating_sub(1) * PAGE_SIZE)
.take(PAGE_SIZE) .take(PAGE_SIZE)
.collect(); .collect();
if rooms.is_empty() { if rooms.is_empty() {
return Ok(RoomMessageEventContent::text_plain("No more rooms.")); return Ok(RoomMessageEventContent::text_plain("No more rooms."));
}; };
let output_plain = format!( let output_plain = format!(
"Rooms:\n{}", "Rooms:\n{}",
rooms rooms
.iter() .iter()
.map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}")) .map(|(id, members, name)| format!(
"{id}\tMembers: {members}\tName: {name}"
))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n")
); );
@ -785,18 +823,35 @@ impl Service {
RoomMessageEventContent::text_html(output_plain, output_html) RoomMessageEventContent::text_html(output_plain, output_html)
} }
// TODO: clean up and deduplicate code // TODO: clean up and deduplicate code
RoomCommand::Alias(command) => { RoomCommand::Alias(command) => match command {
match command { RoomAliasCommand::Set {
RoomAliasCommand::Set { ref room_alias_localpart, .. } | RoomAliasCommand::Remove { ref room_alias_localpart } | RoomAliasCommand::Which { ref room_alias_localpart } => { 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) { }
Ok(alias) => alias, | RoomAliasCommand::Remove {
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {}", err))), 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) {
Ok(alias) => alias,
Err(err) => {
return Ok(RoomMessageEventContent::text_plain(format!(
"Failed to parse alias: {}",
err
)))
}
};
match command { match command {
RoomAliasCommand::Set { force, room_id, .. } => { RoomAliasCommand::Set { force, room_id, .. } => {
match (force, services().rooms.alias.resolve_local_alias(&room_alias)) { match (force, services().rooms.alias.resolve_local_alias(&room_alias)) {
(true, Ok(Some(id))) => match services().rooms.alias.set_alias(&room_alias, &room_id, sender) { (true, Ok(Some(id))) => match services().rooms.alias.set_alias(&room_alias, &room_id, sender) {
Ok(()) => RoomMessageEventContent::text_plain(format!("Successfully overwrote alias (formerly {})", id)), Ok(()) => RoomMessageEventContent::text_plain(format!("Successfully overwrote alias (formerly {})", id)),
Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)), Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)),
@ -809,107 +864,189 @@ impl Service {
Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)), Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)),
} }
(_, Err(err)) => RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {}", err)), (_, Err(err)) => RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {}", err)),
}
},
RoomAliasCommand::Remove { .. } => {
match services().rooms.alias.resolve_local_alias(&room_alias) {
Ok(Some(id)) => match services().rooms.alias.remove_alias(&room_alias, sender) {
Ok(()) => RoomMessageEventContent::text_plain(format!("Removed alias from {}", id)),
Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)),
}
Ok(None) => RoomMessageEventContent::text_plain("Alias isn't in use."),
Err(err) => RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {}", err)),
} }
},
RoomAliasCommand::Which { .. } => {
match services().rooms.alias.resolve_local_alias(&room_alias) {
Ok(Some(id)) => RoomMessageEventContent::text_plain(format!("Alias resolves to {}", id)),
Ok(None) => RoomMessageEventContent::text_plain("Alias isn't in use."),
Err(err) => RoomMessageEventContent::text_plain(&format!("Unable to lookup alias: {}", err)),
}
},
RoomAliasCommand::List { .. } => unreachable!(),
} }
} RoomAliasCommand::Remove { .. } => {
RoomAliasCommand::List { room_id } => match room_id { match services().rooms.alias.resolve_local_alias(&room_alias) {
Some(room_id) => { Ok(Some(id)) => match services()
let aliases: Result<Vec<_>, _> = services().rooms.alias.local_aliases_for_room(&room_id).collect(); .rooms
match aliases { .alias
Ok(aliases) => { .remove_alias(&room_alias, sender)
let plain_list: String = aliases.iter() {
.map(|alias| format!("- {}\n", alias)) Ok(()) => RoomMessageEventContent::text_plain(format!(
.collect(); "Removed alias from {}",
id
let html_list: String = aliases.iter() )),
.map(|alias| format!("<li>{}</li>\n", escape_html(&alias.to_string()))) Err(err) => RoomMessageEventContent::text_plain(format!(
.collect(); "Failed to remove alias: {}",
err
let plain = format!("Aliases for {}:\n{}", room_id, plain_list); )),
let html = format!("Aliases for {}:\n<ul>{}</ul>", room_id, html_list);
RoomMessageEventContent::text_html(plain, html)
}, },
Err(err) => RoomMessageEventContent::text_plain(&format!("Unable to list aliases: {}", err)), Ok(None) => {
RoomMessageEventContent::text_plain("Alias isn't in use.")
}
Err(err) => RoomMessageEventContent::text_plain(format!(
"Unable to lookup alias: {}",
err
)),
} }
} }
None => { RoomAliasCommand::Which { .. } => {
let aliases: Result<Vec<_>, _> = services().rooms.alias.all_local_aliases().collect(); match services().rooms.alias.resolve_local_alias(&room_alias) {
match aliases { Ok(Some(id)) => RoomMessageEventContent::text_plain(format!(
Ok(aliases) => { "Alias resolves to {}",
let server_name = services().globals.server_name(); id
let plain_list: String = aliases.iter() )),
.map(|(id, alias)| format!("- #{}:{} -> {}\n", alias, server_name, id)) Ok(None) => {
.collect(); RoomMessageEventContent::text_plain("Alias isn't in use.")
}
let html_list: String = aliases.iter() Err(err) => RoomMessageEventContent::text_plain(&format!(
.map(|(id, alias)| format!("<li>#{}:{} -> {}</li>\n", escape_html(&alias.to_string()), server_name, escape_html(&id.to_string()))) "Unable to lookup alias: {}",
.collect(); err
)),
let plain = format!("Aliases:\n{}", plain_list);
let html = format!("Aliases:\n<ul>{}</ul>", html_list);
RoomMessageEventContent::text_html(plain, html)
},
Err(err) => RoomMessageEventContent::text_plain(&format!("Unable to list aliases: {}", err)),
} }
} }
RoomAliasCommand::List { .. } => unreachable!(),
} }
} }
} RoomAliasCommand::List { room_id } => match room_id {
Some(room_id) => {
let aliases: Result<Vec<_>, _> = services()
.rooms
.alias
.local_aliases_for_room(&room_id)
.collect();
match aliases {
Ok(aliases) => {
let plain_list: String = aliases
.iter()
.map(|alias| format!("- {}\n", alias))
.collect();
let html_list: String = aliases
.iter()
.map(|alias| {
format!(
"<li>{}</li>\n",
escape_html(&alias.to_string())
)
})
.collect();
let plain = format!("Aliases for {}:\n{}", room_id, plain_list);
let html =
format!("Aliases for {}:\n<ul>{}</ul>", room_id, html_list);
RoomMessageEventContent::text_html(plain, html)
}
Err(err) => RoomMessageEventContent::text_plain(&format!(
"Unable to list aliases: {}",
err
)),
}
}
None => {
let aliases: Result<Vec<_>, _> =
services().rooms.alias.all_local_aliases().collect();
match aliases {
Ok(aliases) => {
let server_name = services().globals.server_name();
let plain_list: String = aliases
.iter()
.map(|(id, alias)| {
format!("- #{}:{} -> {}\n", alias, server_name, id)
})
.collect();
let html_list: String = aliases
.iter()
.map(|(id, alias)| {
format!(
"<li>#{}:{} -> {}</li>\n",
escape_html(&alias.to_string()),
server_name,
escape_html(&id.to_string())
)
})
.collect();
let plain = format!("Aliases:\n{}", plain_list);
let html = format!("Aliases:\n<ul>{}</ul>", html_list);
RoomMessageEventContent::text_html(plain, html)
}
Err(err) => RoomMessageEventContent::text_plain(&format!(
"Unable to list aliases: {}",
err
)),
}
}
},
},
RoomCommand::Directory(command) => match command { RoomCommand::Directory(command) => match command {
RoomDirectoryCommand::Publish { room_id } => match services().rooms.directory.set_public(&room_id) { RoomDirectoryCommand::Publish { room_id } => {
Ok(()) => RoomMessageEventContent::text_plain("Room published"), match services().rooms.directory.set_public(&room_id) {
Err(err) => RoomMessageEventContent::text_plain(&format!("Unable to update room: {}", err)), Ok(()) => RoomMessageEventContent::text_plain("Room published"),
Err(err) => RoomMessageEventContent::text_plain(&format!(
"Unable to update room: {}",
err
)),
}
} }
RoomDirectoryCommand::Unpublish { room_id } => match services().rooms.directory.set_not_public(&room_id) { RoomDirectoryCommand::Unpublish { room_id } => {
Ok(()) => RoomMessageEventContent::text_plain("Room unpublished"), match services().rooms.directory.set_not_public(&room_id) {
Err(err) => RoomMessageEventContent::text_plain(&format!("Unable to update room: {}", err)), Ok(()) => RoomMessageEventContent::text_plain("Room unpublished"),
Err(err) => RoomMessageEventContent::text_plain(&format!(
"Unable to update room: {}",
err
)),
}
} }
RoomDirectoryCommand::List { page } => { RoomDirectoryCommand::List { page } => {
let page = page.unwrap_or(1); let page = page.unwrap_or(1);
let mut rooms = services().rooms.directory.public_rooms() let mut rooms = services()
.rooms
.directory
.public_rooms()
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.map(|id| ( .map(|id| {
id.clone(), (
services().rooms.state_cache.room_joined_count(&id).ok().flatten().unwrap_or(0), id.clone(),
services().rooms.state_accessor.get_name(&id).ok().flatten().unwrap_or(id.to_string()), services()
)) .rooms
.state_cache
.room_joined_count(&id)
.ok()
.flatten()
.unwrap_or(0),
services()
.rooms
.state_accessor
.get_name(&id)
.ok()
.flatten()
.unwrap_or(id.to_string()),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
rooms.sort_by_key(|r| r.1); rooms.sort_by_key(|r| r.1);
rooms.reverse(); rooms.reverse();
let rooms: Vec<_> = rooms.into_iter() let rooms: Vec<_> = rooms
.into_iter()
.skip(page.saturating_sub(1) * PAGE_SIZE) .skip(page.saturating_sub(1) * PAGE_SIZE)
.take(PAGE_SIZE) .take(PAGE_SIZE)
.collect(); .collect();
if rooms.is_empty() { if rooms.is_empty() {
return Ok(RoomMessageEventContent::text_plain("No more rooms.")); return Ok(RoomMessageEventContent::text_plain("No more rooms."));
}; };
let output_plain = format!( let output_plain = format!(
"Rooms:\n{}", "Rooms:\n{}",
rooms rooms
.iter() .iter()
.map(|(id, members, name)| format!("{id}\tMembers: {members}\tName: {name}")) .map(|(id, members, name)| format!(
"{id}\tMembers: {members}\tName: {name}"
))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n")
); );
@ -927,8 +1064,8 @@ impl Service {
); );
RoomMessageEventContent::text_html(output_plain, output_html) RoomMessageEventContent::text_html(output_plain, output_html)
} }
} },
} },
AdminCommand::Federation(command) => match command { AdminCommand::Federation(command) => match command {
FederationCommand::DisableRoom { room_id } => { FederationCommand::DisableRoom { room_id } => {
services().rooms.metadata.disable_room(&room_id, true)?; services().rooms.metadata.disable_room(&room_id, true)?;
@ -939,11 +1076,7 @@ impl Service {
RoomMessageEventContent::text_plain("Room enabled.") RoomMessageEventContent::text_plain("Room enabled.")
} }
FederationCommand::IncomingFederation => { FederationCommand::IncomingFederation => {
let map = services() let map = services().globals.roomid_federationhandletime.read().await;
.globals
.roomid_federationhandletime
.read()
.await;
let mut msg: String = format!("Handling {} incoming pdus:\n", map.len()); let mut msg: String = format!("Handling {} incoming pdus:\n", map.len());
for (r, (e, i)) in map.iter() { for (r, (e, i)) in map.iter() {
@ -957,9 +1090,11 @@ impl Service {
); );
} }
RoomMessageEventContent::text_plain(&msg) RoomMessageEventContent::text_plain(&msg)
}, }
FederationCommand::SignJson => { FederationCommand::SignJson => {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" if body.len() > 2
&& body[0].trim().starts_with("```")
&& body.last().unwrap().trim() == "```"
{ {
let string = body[1..body.len() - 1].join("\n"); let string = body[1..body.len() - 1].join("\n");
match serde_json::from_str(&string) { match serde_json::from_str(&string) {
@ -974,16 +1109,20 @@ impl Service {
.expect("canonical json is valid json"); .expect("canonical json is valid json");
RoomMessageEventContent::text_plain(json_text) RoomMessageEventContent::text_plain(json_text)
} }
Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")), Err(e) => {
RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))
}
} }
} else { } else {
RoomMessageEventContent::text_plain( RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.", "Expected code block in command body. Add --help for details.",
) )
} }
}, }
FederationCommand::VerifyJson => { FederationCommand::VerifyJson => {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" if body.len() > 2
&& body[0].trim().starts_with("```")
&& body.last().unwrap().trim() == "```"
{ {
let string = body[1..body.len() - 1].join("\n"); let string = body[1..body.len() - 1].join("\n");
match serde_json::from_str(&string) { match serde_json::from_str(&string) {
@ -1018,8 +1157,7 @@ impl Service {
); );
} }
} }
if ruma::signatures::verify_json(&valid_key_map, &value).is_ok() { if ruma::signatures::verify_json(&valid_key_map, &value).is_ok() {
RoomMessageEventContent::text_plain("Signature correct") RoomMessageEventContent::text_plain("Signature correct")
} else if let Err(e) = } else if let Err(e) =
@ -1034,20 +1172,22 @@ impl Service {
) )
} }
} }
Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")), Err(e) => {
RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))
}
} }
} else { } else {
RoomMessageEventContent::text_plain( RoomMessageEventContent::text_plain(
"Expected code block in command body. Add --help for details.", "Expected code block in command body. Add --help for details.",
) )
} }
}, }
} },
AdminCommand::Server(command) => match command { AdminCommand::Server(command) => match command {
ServerCommand::ShowConfig => { ServerCommand::ShowConfig => {
// Construct and send the response // Construct and send the response
RoomMessageEventContent::text_plain(format!("{}", services().globals.config)) RoomMessageEventContent::text_plain(format!("{}", services().globals.config))
}, }
ServerCommand::MemoryUsage => { ServerCommand::MemoryUsage => {
let response1 = services().memory_usage().await; let response1 = services().memory_usage().await;
let response2 = services().globals.db.memory_usage(); let response2 = services().globals.db.memory_usage();
@ -1055,18 +1195,18 @@ impl Service {
RoomMessageEventContent::text_plain(format!( RoomMessageEventContent::text_plain(format!(
"Services:\n{response1}\n\nDatabase:\n{response2}" "Services:\n{response1}\n\nDatabase:\n{response2}"
)) ))
}, }
ServerCommand::ClearDatabaseCaches { amount } => { ServerCommand::ClearDatabaseCaches { amount } => {
services().globals.db.clear_caches(amount); services().globals.db.clear_caches(amount);
RoomMessageEventContent::text_plain("Done.") RoomMessageEventContent::text_plain("Done.")
}, }
ServerCommand::ClearServiceCaches { amount } => { ServerCommand::ClearServiceCaches { amount } => {
services().clear_caches(amount).await; services().clear_caches(amount).await;
RoomMessageEventContent::text_plain("Done.") RoomMessageEventContent::text_plain("Done.")
}, }
} },
AdminCommand::Debug(command) => match command { AdminCommand::Debug(command) => match command {
DebugCommand::GetAuthChain { event_id } => { DebugCommand::GetAuthChain { event_id } => {
let event_id = Arc::<EventId>::from(event_id); let event_id = Arc::<EventId>::from(event_id);
@ -1093,9 +1233,11 @@ impl Service {
} else { } else {
RoomMessageEventContent::text_plain("Event not found.") RoomMessageEventContent::text_plain("Event not found.")
} }
}, }
DebugCommand::ParsePdu => { DebugCommand::ParsePdu => {
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" if body.len() > 2
&& body[0].trim().starts_with("```")
&& body.last().unwrap().trim() == "```"
{ {
let string = body[1..body.len() - 1].join("\n"); let string = body[1..body.len() - 1].join("\n");
match serde_json::from_str(&string) { match serde_json::from_str(&string) {
@ -1107,9 +1249,9 @@ impl Service {
match serde_json::from_value::<PduEvent>( match serde_json::from_value::<PduEvent>(
serde_json::to_value(value).expect("value is json"), serde_json::to_value(value).expect("value is json"),
) { ) {
Ok(pdu) => RoomMessageEventContent::text_plain(format!( Ok(pdu) => RoomMessageEventContent::text_plain(
"EventId: {event_id:?}\n{pdu:#?}" format!("EventId: {event_id:?}\n{pdu:#?}"),
)), ),
Err(e) => RoomMessageEventContent::text_plain(format!( Err(e) => RoomMessageEventContent::text_plain(format!(
"EventId: {event_id:?}\nCould not parse event: {e}" "EventId: {event_id:?}\nCould not parse event: {e}"
)), )),
@ -1127,7 +1269,7 @@ impl Service {
} else { } else {
RoomMessageEventContent::text_plain("Expected code block in command body.") RoomMessageEventContent::text_plain("Expected code block in command body.")
} }
}, }
DebugCommand::GetPdu { event_id } => { DebugCommand::GetPdu { event_id } => {
let mut outlier = false; let mut outlier = false;
let mut pdu_json = services() let mut pdu_json = services()
@ -1165,8 +1307,8 @@ impl Service {
} }
None => RoomMessageEventContent::text_plain("PDU not found."), None => RoomMessageEventContent::text_plain("PDU not found."),
} }
}, }
} },
}; };
Ok(reply_message_content) Ok(reply_message_content)
@ -1659,7 +1801,9 @@ impl Service {
} }
fn escape_html(s: &str) -> String { fn escape_html(s: &str) -> String {
s.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;") s.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
} }
#[cfg(test)] #[cfg(test)]

View file

@ -19,7 +19,7 @@ pub trait Data: Send + Sync {
&'a self, &'a self,
room_id: &RoomId, room_id: &RoomId,
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a>; ) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a>;
/// Returns all local aliases on the server /// Returns all local aliases on the server
fn all_local_aliases<'a>( fn all_local_aliases<'a>(
&'a self, &'a self,

View file

@ -506,7 +506,11 @@ impl Service {
.state_cache .state_cache
.is_joined(server_user, &admin_room)? .is_joined(server_user, &admin_room)?
{ {
services().admin.process_message(body, pdu.event_id.clone(), pdu.sender.clone()); services().admin.process_message(
body,
pdu.event_id.clone(),
pdu.sender.clone(),
);
} }
} }
} }