use clap::App; use hbb_common::{ anyhow::{Context, Result}, log, ResultType, }; use ini::Ini; use sodiumoxide::crypto::sign; use std::{ io::prelude::*, io::Read, net::SocketAddr, time::{Instant, SystemTime}, }; #[allow(dead_code)] pub(crate) fn get_expired_time() -> Instant { let now = Instant::now(); now.checked_sub(std::time::Duration::from_secs(3600)) .unwrap_or(now) } #[allow(dead_code)] pub(crate) fn test_if_valid_server(host: &str, name: &str) -> ResultType { use std::net::ToSocketAddrs; let res = if host.contains(':') { host.to_socket_addrs()?.next().context("") } else { format!("{}:{}", host, 0) .to_socket_addrs()? .next() .context("") }; if res.is_err() { log::error!("Invalid {} {}: {:?}", name, host, res); } res } #[allow(dead_code)] pub(crate) fn get_servers(s: &str, tag: &str) -> Vec { let servers: Vec = s .split(',') .filter(|x| !x.is_empty() && test_if_valid_server(x, tag).is_ok()) .map(|x| x.to_owned()) .collect(); log::info!("{}={:?}", tag, servers); servers } #[allow(dead_code)] #[inline] fn arg_name(name: &str) -> String { name.to_uppercase().replace('_', "-") } #[allow(dead_code)] pub fn init_args(args: &str, name: &str, about: &str) { let matches = App::new(name) .version(crate::version::VERSION) .author("Purslane Ltd. ") .about(about) .args_from_usage(args) .get_matches(); if let Ok(v) = Ini::load_from_file(".env") { if let Some(section) = v.section(None::) { section .iter() .for_each(|(k, v)| std::env::set_var(arg_name(k), v)); } } if let Some(config) = matches.value_of("config") { if let Ok(v) = Ini::load_from_file(config) { if let Some(section) = v.section(None::) { section .iter() .for_each(|(k, v)| std::env::set_var(arg_name(k), v)); } } } for (k, v) in matches.args { if let Some(v) = v.vals.first() { std::env::set_var(arg_name(k), v.to_string_lossy().to_string()); } } } #[allow(dead_code)] #[inline] pub fn get_arg(name: &str) -> String { get_arg_or(name, "".to_owned()) } #[allow(dead_code)] #[inline] pub fn get_arg_or(name: &str, default: String) -> String { std::env::var(arg_name(name)).unwrap_or(default) } #[allow(dead_code)] #[inline] pub fn now() -> u64 { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|x| x.as_secs()) .unwrap_or_default() } pub fn gen_sk(wait: u64) -> (String, Option) { let sk_file = "id_ed25519"; if wait > 0 && !std::path::Path::new(sk_file).exists() { std::thread::sleep(std::time::Duration::from_millis(wait)); } if let Ok(mut file) = std::fs::File::open(sk_file) { let mut contents = String::new(); if file.read_to_string(&mut contents).is_ok() { let contents = contents.trim(); let sk = base64::decode(contents).unwrap_or_default(); if sk.len() == sign::SECRETKEYBYTES { let mut tmp = [0u8; sign::SECRETKEYBYTES]; tmp[..].copy_from_slice(&sk); let pk = base64::encode(&tmp[sign::SECRETKEYBYTES / 2..]); log::info!("Private key comes from {}", sk_file); return (pk, Some(sign::SecretKey(tmp))); } else { // don't use log here, since it is async println!("Fatal error: malformed private key in {sk_file}."); std::process::exit(1); } } } else { let gen_func = || { let (tmp, sk) = sign::gen_keypair(); (base64::encode(tmp), sk) }; let (mut pk, mut sk) = gen_func(); for _ in 0..300 { if !pk.contains('/') && !pk.contains(':') { break; } (pk, sk) = gen_func(); } let pub_file = format!("{sk_file}.pub"); if let Ok(mut f) = std::fs::File::create(&pub_file) { f.write_all(pk.as_bytes()).ok(); if let Ok(mut f) = std::fs::File::create(sk_file) { let s = base64::encode(&sk); if f.write_all(s.as_bytes()).is_ok() { log::info!("Private/public key written to {}/{}", sk_file, pub_file); log::debug!("Public key: {}", pk); return (pk, Some(sk)); } } } } ("".to_owned(), None) } #[cfg(unix)] pub async fn listen_signal() -> Result<()> { use hbb_common::tokio; use hbb_common::tokio::signal::unix::{signal, SignalKind}; tokio::spawn(async { let mut s = signal(SignalKind::terminate())?; let terminate = s.recv(); let mut s = signal(SignalKind::interrupt())?; let interrupt = s.recv(); let mut s = signal(SignalKind::quit())?; let quit = s.recv(); tokio::select! { _ = terminate => { log::info!("signal terminate"); } _ = interrupt => { log::info!("signal interrupt"); } _ = quit => { log::info!("signal quit"); } } Ok(()) }) .await? } #[cfg(not(unix))] pub async fn listen_signal() -> Result<()> { let () = std::future::pending().await; unreachable!(); }