//! Server RPC provider: `server.list`, `server.rehash`, `server.connect`, //! `server.disconnect`. use super::json::{obj, qstr}; use super::RpcError; use crate::config::Config; use crate::server::Server; pub fn handle(s: &mut Server, action: &str, params: &str) -> Result { match action { "list" => { let mut servers = vec![obj(&[ ("name", qstr(&s.name)), ("description", qstr(&s.server_desc)), ("uplink", qstr("")), ("usercount", s.users.len().to_string()), ])]; for rs in s.servers.values() { let uplink = s .servers .values() .find(|o| o.sid == rs.sid) .map(|_| s.name.clone()) .unwrap_or_default(); servers.push(obj(&[ ("name", qstr(&rs.name)), ("description", qstr(&rs.desc)), ("uplink", qstr(&uplink)), ("usercount", "0".to_string()), ])); } Ok(obj(&[("servers", format!("[{}]", servers.join(",")))])) } "rehash" => match Config::try_load(&s.conf_path) { Some(fresh) => { s.apply_config(fresh); s.announce("Server configuration reloaded via RPC."); Ok(obj(&[("result", "true".into())])) } None => Err(RpcError::internal("config file could not be read")), }, "connect" => { let name = json_name(params)?; let Some(b) = s .link_blocks .iter() .find(|b| b.name.eq_ignore_ascii_case(&name)) .cloned() else { return Err(RpcError::not_found(&format!("no link block named {name}"))); }; if s.servers .values() .any(|sv| sv.name.eq_ignore_ascii_case(&b.name)) { return Err(RpcError::invalid_params("server is already linked")); } let addr = format!("{}:{}", b.ip, b.port); let (tx, counter) = (s.event_tx.clone(), s.conn_counter.clone()); let max_line = s.conf_num("max_line", crate::socketengine::DEFAULT_MAX_LINE); std::thread::spawn(move || { crate::socketengine::connect_link(&addr, tx, counter, max_line) }); s.snotice_c('l', &format!("RPC initiated a link to {}", b.name)); Ok(obj(&[("result", "true".into())])) } "disconnect" => { let name = json_name(params)?; let via = s .servers .values() .find(|rs| rs.name.eq_ignore_ascii_case(&name)) .map(|rs| rs.via) .ok_or_else(|| RpcError::not_found("no such linked server"))?; s.close_link(via, "Disconnected via RPC"); Ok(obj(&[("result", "true".into())])) } other => Err(RpcError::method_not_found(&format!("server.{other}"))), } } fn json_name(params: &str) -> Result { super::json::get_str(params, "name") .or_else(|| super::json::get_str(params, "server")) .ok_or_else(|| RpcError::invalid_params("missing 'name'")) }