diff --git a/src/admin/appservice/appservice_command.rs b/src/admin/appservice/appservice_command.rs index d15cb7a6..6f36ebf7 100644 --- a/src/admin/appservice/appservice_command.rs +++ b/src/admin/appservice/appservice_command.rs @@ -3,26 +3,26 @@ use ruma::{api::appservice::Registration, events::room::message::RoomMessageEven use crate::{escape_html, services, Result}; pub(crate) async fn register(body: Vec<&str>) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n"); - let parsed_config = serde_yaml::from_str::(&appservice_config); - match parsed_config { - Ok(yaml) => match services().appservice.register_appservice(yaml).await { - Ok(id) => Ok(RoomMessageEventContent::text_plain(format!( - "Appservice registered with ID: {id}." - ))), - Err(e) => Ok(RoomMessageEventContent::text_plain(format!( - "Failed to register appservice: {e}" - ))), - }, - Err(e) => Ok(RoomMessageEventContent::text_plain(format!( - "Could not parse appservice config: {e}" - ))), - } - } else { - Ok(RoomMessageEventContent::text_plain( + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( "Expected code block in command body. Add --help for details.", - )) + )); + } + + let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n"); + let parsed_config = serde_yaml::from_str::(&appservice_config); + match parsed_config { + Ok(yaml) => match services().appservice.register_appservice(yaml).await { + Ok(id) => Ok(RoomMessageEventContent::text_plain(format!( + "Appservice registered with ID: {id}." + ))), + Err(e) => Ok(RoomMessageEventContent::text_plain(format!( + "Failed to register appservice: {e}" + ))), + }, + Err(e) => Ok(RoomMessageEventContent::text_plain(format!( + "Could not parse appservice config: {e}" + ))), } } diff --git a/src/admin/debug/debug_commands.rs b/src/admin/debug/debug_commands.rs index 56563748..bd15ba50 100644 --- a/src/admin/debug/debug_commands.rs +++ b/src/admin/debug/debug_commands.rs @@ -43,28 +43,30 @@ pub(crate) async fn get_auth_chain(_body: Vec<&str>, event_id: Box) -> } pub(crate) async fn parse_pdu(body: Vec<&str>) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let string = body[1..body.len() - 1].join("\n"); - match serde_json::from_str(&string) { - Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) { - Ok(hash) => { - let event_id = EventId::parse(format!("${hash}")); + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( + "Expected code block in command body. Add --help for details.", + )); + } - match serde_json::from_value::(serde_json::to_value(value).expect("value is json")) { - 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}" - ))), - } - }, - Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Could not parse PDU JSON: {e:?}"))), + let string = body[1..body.len() - 1].join("\n"); + match serde_json::from_str(&string) { + Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) { + Ok(hash) => { + let event_id = EventId::parse(format!("${hash}")); + + match serde_json::from_value::(serde_json::to_value(value).expect("value is json")) { + 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}" + ))), + } }, - Err(e) => Ok(RoomMessageEventContent::text_plain(format!( - "Invalid json in command body: {e}" - ))), - } - } else { - Ok(RoomMessageEventContent::text_plain("Expected code block in command body.")) + Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Could not parse PDU JSON: {e:?}"))), + }, + Err(e) => Ok(RoomMessageEventContent::text_plain(format!( + "Invalid json in command body: {e}" + ))), } } @@ -117,33 +119,40 @@ pub(crate) async fn get_remote_pdu_list( if server == services().globals.server_name() { 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 from \ + the database.", )); } - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let list = body - .clone() - .drain(1..body.len().checked_sub(1).unwrap()) - .filter_map(|pdu| EventId::parse(pdu).ok()) - .collect::>(); - - for pdu in list { - if force { - if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await { - warn!(%e, "Failed to get remote PDU, ignoring error"); - } - } else { - get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?; - } - } - - return Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs.")); + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( + "Expected code block in command body. Add --help for details.", + )); } - Ok(RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", - )) + let list = body + .clone() + .drain(1..body.len().checked_sub(1).unwrap()) + .filter_map(|pdu| EventId::parse(pdu).ok()) + .collect::>(); + + for pdu in list { + if force { + if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await { + services() + .admin + .send_message(RoomMessageEventContent::text_plain(format!( + "Failed to get remote PDU, ignoring error: {e}" + ))) + .await; + warn!(%e, "Failed to get remote PDU, ignoring error"); + } + } else { + get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?; + } + } + + Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs.")) } pub(crate) async fn get_remote_pdu( @@ -384,55 +393,55 @@ pub(crate) async fn change_log_level( } pub(crate) async fn sign_json(body: Vec<&str>) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let string = body[1..body.len().checked_sub(1).unwrap()].join("\n"); - match serde_json::from_str(&string) { - Ok(mut value) => { - ruma::signatures::sign_json( - services().globals.server_name().as_str(), - services().globals.keypair(), - &mut value, - ) - .expect("our request json is what ruma expects"); - let json_text = serde_json::to_string_pretty(&value).expect("canonical json is valid json"); - Ok(RoomMessageEventContent::text_plain(json_text)) - }, - Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))), - } - } else { - Ok(RoomMessageEventContent::text_plain( + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( "Expected code block in command body. Add --help for details.", - )) + )); + } + + let string = body[1..body.len().checked_sub(1).unwrap()].join("\n"); + match serde_json::from_str(&string) { + Ok(mut value) => { + ruma::signatures::sign_json( + services().globals.server_name().as_str(), + services().globals.keypair(), + &mut value, + ) + .expect("our request json is what ruma expects"); + let json_text = serde_json::to_string_pretty(&value).expect("canonical json is valid json"); + Ok(RoomMessageEventContent::text_plain(json_text)) + }, + Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))), } } pub(crate) async fn verify_json(body: Vec<&str>) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let string = body[1..body.len().checked_sub(1).unwrap()].join("\n"); - match serde_json::from_str(&string) { - Ok(value) => { - let pub_key_map = RwLock::new(BTreeMap::new()); - - services() - .rooms - .event_handler - .fetch_required_signing_keys([&value], &pub_key_map) - .await?; - - let pub_key_map = pub_key_map.read().await; - match ruma::signatures::verify_json(&pub_key_map, &value) { - Ok(()) => Ok(RoomMessageEventContent::text_plain("Signature correct")), - Err(e) => Ok(RoomMessageEventContent::text_plain(format!( - "Signature verification failed: {e}" - ))), - } - }, - Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))), - } - } else { - Ok(RoomMessageEventContent::text_plain( + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( "Expected code block in command body. Add --help for details.", - )) + )); + } + + let string = body[1..body.len().checked_sub(1).unwrap()].join("\n"); + match serde_json::from_str(&string) { + Ok(value) => { + let pub_key_map = RwLock::new(BTreeMap::new()); + + services() + .rooms + .event_handler + .fetch_required_signing_keys([&value], &pub_key_map) + .await?; + + let pub_key_map = pub_key_map.read().await; + match ruma::signatures::verify_json(&pub_key_map, &value) { + Ok(()) => Ok(RoomMessageEventContent::text_plain("Signature correct")), + Err(e) => Ok(RoomMessageEventContent::text_plain(format!( + "Signature verification failed: {e}" + ))), + } + }, + Err(e) => Ok(RoomMessageEventContent::text_plain(format!("Invalid json: {e}"))), } } diff --git a/src/admin/media/media_commands.rs b/src/admin/media/media_commands.rs index 956f5743..a78ee387 100644 --- a/src/admin/media/media_commands.rs +++ b/src/admin/media/media_commands.rs @@ -138,30 +138,30 @@ pub(crate) async fn delete( } pub(crate) async fn delete_list(body: Vec<&str>) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let mxc_list = body - .clone() - .drain(1..body.len().checked_sub(1).unwrap()) - .collect::>(); - - let mut mxc_deletion_count: usize = 0; - - for mxc in mxc_list { - debug!("Deleting MXC {mxc} in bulk"); - services().media.delete(mxc.to_owned()).await?; - mxc_deletion_count = mxc_deletion_count - .checked_add(1) - .expect("mxc_deletion_count should not get this high"); - } - - return Ok(RoomMessageEventContent::text_plain(format!( - "Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database and the filesystem.", - ))); + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( + "Expected code block in command body. Add --help for details.", + )); } - Ok(RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", - )) + let mxc_list = body + .clone() + .drain(1..body.len().checked_sub(1).unwrap()) + .collect::>(); + + let mut mxc_deletion_count: usize = 0; + + for mxc in mxc_list { + debug!("Deleting MXC {mxc} in bulk"); + services().media.delete(mxc.to_owned()).await?; + mxc_deletion_count = mxc_deletion_count + .checked_add(1) + .expect("mxc_deletion_count should not get this high"); + } + + Ok(RoomMessageEventContent::text_plain(format!( + "Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database and the filesystem.", + ))) } pub(crate) async fn delete_past_remote_media( diff --git a/src/admin/room/room_moderation_commands.rs b/src/admin/room/room_moderation_commands.rs index 6b759c9b..f6d3206a 100644 --- a/src/admin/room/room_moderation_commands.rs +++ b/src/admin/room/room_moderation_commands.rs @@ -187,135 +187,137 @@ async fn ban_room( } async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: bool) -> Result { - if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { - let rooms_s = body.clone().drain(1..body.len() - 1).collect::>(); + if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" { + return Ok(RoomMessageEventContent::text_plain( + "Expected code block in command body. Add --help for details.", + )); + } - let admin_room_alias: Box = format!("#admins:{}", services().globals.server_name()) - .try_into() - .expect("#admins:server_name is a valid alias name"); + let rooms_s = body.clone().drain(1..body.len() - 1).collect::>(); - let mut room_ban_count: usize = 0; - let mut room_ids: Vec = Vec::new(); + let admin_room_alias: Box = format!("#admins:{}", services().globals.server_name()) + .try_into() + .expect("#admins:server_name is a valid alias name"); - for &room in &rooms_s { - match <&RoomOrAliasId>::try_from(room) { - Ok(room_alias_or_id) => { - if let Some(admin_room_id) = Service::get_admin_room().await? { - 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"); - continue; - } - } + let mut room_ban_count: usize = 0; + let mut room_ids: Vec = Vec::new(); - if room_alias_or_id.is_room_id() { - let room_id = match RoomId::parse(room_alias_or_id) { - Ok(room_id) => room_id, - Err(e) => { - if force { - // ignore rooms we failed to parse if we're force banning - warn!( - "Error parsing room \"{room}\" during bulk room banning, ignoring error and \ - logging here: {e}" - ); - continue; - } - - 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_ids.push(room_id); - } - - if room_alias_or_id.is_room_alias_id() { - match RoomAliasId::parse(room_alias_or_id) { - Ok(room_alias) => { - let room_id = - if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? { - room_id - } else { - debug!( - "We don't have this room alias to a room ID locally, attempting to fetch \ - room ID over federation" - ); - - match get_alias_helper(room_alias, None).await { - Ok(response) => { - debug!( - "Got federation response fetching room ID for room {room}: {:?}", - response - ); - response.room_id - }, - Err(e) => { - // don't fail if force blocking - if force { - warn!("Failed to resolve room alias {room} to a room ID: {e}"); - continue; - } - - return Ok(RoomMessageEventContent::text_plain(format!( - "Failed to resolve room alias {room} to a room ID: {e}" - ))); - }, - } - }; - - room_ids.push(room_id); - }, - Err(e) => { - if force { - // ignore rooms we failed to parse if we're force deleting - error!( - "Error parsing room \"{room}\" during bulk room banning, ignoring error and \ - logging here: {e}" - ); - continue; - } - - return Ok(RoomMessageEventContent::text_plain(format!( - "{room} is not a valid room ID or room alias, please fix the list and try again: \ - {e}" - ))); - }, - } - } - }, - Err(e) => { - if force { - // ignore rooms we failed to parse if we're force deleting - error!( - "Error parsing room \"{room}\" during bulk room banning, ignoring error and logging here: \ - {e}" - ); + for &room in &rooms_s { + match <&RoomOrAliasId>::try_from(room) { + Ok(room_alias_or_id) => { + if let Some(admin_room_id) = Service::get_admin_room().await? { + 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"); continue; } + } - return Ok(RoomMessageEventContent::text_plain(format!( - "{room} is not a valid room ID or room alias, please fix the list and try again: {e}" - ))); - }, - } + if room_alias_or_id.is_room_id() { + let room_id = match RoomId::parse(room_alias_or_id) { + Ok(room_id) => room_id, + Err(e) => { + if force { + // ignore rooms we failed to parse if we're force banning + warn!( + "Error parsing room \"{room}\" during bulk room banning, ignoring error and \ + logging here: {e}" + ); + continue; + } + + 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_ids.push(room_id); + } + + if room_alias_or_id.is_room_alias_id() { + match RoomAliasId::parse(room_alias_or_id) { + Ok(room_alias) => { + let room_id = + if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? { + room_id + } else { + debug!( + "We don't have this room alias to a room ID locally, attempting to fetch room \ + ID over federation" + ); + + match get_alias_helper(room_alias, None).await { + Ok(response) => { + debug!( + "Got federation response fetching room ID for room {room}: {:?}", + response + ); + response.room_id + }, + Err(e) => { + // don't fail if force blocking + if force { + warn!("Failed to resolve room alias {room} to a room ID: {e}"); + continue; + } + + return Ok(RoomMessageEventContent::text_plain(format!( + "Failed to resolve room alias {room} to a room ID: {e}" + ))); + }, + } + }; + + room_ids.push(room_id); + }, + Err(e) => { + if force { + // ignore rooms we failed to parse if we're force deleting + error!( + "Error parsing room \"{room}\" during bulk room banning, ignoring error and \ + logging here: {e}" + ); + continue; + } + + return Ok(RoomMessageEventContent::text_plain(format!( + "{room} is not a valid room ID or room alias, please fix the list and try again: {e}" + ))); + }, + } + } + }, + Err(e) => { + if force { + // ignore rooms we failed to parse if we're force deleting + error!( + "Error parsing room \"{room}\" during bulk room banning, ignoring error and logging here: {e}" + ); + continue; + } + + return Ok(RoomMessageEventContent::text_plain(format!( + "{room} is not a valid room ID or room alias, please fix the list and try again: {e}" + ))); + }, + } + } + + for room_id in room_ids { + if services().rooms.metadata.ban_room(&room_id, true).is_ok() { + debug!("Banned {room_id} successfully"); + room_ban_count = room_ban_count.saturating_add(1); } - for room_id in room_ids { - if services().rooms.metadata.ban_room(&room_id, true).is_ok() { - debug!("Banned {room_id} successfully"); - room_ban_count = room_ban_count.saturating_add(1); - } - - debug!("Making all users leave the room {}", &room_id); - if force { - for local_user in services() - .rooms - .state_cache - .room_members(&room_id) - .filter_map(|user| { - user.ok().filter(|local_user| { - local_user.server_name() == services().globals.server_name() + debug!("Making all users leave the room {}", &room_id); + if force { + for local_user in services() + .rooms + .state_cache + .room_members(&room_id) + .filter_map(|user| { + user.ok().filter(|local_user| { + local_user.server_name() == services().globals.server_name() // additional wrapped check here is to avoid adding remote users // who are in the admin room to the list of local users (would fail auth check) && (local_user.server_name() @@ -324,31 +326,31 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo .users .is_admin(local_user) .unwrap_or(true)) // since this is a - // force operation, - // assume user is - // an admin if - // somehow this - // fails - }) + // force operation, + // assume user is + // an admin if + // somehow this + // fails }) - .collect::>() - { - debug!( - "Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)", - &local_user, room_id - ); - if let Err(e) = leave_room(&local_user, &room_id, None).await { - warn!(%e, "Failed to leave room"); - } + }) + .collect::>() + { + debug!( + "Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)", + &local_user, room_id + ); + if let Err(e) = leave_room(&local_user, &room_id, None).await { + warn!(%e, "Failed to leave room"); } - } else { - for local_user in services() - .rooms - .state_cache - .room_members(&room_id) - .filter_map(|user| { - user.ok().filter(|local_user| { - local_user.server_name() == services().globals.server_name() + } + } else { + for local_user in services() + .rooms + .state_cache + .room_members(&room_id) + .filter_map(|user| { + user.ok().filter(|local_user| { + local_user.server_name() == services().globals.server_name() // additional wrapped check here is to avoid adding remote users // who are in the admin room to the list of local users (would fail auth check) && (local_user.server_name() @@ -357,45 +359,41 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo .users .is_admin(local_user) .unwrap_or(false)) - }) }) - .collect::>() - { - debug!("Attempting leave for user {} in room {}", &local_user, &room_id); - if let Err(e) = leave_room(&local_user, &room_id, None).await { - error!( - "Error attempting to make local user {} leave room {} during bulk room banning: {}", - &local_user, &room_id, e - ); - return Ok(RoomMessageEventContent::text_plain(format!( - "Error attempting to make local user {} leave room {} during room banning (room is still \ - banned but not removing any more users and not banning any more rooms): {}\nIf you would \ - like to ignore errors, use --force", - &local_user, &room_id, e - ))); - } + }) + .collect::>() + { + debug!("Attempting leave for user {} in room {}", &local_user, &room_id); + if let Err(e) = leave_room(&local_user, &room_id, None).await { + error!( + "Error attempting to make local user {} leave room {} during bulk room banning: {}", + &local_user, &room_id, e + ); + return Ok(RoomMessageEventContent::text_plain(format!( + "Error attempting to make local user {} leave room {} during room banning (room is still \ + banned but not removing any more users and not banning any more rooms): {}\nIf you would \ + like to ignore errors, use --force", + &local_user, &room_id, e + ))); } } - - if disable_federation { - services().rooms.metadata.disable_room(&room_id, true)?; - } } if disable_federation { - return Ok(RoomMessageEventContent::text_plain(format!( - "Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, and disabled \ - incoming federation with the room." - ))); + services().rooms.metadata.disable_room(&room_id, true)?; } - return Ok(RoomMessageEventContent::text_plain(format!( - "Finished bulk room ban, banned {room_ban_count} total rooms and evicted all users." - ))); } - Ok(RoomMessageEventContent::text_plain( - "Expected code block in command body. Add --help for details.", - )) + if disable_federation { + Ok(RoomMessageEventContent::text_plain(format!( + "Finished bulk room ban, banned {room_ban_count} total rooms, evicted all users, and disabled incoming \ + federation with the room." + ))) + } else { + Ok(RoomMessageEventContent::text_plain(format!( + "Finished bulk room ban, banned {room_ban_count} total rooms and evicted all users." + ))) + } } async fn unban_room(