diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..717e664 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,10 @@ +max_width = 120 +hard_tabs = false +tab_spaces = 4 +edition = "2021" +reorder_imports = true +reorder_modules = true +format_strings = true +normalize_comments = true +wrap_comments = true +comment_width = 80 diff --git a/src/commands/args.rs b/src/commands/args.rs index 9a04a15..61755ed 100644 --- a/src/commands/args.rs +++ b/src/commands/args.rs @@ -80,11 +80,7 @@ mod tests { fn parses_double_quoted_argument() { assert_eq!( split_command_line(r#"/cmd arg1 "arg2 value""#), - vec![ - "/cmd".to_string(), - "arg1".to_string(), - "arg2 value".to_string() - ] + vec!["/cmd".to_string(), "arg1".to_string(), "arg2 value".to_string()] ); } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 39558f5..5049c27 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -41,10 +41,7 @@ pub async fn global_usage(ctx: Arc>) -> String { usage += "\nCommands from plugins:\n"; for cmd in ctx_lock.plugin_commands.values() { - usage += &format!( - "{} (from {}) - {}\n", - &cmd.usage, &cmd.plugin_id, &cmd.description - ); + usage += &format!("{} (from {}) - {}\n", &cmd.usage, &cmd.plugin_id, &cmd.description); } usage += "\nDeltaChat Remote Control Bot by slavasil, 2026\n"; @@ -65,12 +62,7 @@ pub async fn auth_command( let dchat_ctx_lock = dchat_ctx.lock().await; if !AUTH_REQUIRED { - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - "Authentication is disabled".to_owned(), - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, "Authentication is disabled".to_owned()).await?; return Ok(()); } @@ -80,23 +72,13 @@ pub async fn auth_command( } let Some(password) = args.first() else { - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - "Usage: /auth ".to_owned(), - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, "Usage: /auth ".to_owned()).await?; return Ok(()); }; if *password == ctx_lock.config.auth.password { ctx_lock.authed_contacts.insert(contact_id); - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - "Authentication successful!".to_owned(), - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, "Authentication successful!".to_owned()).await?; } else { chat::send_text_msg(&dchat_ctx_lock, chat_id, "Incorrect password".to_owned()).await?; } @@ -201,12 +183,7 @@ async fn send_magic_packet(mac: MacAddress) -> AnyhowResult<()> { fn ssh_unlock_disk_usage(config: &BotConfig) -> String { if !config.machines.is_empty() { - let list = config - .machines - .keys() - .cloned() - .collect::>() - .join("|"); + let list = config.machines.keys().cloned().collect::>().join("|"); if config.default_machine().is_some() { format!("Usage: /ssh-unlock-disk [{list}] ") } else { @@ -235,12 +212,7 @@ pub async fn ssh_unlock_disk_command( if args.is_empty() || args.len() > 2 { log::warn!("wrong number of args"); - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - ssh_unlock_disk_usage(&ctx_lock.config), - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, ssh_unlock_disk_usage(&ctx_lock.config)).await?; return Ok(()); } @@ -255,12 +227,7 @@ pub async fn ssh_unlock_disk_command( None } }) else { - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - ssh_unlock_disk_usage(&ctx_lock.config), - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, ssh_unlock_disk_usage(&ctx_lock.config)).await?; return Ok(()); }; @@ -284,8 +251,7 @@ pub async fn ssh_unlock_disk_command( let password = args[args.len() - 1].to_owned(); let host = machine.static_ip.to_string(); - let ssh_key = - russh::keys::PrivateKey::from_openssh(ssh_key).context("Invalid remote unlock SSH key")?; + let ssh_key = russh::keys::PrivateKey::from_openssh(ssh_key).context("Invalid remote unlock SSH key")?; drop(ctx_lock); drop(dchat_ctx_lock); @@ -311,10 +277,7 @@ pub async fn ssh_unlock_disk_command( SSH_TIMEOUT, connection.authenticate_publickey( "root", - PrivateKeyWithHashAlg::new( - Arc::new(ssh_key), - connection.best_supported_rsa_hash().await?.flatten(), - ), + PrivateKeyWithHashAlg::new(Arc::new(ssh_key), connection.best_supported_rsa_hash().await?.flatten()), ), ) .await @@ -350,10 +313,7 @@ pub async fn ssh_unlock_disk_command( .context("Cannot send password via SSH")?; loop { - let Some(msg) = timeout(SSH_TIMEOUT, channel.wait()) - .await - .context("Timed out")? - else { + let Some(msg) = timeout(SSH_TIMEOUT, channel.wait()).await.context("Timed out")? else { break; }; log::info!("Received message from SSH server: {:?}", msg); @@ -372,12 +332,7 @@ pub async fn ssh_unlock_disk_command( fn ssh_exec_usage(config: &BotConfig) -> String { if !config.machines.is_empty() { - let list = config - .machines - .keys() - .cloned() - .collect::>() - .join("|"); + let list = config.machines.keys().cloned().collect::>().join("|"); format!("Usage: /ssh-exec @({list}||) ") } else { "Command /ssh-exec unavailable: no machines configured".to_string() @@ -499,24 +454,16 @@ pub async fn ssh_exec_command( russh::ChannelMsg::Data { data } => { let dchat_ctx_lock = dchat_ctx.lock().await; report_text += &String::from_utf8_lossy(&data); - chat::send_edit_request( - &dchat_ctx_lock, - report_message_id, - report_text.clone(), - ) - .await - .context("Cannot edit message")?; + chat::send_edit_request(&dchat_ctx_lock, report_message_id, report_text.clone()) + .await + .context("Cannot edit message")?; } russh::ChannelMsg::ExtendedData { data, ext: 1 } => { let dchat_ctx_lock = dchat_ctx.lock().await; report_text += &String::from_utf8_lossy(&data); - chat::send_edit_request( - &dchat_ctx_lock, - report_message_id, - report_text.clone(), - ) - .await - .context("Cannot edit message")?; + chat::send_edit_request(&dchat_ctx_lock, report_message_id, report_text.clone()) + .await + .context("Cannot edit message")?; } _ => { log::info!("Message from SSH channel: {msg:?}"); @@ -531,13 +478,9 @@ pub async fn ssh_exec_command( let dchat_ctx_lock = dchat_ctx.lock().await; report_text += "\n\nRead timed out"; - chat::send_edit_request( - &dchat_ctx_lock, - report_message_id, - report_text.clone(), - ) - .await - .context("Cannot edit message")?; + chat::send_edit_request(&dchat_ctx_lock, report_message_id, report_text.clone()) + .await + .context("Cannot edit message")?; break; } diff --git a/src/main.rs b/src/main.rs index f232453..62e5236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,10 +7,7 @@ mod ssh; mod proto { pub(crate) mod deltachat_remotecontrol_bot { pub(crate) mod plugin { - include!(concat!( - env!("OUT_DIR"), - "/deltachat_remotecontrol_bot.plugin.rs" - )); + include!(concat!(env!("OUT_DIR"), "/deltachat_remotecontrol_bot.plugin.rs")); } } pub(crate) use deltachat_remotecontrol_bot::plugin::*; @@ -82,14 +79,9 @@ async fn run_bot(bot_context: Arc>) -> AnyhowResult<()> { let dchat_db_filename = dchat_db_dir.join("deltachat.sqlite"); - let dchat_ctx = Context::new( - &dchat_db_filename, - 0, - Events::default(), - StockStrings::default(), - ) - .await - .context("Failed to open Delta Chat client DB")?; + let dchat_ctx = Context::new(&dchat_db_filename, 0, Events::default(), StockStrings::default()) + .await + .context("Failed to open Delta Chat client DB")?; let ctx_lock = bot_context.lock().await; @@ -114,9 +106,7 @@ async fn run_bot(bot_context: Arc>) -> AnyhowResult<()> { avatar_path = data_path().join(&avatar_path); } if let Some(avatar_str) = avatar_path.to_str() { - dchat_ctx - .set_config(Config::Selfavatar, Some(avatar_str)) - .await?; + dchat_ctx.set_config(Config::Selfavatar, Some(avatar_str)).await?; } } } @@ -156,12 +146,7 @@ async fn send_command_not_found( chat_id: ChatId, ) -> AnyhowResult<()> { let dchat_ctx_lock = dchat_ctx.lock().await; - chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - commands::global_usage(bot_ctx).await, - ) - .await?; + chat::send_text_msg(&dchat_ctx_lock, chat_id, commands::global_usage(bot_ctx).await).await?; Ok(()) } @@ -208,9 +193,7 @@ async fn handle_message( }; let Some(plugin) = ctx_lock.plugins.get(&cmd.plugin_id) else { - bail!( - "Inconsistency in plugin_cmd_aliases hashmap: command references a plugin that is not loaded" - ); + bail!("Inconsistency in plugin_cmd_aliases hashmap: command references a plugin that is not loaded"); }; let (plugin_conn, plugin_name) = { let lock = plugin.lock().await; @@ -218,11 +201,7 @@ async fn handle_message( bail!("Plugin disconnected"); }; let plugin_name = lock.plugin_id.clone(); - log::info!( - "Delegating command /{} to plugin {}", - &cmd.name, - &plugin_name - ); + log::info!("Delegating command /{} to plugin {}", &cmd.name, &plugin_name); (conn, plugin_name) }; let contact_id = msg.get_from_id(); @@ -239,11 +218,7 @@ async fn handle_message( tokio::spawn(async move { log::debug!("Started plugin command task"); match plugin_conn - .execute_command( - cmd_name.clone(), - issuer_id, - commands::args::split_command_line(&text), - ) + .execute_command(cmd_name.clone(), issuer_id, commands::args::split_command_line(&text)) .await { Ok(mut replies) => { @@ -259,21 +234,15 @@ async fn handle_message( ); if reply.edit { if let Some(msg_id) = last_message_id { - if let Some(proto::command_reply::Reply::Text(text)) = - reply.reply - { + if let Some(proto::command_reply::Reply::Text(text)) = reply.reply { let dchat_ctx_lock = dchat_ctx.lock().await; log::debug!( "Command {} :: /{}: editing last message", &plugin_name, &cmd_name ); - if let Err(e) = chat::send_edit_request( - &dchat_ctx_lock, - msg_id, - text, - ) - .await + if let Err(e) = + chat::send_edit_request(&dchat_ctx_lock, msg_id, text).await { log::error!("Cannot edit message: {e}"); continue; @@ -285,40 +254,22 @@ async fn handle_message( &plugin_name ); } - } else { - if let Some(proto::command_reply::Reply::Text(text)) = - reply.reply + } else if let Some(proto::command_reply::Reply::Text(text)) = reply.reply { + let dchat_ctx_lock = dchat_ctx.lock().await; + log::debug!("Command {} :: /{}: sending message", &plugin_name, &cmd_name); + let msg_id = match chat::send_text_msg(&dchat_ctx_lock, chat_id, text).await { - let dchat_ctx_lock = dchat_ctx.lock().await; - log::debug!( - "Command {} :: /{}: sending message", - &plugin_name, - &cmd_name - ); - let msg_id = match chat::send_text_msg( - &dchat_ctx_lock, - chat_id, - text, - ) - .await - { - Ok(msg_id) => msg_id, - Err(e) => { - log::error!("Cannot send new message: {e}"); - continue; - } - }; - last_message_id = Some(msg_id); - } + Ok(msg_id) => msg_id, + Err(e) => { + log::error!("Cannot send new message: {e}"); + continue; + } + }; + last_message_id = Some(msg_id); } } Err(e) => { - log::error!( - "Error while executing {} :: /{}: {}", - &plugin_name, - &cmd_name, - e - ); + log::error!("Error while executing {} :: /{}: {}", &plugin_name, &cmd_name, e); break; } } @@ -374,12 +325,7 @@ async fn main() { } }; - let requested_plugins: Vec = config - .plugins - .clone() - .into_iter() - .filter(|p| p.enabled) - .collect(); + let requested_plugins: Vec = config.plugins.clone().into_iter().filter(|p| p.enabled).collect(); let bot_context = Arc::new(Mutex::new(BotContext::new(config))); diff --git a/src/paths.rs b/src/paths.rs index 556b186..5ee3091 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -16,11 +16,7 @@ pub(crate) fn default_config_paths() -> Vec { paths.push(config_home.join(APP_CONFIG_DIR).join(CONFIG_FILENAME)); } - paths.push( - PathBuf::from("/etc") - .join(APP_CONFIG_DIR) - .join(CONFIG_FILENAME), - ); + paths.push(PathBuf::from("/etc").join(APP_CONFIG_DIR).join(CONFIG_FILENAME)); paths } @@ -28,14 +24,7 @@ pub(crate) fn default_config_paths() -> Vec { pub(crate) fn data_path() -> PathBuf { std::env::var("XDG_DATA_HOME") .map(|dh| PathBuf::from(dh).join(APP_DATA_DIR)) - .or_else(|_| { - std::env::var("HOME").map(|h| { - PathBuf::from(h) - .join(".local") - .join("share") - .join(APP_DATA_DIR) - }) - }) + .or_else(|_| std::env::var("HOME").map(|h| PathBuf::from(h).join(".local").join("share").join(APP_DATA_DIR))) .unwrap_or(PathBuf::from(".")) } diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 31b5f4e..574bb93 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -58,24 +58,17 @@ impl Display for PluginRequestType { #[async_trait] pub(crate) trait PluginConnection: Send + Sync + Debug { - async fn initialize_plugin( - &self, - config: String, - ) -> Result; + async fn initialize_plugin(&self, config: String) + -> Result; - async fn request_plugin_command_list( - &self, - ) -> Result; + async fn request_plugin_command_list(&self) -> Result; async fn execute_command( self: Arc, command_id: String, issuer_id: String, argv: Vec, - ) -> Result< - mpsc::Receiver>, - PluginConnectionError, - >; + ) -> Result>, PluginConnectionError>; } #[derive(Debug)] @@ -138,8 +131,7 @@ pub(crate) async fn try_load_plugin( cmd.env("DCRCBOT_PLUGIN_TRANSPORT", "stdio"); let plugin_process = cmd.spawn().context("Failed to start the plugin")?; - let plugin = - stdio::initialize_stdio_plugin(plugin_process, unique_name.clone(), config_json).await?; + let plugin = stdio::initialize_stdio_plugin(plugin_process, unique_name.clone(), config_json).await?; let mut ctx_lock = ctx.lock().await; for cmd in plugin.lock().await.commands.iter().cloned() { @@ -147,12 +139,8 @@ pub(crate) async fn try_load_plugin( if ctx_lock.plugin_commands.contains_key(&cmd.name) { bail!("duplicate command specification"); } - ctx_lock - .plugin_commands - .insert(cmd.name.clone(), Arc::clone(&cmd)); - ctx_lock - .plugin_cmd_aliases - .insert(cmd.name.clone(), Arc::clone(&cmd)); + ctx_lock.plugin_commands.insert(cmd.name.clone(), Arc::clone(&cmd)); + ctx_lock.plugin_cmd_aliases.insert(cmd.name.clone(), Arc::clone(&cmd)); for alias in cmd.aliases.iter() { log::debug!( "adding command alias /{} -> /{} of plugin {}", @@ -160,9 +148,7 @@ pub(crate) async fn try_load_plugin( &cmd.name, &unique_name ); - ctx_lock - .plugin_cmd_aliases - .insert(alias.to_owned(), Arc::clone(&cmd)); + ctx_lock.plugin_cmd_aliases.insert(alias.to_owned(), Arc::clone(&cmd)); } } diff --git a/src/plugin/stdio.rs b/src/plugin/stdio.rs index a24f3e8..c781307 100644 --- a/src/plugin/stdio.rs +++ b/src/plugin/stdio.rs @@ -10,9 +10,7 @@ use tokio::{ }; use crate::{ - plugin::{ - LoadedPlugin, PluginCommand, PluginConnection, PluginConnectionError, PluginRequestType, - }, + plugin::{LoadedPlugin, PluginCommand, PluginConnection, PluginConnectionError, PluginRequestType}, proto, }; @@ -85,19 +83,16 @@ impl StdioPluginConnection { async fn stdout_reader_loop(self: Arc) { loop { - let frame = - match Self::read_length_delimited(self.buffered_stdout.lock().await.deref_mut()) - .await - { - Ok(frame) => frame, - Err(e) => { - log::error!( - "Error while reading STDOUT of stdio plugin {}: {e}", - &self.plugin.lock().await.plugin_id - ); - break; - } - }; + let frame = match Self::read_length_delimited(self.buffered_stdout.lock().await.deref_mut()).await { + Ok(frame) => frame, + Err(e) => { + log::error!( + "Error while reading STDOUT of stdio plugin {}: {e}", + &self.plugin.lock().await.plugin_id + ); + break; + } + }; let response = match proto::Response::decode(frame.as_slice()) { Ok(response) => response, Err(_) => { @@ -133,9 +128,7 @@ impl StdioPluginConnection { self.pending_requests.lock().await.clear(); } - async fn read_length_delimited( - reader: &mut R, - ) -> Result, Box> + async fn read_length_delimited(reader: &mut R) -> Result, Box> where R: AsyncRead + Unpin, { diff --git a/src/ssh.rs b/src/ssh.rs index 459007e..1bc37ab 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -36,11 +36,7 @@ pub(crate) async fn connect_ssh, P: Int "Connecting to SSH {:?} {} key & {} password", &addr, if key.is_some() { "with" } else { "without" }, - if password.is_some() { - "with" - } else { - "without" - } + if password.is_some() { "with" } else { "without" } ); let mut connection = tokio::time::timeout( connect_timeout, @@ -65,12 +61,7 @@ pub(crate) async fn connect_ssh, P: Int if !auth_success { if let Some(key) = key { log::info!("Trying to authenticate using key"); - match tokio::time::timeout( - auth_timeout, - connection.authenticate_publickey(username, key), - ) - .await - { + match tokio::time::timeout(auth_timeout, connection.authenticate_publickey(username, key)).await { Ok(Ok(AuthResult::Success)) => { auth_success = true; } @@ -78,8 +69,7 @@ pub(crate) async fn connect_ssh, P: Int remaining_methods, partial_success, })) => { - if partial_success || !remaining_methods.contains(&russh::MethodKind::Password) - { + if partial_success || !remaining_methods.contains(&russh::MethodKind::Password) { if partial_success { anyhow::bail!("SSH auth failed: multi-factor auth is not supported"); } else { @@ -102,12 +92,7 @@ pub(crate) async fn connect_ssh, P: Int if !auth_success { if let Some(password) = password { log::info!("Trying to authenticate using password"); - match tokio::time::timeout( - auth_timeout, - connection.authenticate_password(username, password), - ) - .await - { + match tokio::time::timeout(auth_timeout, connection.authenticate_password(username, password)).await { Ok(Ok(AuthResult::Success)) => { auth_success = true; }