#[cfg(any(target_os = "android", target_os = "ios"))] use hbb_common::password_security; use hbb_common::{ allow_err, bytes::Bytes, config::{self, Config, LocalConfig, PeerConfig}, config::{CONNECT_TIMEOUT, RENDEZVOUS_PORT}, directories_next, futures::future::join_all, log, rendezvous_proto::*, tokio, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{ sleep, tokio::{sync::mpsc, time}, }; use serde_derive::Serialize; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::process::Child; use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use crate::common::SOFTWARE_UPDATE_URL; #[cfg(feature = "flutter")] use crate::hbbs_http::account; #[cfg(not(any(target_os = "ios")))] use crate::ipc; type Message = RendezvousMessage; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub type Children = Arc)>>; #[derive(Clone, Debug, Serialize)] pub struct UiStatus { pub status_num: i32, #[cfg(not(feature = "flutter"))] pub key_confirmed: bool, #[cfg(not(any(target_os = "android", target_os = "ios")))] pub mouse_time: i64, #[cfg(not(feature = "flutter"))] pub id: String, } #[derive(Debug, Clone, Serialize)] pub struct LoginDeviceInfo { pub os: String, pub r#type: String, pub name: String, } lazy_static::lazy_static! { static ref UI_STATUS : Arc> = Arc::new(Mutex::new(UiStatus{ status_num: 0, #[cfg(not(feature = "flutter"))] key_confirmed: false, #[cfg(not(any(target_os = "android", target_os = "ios")))] mouse_time: 0, #[cfg(not(feature = "flutter"))] id: "".to_owned(), })); static ref ASYNC_JOB_STATUS : Arc> = Default::default(); static ref TEMPORARY_PASSWD : Arc> = Arc::new(Mutex::new("".to_owned())); } #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref OPTION_SYNCED: Arc> = Default::default(); static ref OPTIONS : Arc>> = Arc::new(Mutex::new(Config::get_options())); pub static ref SENDER : Mutex> = Mutex::new(check_connect_status(true)); static ref CHILDREN : Children = Default::default(); } const INIT_ASYNC_JOB_STATUS: &str = " "; #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn get_id() -> String { #[cfg(any(target_os = "android", target_os = "ios"))] return Config::get_id(); #[cfg(not(any(target_os = "android", target_os = "ios")))] return ipc::get_id(); } #[inline] pub fn goto_install() { allow_err!(crate::run_me(vec!["--install"])); std::process::exit(0); } #[inline] pub fn install_me(_options: String, _path: String, _silent: bool, _debug: bool) { #[cfg(windows)] std::thread::spawn(move || { allow_err!(crate::platform::windows::install_me( &_options, _path, _silent, _debug )); std::process::exit(0); }); } #[inline] pub fn update_me(_path: String) { goto_install(); } #[inline] pub fn run_without_install() { crate::run_me(vec!["--noinstall"]).ok(); std::process::exit(0); } #[inline] pub fn show_run_without_install() -> bool { let mut it = std::env::args(); if let Some(tmp) = it.next() { if crate::is_setup(&tmp) { return it.next() == None; } } false } #[inline] pub fn get_license() -> String { #[cfg(windows)] if let Ok(lic) = crate::platform::windows::get_license_from_exe_name() { #[cfg(feature = "flutter")] return format!("Key: {}\nHost: {}\nApi: {}", lic.key, lic.host, lic.api); // default license format is html formed (sciter) #[cfg(not(feature = "flutter"))] return format!( "
Key: {}
Host: {} Api: {}", lic.key, lic.host, lic.api ); } Default::default() } #[inline] pub fn refresh_options() { #[cfg(not(any(target_os = "android", target_os = "ios")))] { *OPTIONS.lock().unwrap() = Config::get_options(); } } #[inline] pub fn get_option>(key: T) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] { let map = OPTIONS.lock().unwrap(); if let Some(v) = map.get(key.as_ref()) { v.to_owned() } else { "".to_owned() } } #[cfg(any(target_os = "android", target_os = "ios"))] { Config::get_option(key.as_ref()) } } #[inline] pub fn get_local_option(key: String) -> String { LocalConfig::get_option(&key) } #[inline] pub fn set_local_option(key: String, value: String) { LocalConfig::set_option(key, value); } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn get_local_flutter_option(key: String) -> String { LocalConfig::get_flutter_option(&key) } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn set_local_flutter_option(key: String, value: String) { LocalConfig::set_flutter_option(key, value); } #[cfg(feature = "flutter")] #[inline] pub fn get_kb_layout_type() -> String { LocalConfig::get_kb_layout_type() } #[cfg(feature = "flutter")] #[inline] pub fn set_kb_layout_type(kb_layout_type: String) { LocalConfig::set_kb_layout_type(kb_layout_type); } #[inline] pub fn peer_has_password(id: String) -> bool { !PeerConfig::load(&id).password.is_empty() } #[inline] pub fn forget_password(id: String) { let mut c = PeerConfig::load(&id); c.password.clear(); c.store(&id); } #[inline] pub fn get_peer_option(id: String, name: String) -> String { let c = PeerConfig::load(&id); c.options.get(&name).unwrap_or(&"".to_owned()).to_owned() } #[inline] #[cfg(feature = "flutter")] pub fn get_peer_flutter_option(id: String, name: String) -> String { let c = PeerConfig::load(&id); c.ui_flutter.get(&name).unwrap_or(&"".to_owned()).to_owned() } #[inline] #[cfg(feature = "flutter")] pub fn set_peer_flutter_option(id: String, name: String, value: String) { let mut c = PeerConfig::load(&id); if value.is_empty() { c.ui_flutter.remove(&name); } else { c.ui_flutter.insert(name, value); } c.store(&id); } #[inline] pub fn set_peer_option(id: String, name: String, value: String) { let mut c = PeerConfig::load(&id); if value.is_empty() { c.options.remove(&name); } else { c.options.insert(name, value); } c.store(&id); } #[inline] pub fn using_public_server() -> bool { option_env!("RENDEZVOUS_SERVER").unwrap_or("").is_empty() && crate::get_custom_rendezvous_server(get_option("custom-rendezvous-server")).is_empty() } #[inline] pub fn get_options() -> String { let options = { #[cfg(not(any(target_os = "android", target_os = "ios")))] { OPTIONS.lock().unwrap() } #[cfg(any(target_os = "android", target_os = "ios"))] { Config::get_options() } }; let mut m = serde_json::Map::new(); for (k, v) in options.iter() { m.insert(k.into(), v.to_owned().into()); } serde_json::to_string(&m).unwrap_or_default() } #[inline] pub fn test_if_valid_server(host: String) -> String { hbb_common::socket_client::test_if_valid_server(&host) } #[inline] #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_sound_inputs() -> Vec { let mut a = Vec::new(); #[cfg(not(target_os = "linux"))] { fn get_sound_inputs_() -> Vec { let mut out = Vec::new(); use cpal::traits::{DeviceTrait, HostTrait}; let host = cpal::default_host(); if let Ok(devices) = host.devices() { for device in devices { if device.default_input_config().is_err() { continue; } if let Ok(name) = device.name() { out.push(name); } } } out } let inputs = Arc::new(Mutex::new(Vec::new())); let cloned = inputs.clone(); // can not call below in UI thread, because conflict with sciter sound com initialization std::thread::spawn(move || *cloned.lock().unwrap() = get_sound_inputs_()) .join() .ok(); for name in inputs.lock().unwrap().drain(..) { a.push(name); } } #[cfg(target_os = "linux")] { let inputs: Vec = crate::platform::linux::get_pa_sources() .drain(..) .map(|x| x.1) .collect(); for name in inputs { a.push(name); } } a } #[inline] pub fn set_options(m: HashMap) { #[cfg(not(any(target_os = "android", target_os = "ios")))] { *OPTIONS.lock().unwrap() = m.clone(); ipc::set_options(m).ok(); } #[cfg(any(target_os = "android", target_os = "ios"))] Config::set_options(m); } #[inline] pub fn set_option(key: String, value: String) { if &key == "stop-service" { #[cfg(target_os = "macos")] { let is_stop = value == "Y"; if is_stop && crate::platform::macos::uninstall_service(true) { return; } } #[cfg(any(target_os = "windows", target_os = "linux"))] { if crate::platform::is_installed() { if value == "Y" { if crate::platform::uninstall_service(true) { return; } } else { if crate::platform::install_service() { return; } } return; } } } #[cfg(not(any(target_os = "android", target_os = "ios")))] { let mut options = OPTIONS.lock().unwrap(); if value.is_empty() { options.remove(&key); } else { options.insert(key.clone(), value.clone()); } ipc::set_options(options.clone()).ok(); } #[cfg(any(target_os = "android", target_os = "ios"))] Config::set_option(key, value); } #[inline] pub fn install_path() -> String { #[cfg(windows)] return crate::platform::windows::get_install_info().1; #[cfg(not(windows))] return "".to_owned(); } #[inline] pub fn get_socks() -> Vec { #[cfg(any(target_os = "android", target_os = "ios"))] return Vec::new(); #[cfg(not(any(target_os = "android", target_os = "ios")))] { let s = ipc::get_socks(); match s { None => Vec::new(), Some(s) => { let mut v = Vec::new(); v.push(s.proxy); v.push(s.username); v.push(s.password); v } } } } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn set_socks(proxy: String, username: String, password: String) { ipc::set_socks(config::Socks5Server { proxy, username, password, }) .ok(); } #[cfg(any(target_os = "android", target_os = "ios"))] pub fn set_socks(_: String, _: String, _: String) {} #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_installed() -> bool { crate::platform::is_installed() } #[cfg(any(target_os = "android", target_os = "ios"))] #[inline] pub fn is_installed() -> bool { false } #[inline] pub fn is_rdp_service_open() -> bool { #[cfg(windows)] return is_installed() && crate::platform::windows::is_rdp_service_open(); #[cfg(not(windows))] return false; } #[inline] pub fn is_share_rdp() -> bool { #[cfg(windows)] return crate::platform::windows::is_share_rdp(); #[cfg(not(windows))] return false; } #[inline] pub fn set_share_rdp(_enable: bool) { #[cfg(windows)] crate::platform::windows::set_share_rdp(_enable); } #[inline] pub fn is_installed_lower_version() -> bool { #[cfg(not(windows))] return false; #[cfg(windows)] { let b = crate::platform::windows::get_reg("BuildDate"); return crate::BUILD_DATE.cmp(&b).is_gt(); } } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_mouse_time() -> f64 { UI_STATUS.lock().unwrap().mouse_time as f64 } #[inline] pub fn check_mouse_time() { #[cfg(not(any(target_os = "android", target_os = "ios")))] { let sender = SENDER.lock().unwrap(); allow_err!(sender.send(ipc::Data::MouseMoveTime(0))); } } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_connect_status() -> UiStatus { UI_STATUS.lock().unwrap().clone() } #[inline] pub fn temporary_password() -> String { #[cfg(any(target_os = "android", target_os = "ios"))] return password_security::temporary_password(); #[cfg(not(any(target_os = "android", target_os = "ios")))] return TEMPORARY_PASSWD.lock().unwrap().clone(); } #[inline] pub fn update_temporary_password() { #[cfg(any(target_os = "android", target_os = "ios"))] password_security::update_temporary_password(); #[cfg(not(any(target_os = "android", target_os = "ios")))] allow_err!(ipc::update_temporary_password()); } #[inline] pub fn permanent_password() -> String { #[cfg(any(target_os = "android", target_os = "ios"))] return Config::get_permanent_password(); #[cfg(not(any(target_os = "android", target_os = "ios")))] return ipc::get_permanent_password(); } #[inline] pub fn set_permanent_password(password: String) { #[cfg(any(target_os = "android", target_os = "ios"))] Config::set_permanent_password(&password); #[cfg(not(any(target_os = "android", target_os = "ios")))] allow_err!(ipc::set_permanent_password(password)); } #[inline] pub fn get_peer(id: String) -> PeerConfig { PeerConfig::load(&id) } #[inline] pub fn get_fav() -> Vec { LocalConfig::get_fav() } #[inline] pub fn store_fav(fav: Vec) { LocalConfig::set_fav(fav); } #[inline] pub fn is_process_trusted(_prompt: bool) -> bool { #[cfg(target_os = "macos")] return crate::platform::macos::is_process_trusted(_prompt); #[cfg(not(target_os = "macos"))] return true; } #[inline] pub fn is_can_screen_recording(_prompt: bool) -> bool { #[cfg(target_os = "macos")] return crate::platform::macos::is_can_screen_recording(_prompt); #[cfg(not(target_os = "macos"))] return true; } #[inline] pub fn is_installed_daemon(_prompt: bool) -> bool { #[cfg(target_os = "macos")] return crate::platform::macos::is_installed_daemon(_prompt); #[cfg(not(target_os = "macos"))] return true; } #[inline] #[cfg(feature = "flutter")] pub fn is_can_input_monitoring(_prompt: bool) -> bool { #[cfg(target_os = "macos")] return crate::platform::macos::is_can_input_monitoring(_prompt); #[cfg(not(target_os = "macos"))] return true; } #[inline] pub fn get_error() -> String { #[cfg(not(any(feature = "cli")))] #[cfg(target_os = "linux")] { let dtype = crate::platform::linux::get_display_server(); if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype { return crate::server::wayland::common_get_error(); } if dtype != crate::platform::linux::DISPLAY_SERVER_X11 { return format!( "{} {}, {}", crate::client::translate("Unsupported display server".to_owned()), dtype, crate::client::translate("x11 expected".to_owned()), ); } } return "".to_owned(); } #[inline] pub fn is_login_wayland() -> bool { #[cfg(target_os = "linux")] return crate::platform::linux::is_login_wayland(); #[cfg(not(target_os = "linux"))] return false; } #[inline] pub fn current_is_wayland() -> bool { #[cfg(target_os = "linux")] return crate::platform::linux::current_is_wayland(); #[cfg(not(target_os = "linux"))] return false; } #[inline] pub fn get_new_version() -> String { (*SOFTWARE_UPDATE_URL .lock() .unwrap() .rsplit('/') .next() .unwrap_or("")) .to_string() } #[inline] pub fn get_version() -> String { crate::VERSION.to_owned() } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn get_app_name() -> String { crate::get_app_name() } #[cfg(windows)] #[inline] pub fn create_shortcut(_id: String) { crate::platform::windows::create_shortcut(&_id).ok(); } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn discover() { #[cfg(not(any(target_os = "ios")))] std::thread::spawn(move || { allow_err!(crate::lan::discover()); }); } #[cfg(feature = "flutter")] pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> { use hbb_common::sodiumoxide::base64; HashMap::<&str, String>::from_iter([ ("id", id), ("username", p.info.username.clone()), ("hostname", p.info.hostname.clone()), ("platform", p.info.platform.clone()), ( "alias", p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(), ), ( "hash", base64::encode(p.password, base64::Variant::Original), ), ]) } #[cfg(feature = "flutter")] pub fn peer_exists(id: &str) -> bool { PeerConfig::exists(id) } #[inline] pub fn get_lan_peers() -> Vec> { config::LanPeers::load() .peers .iter() .map(|peer| { HashMap::<&str, String>::from_iter([ ("id", peer.id.clone()), ("username", peer.username.clone()), ("hostname", peer.hostname.clone()), ("platform", peer.platform.clone()), ]) }) .collect() } #[inline] pub fn remove_discovered(id: String) { let mut peers = config::LanPeers::load().peers; peers.retain(|x| x.id != id); config::LanPeers::store(&peers); } #[inline] pub fn get_uuid() -> String { crate::encode64(hbb_common::get_uuid()) } #[inline] pub fn get_init_async_job_status() -> String { INIT_ASYNC_JOB_STATUS.to_string() } #[inline] pub fn reset_async_job_status() { *ASYNC_JOB_STATUS.lock().unwrap() = get_init_async_job_status(); } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn change_id(id: String) { reset_async_job_status(); let old_id = get_id(); std::thread::spawn(move || { change_id_shared(id, old_id); }); } #[inline] pub fn post_request(url: String, body: String, header: String) { *ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned(); std::thread::spawn(move || { *ASYNC_JOB_STATUS.lock().unwrap() = match crate::post_request_sync(url, body, &header) { Err(err) => err.to_string(), Ok(text) => text, }; }); } #[inline] pub fn get_async_job_status() -> String { ASYNC_JOB_STATUS.lock().unwrap().clone() } #[inline] pub fn get_langs() -> String { use serde_json::json; let mut x: Vec<(&str, String)> = crate::lang::LANGS .iter() .map(|a| (a.0, format!("{} ({})", a.1, a.0))) .collect(); x.sort_by(|a, b| a.0.cmp(b.0)); json!(x).to_string() } #[inline] pub fn default_video_save_directory() -> String { let appname = crate::get_app_name(); // ui process can show it correctly Once vidoe process created it. let try_create = |path: &std::path::Path| { if !path.exists() { std::fs::create_dir_all(path).ok(); } if path.exists() { path.to_string_lossy().to_string() } else { "".to_string() } }; #[cfg(any(target_os = "android", target_os = "ios"))] if let Ok(home) = config::APP_HOME_DIR.read() { let mut path = home.to_owned(); path.push_str("/RustDesk/ScreenRecord"); let dir = try_create(&std::path::Path::new(&path)); if !dir.is_empty() { return dir; } } if let Some(user) = directories_next::UserDirs::new() { if let Some(video_dir) = user.video_dir() { let dir = try_create(&video_dir.join(&appname)); if !dir.is_empty() { return dir; } if video_dir.exists() { return video_dir.to_string_lossy().to_string(); } } if let Some(desktop_dir) = user.desktop_dir() { if desktop_dir.exists() { return desktop_dir.to_string_lossy().to_string(); } } let home = user.home_dir(); if home.exists() { return home.to_string_lossy().to_string(); } } // same order as above #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(home) = crate::platform::get_active_user_home() { let name = if cfg!(target_os = "macos") { "Movies" } else { "Videos" }; let video_dir = home.join(name); let dir = try_create(&video_dir.join(&appname)); if !dir.is_empty() { return dir; } if video_dir.exists() { return video_dir.to_string_lossy().to_string(); } let desktop_dir = home.join("Desktop"); if desktop_dir.exists() { return desktop_dir.to_string_lossy().to_string(); } if home.exists() { return home.to_string_lossy().to_string(); } } if let Ok(exe) = std::env::current_exe() { if let Some(parent) = exe.parent() { let dir = try_create(&parent.join("videos")); if !dir.is_empty() { return dir; } // basically exist return parent.to_string_lossy().to_string(); } } "".to_owned() } #[inline] pub fn get_api_server() -> String { crate::get_api_server( get_option("api-server"), get_option("custom-rendezvous-server"), ) } #[inline] pub fn has_hwcodec() -> bool { #[cfg(not(any(feature = "hwcodec", feature = "mediacodec")))] return false; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] return true; } #[inline] pub fn has_gpucodec() -> bool { cfg!(feature = "gpucodec") } #[cfg(feature = "flutter")] #[inline] pub fn supported_hwdecodings() -> (bool, bool) { let decoding = scrap::codec::Decoder::supported_decodings(None, true, None); #[allow(unused_mut)] let (mut h264, mut h265) = (decoding.ability_h264 > 0, decoding.ability_h265 > 0); #[cfg(feature = "gpucodec")] { // supported_decodings check runtime luid let gpu = scrap::gpucodec::GpuDecoder::possible_available_without_check(); if gpu.0 { h264 = true; } if gpu.1 { h265 = true; } } (h264, h265) } #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { crate::platform::is_root() } #[cfg(any(target_os = "android", target_os = "ios"))] #[inline] pub fn is_root() -> bool { false } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn check_super_user_permission() -> bool { #[cfg(feature = "flatpak")] return true; #[cfg(any(windows, target_os = "linux", target_os = "macos"))] return crate::platform::check_super_user_permission().unwrap_or(false); #[cfg(not(any(windows, target_os = "linux", target_os = "macos")))] return true; } #[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] pub fn check_zombie() { let mut deads = Vec::new(); loop { let mut lock = CHILDREN.lock().unwrap(); let mut n = 0; for (id, c) in lock.1.iter_mut() { if let Ok(Some(_)) = c.try_wait() { deads.push(id.clone()); n += 1; } } for ref id in deads.drain(..) { lock.1.remove(id); } if n > 0 { lock.0 = true; } drop(lock); std::thread::sleep(std::time::Duration::from_millis(100)); } } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] pub fn recent_sessions_updated() -> bool { let mut children = CHILDREN.lock().unwrap(); if children.0 { children.0 = false; true } else { false } } #[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] pub fn new_remote(id: String, remote_type: String, force_relay: bool) { let mut lock = CHILDREN.lock().unwrap(); let mut args = vec![format!("--{}", remote_type), id.clone()]; if force_relay { args.push("".to_string()); // password args.push("--relay".to_string()); } let key = (id.clone(), remote_type.clone()); if let Some(c) = lock.1.get_mut(&key) { if let Ok(Some(_)) = c.try_wait() { lock.1.remove(&key); } else { if remote_type == "rdp" { allow_err!(c.kill()); std::thread::sleep(std::time::Duration::from_millis(30)); c.try_wait().ok(); lock.1.remove(&key); } else { return; } } } match crate::run_me(args) { Ok(child) => { lock.1.insert(key, child); } Err(err) => { log::error!("Failed to spawn remote: {}", err); } } } // Make sure `SENDER` is inited here. #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start_option_status_sync() { let _sender = SENDER.lock().unwrap(); } // not call directly #[cfg(not(any(target_os = "android", target_os = "ios")))] fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender { let (tx, rx) = mpsc::unbounded_channel::(); std::thread::spawn(move || check_connect_status_(reconnect, rx)); tx } #[cfg(feature = "flutter")] pub fn account_auth(op: String, id: String, uuid: String, remember_me: bool) { account::OidcSession::account_auth(get_api_server(), op, id, uuid, remember_me); } #[cfg(feature = "flutter")] pub fn account_auth_cancel() { account::OidcSession::auth_cancel(); } #[cfg(feature = "flutter")] pub fn account_auth_result() -> String { serde_json::to_string(&account::OidcSession::get_result()).unwrap_or_default() } #[cfg(feature = "flutter")] pub fn set_user_default_option(key: String, value: String) { use hbb_common::config::UserDefaultConfig; UserDefaultConfig::load().set(key, value); } #[cfg(feature = "flutter")] pub fn get_user_default_option(key: String) -> String { use hbb_common::config::UserDefaultConfig; UserDefaultConfig::load().get(&key) } pub fn get_fingerprint() -> String { #[cfg(any(target_os = "android", target_os = "ios"))] if Config::get_key_confirmed() { return crate::common::pk_to_fingerprint(Config::get_key_pair().1); } else { return "".to_owned(); } #[cfg(not(any(target_os = "android", target_os = "ios")))] return ipc::get_fingerprint(); } #[inline] pub fn get_login_device_info() -> LoginDeviceInfo { LoginDeviceInfo { // std::env::consts::OS is better than whoami::platform() here. os: std::env::consts::OS.to_owned(), r#type: "client".to_owned(), name: crate::common::hostname(), } } #[inline] pub fn get_login_device_info_json() -> String { serde_json::to_string(&get_login_device_info()).unwrap_or("{}".to_string()) } // notice: avoiding create ipc connection repeatedly, // because windows named pipe has serious memory leak issue. #[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver) { #[cfg(not(feature = "flutter"))] let mut key_confirmed = false; let mut rx = rx; let mut mouse_time = 0; #[cfg(not(feature = "flutter"))] let mut id = "".to_owned(); #[cfg(any( target_os = "windows", all( any(target_os = "linux", target_os = "macos"), feature = "unix-file-copy-paste" ) ))] let mut enable_file_transfer = "".to_owned(); loop { if let Ok(mut c) = ipc::connect(1000, "").await { let mut timer = time::interval(time::Duration::from_secs(1)); loop { tokio::select! { res = c.next() => { match res { Err(err) => { log::error!("ipc connection closed: {}", err); break; } #[cfg(not(any(target_os = "android", target_os = "ios")))] Ok(Some(ipc::Data::MouseMoveTime(v))) => { mouse_time = v; UI_STATUS.lock().unwrap().mouse_time = v; } Ok(Some(ipc::Data::Options(Some(v)))) => { *OPTIONS.lock().unwrap() = v; *OPTION_SYNCED.lock().unwrap() = true; #[cfg(any( target_os = "windows", all( any(target_os="linux", target_os = "macos"), feature = "unix-file-copy-paste" ) ))] { let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default(); if b != enable_file_transfer { clipboard::ContextSend::enable(b.is_empty()); enable_file_transfer = b; } } } Ok(Some(ipc::Data::Config((name, Some(value))))) => { if name == "id" { #[cfg(not(feature = "flutter"))] { id = value; } } else if name == "temporary-password" { *TEMPORARY_PASSWD.lock().unwrap() = value; } } Ok(Some(ipc::Data::OnlineStatus(Some((mut x, _c))))) => { if x > 0 { x = 1 } #[cfg(not(feature = "flutter"))] { key_confirmed = _c; } *UI_STATUS.lock().unwrap() = UiStatus { status_num: x as _, #[cfg(not(feature = "flutter"))] key_confirmed: _c, #[cfg(not(any(target_os = "android", target_os = "ios")))] mouse_time, #[cfg(not(feature = "flutter"))] id: id.clone(), }; } _ => {} } } Some(data) = rx.recv() => { allow_err!(c.send(&data).await); } _ = timer.tick() => { c.send(&ipc::Data::OnlineStatus(None)).await.ok(); c.send(&ipc::Data::Options(None)).await.ok(); c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok(); c.send(&ipc::Data::Config(("temporary-password".to_owned(), None))).await.ok(); } } } } if !reconnect { OPTIONS .lock() .unwrap() .insert("ipc-closed".to_owned(), "Y".to_owned()); break; } *UI_STATUS.lock().unwrap() = UiStatus { status_num: -1, #[cfg(not(feature = "flutter"))] key_confirmed, #[cfg(not(any(target_os = "android", target_os = "ios")))] mouse_time, #[cfg(not(feature = "flutter"))] id: id.clone(), }; sleep(1.).await; } } #[allow(dead_code)] pub fn option_synced() -> bool { #[cfg(not(any(target_os = "android", target_os = "ios")))] { OPTION_SYNCED.lock().unwrap().clone() } #[cfg(any(target_os = "android", target_os = "ios"))] { true } } #[cfg(any(target_os = "android", feature = "flutter"))] #[cfg(not(any(target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub(crate) async fn send_to_cm(data: &ipc::Data) { if let Ok(mut c) = ipc::connect(1000, "_cm").await { c.send(data).await.ok(); } } const INVALID_FORMAT: &'static str = "Invalid format"; const UNKNOWN_ERROR: &'static str = "Unknown error"; #[inline] #[tokio::main(flavor = "current_thread")] pub async fn change_id_shared(id: String, old_id: String) -> String { let res = change_id_shared_(id, old_id).await.to_owned(); *ASYNC_JOB_STATUS.lock().unwrap() = res.clone(); res } pub async fn change_id_shared_(id: String, old_id: String) -> &'static str { if !hbb_common::is_valid_custom_id(&id) { return INVALID_FORMAT; } #[cfg(not(any(target_os = "android", target_os = "ios")))] let uuid = Bytes::from( hbb_common::machine_uid::get() .unwrap_or("".to_owned()) .as_bytes() .to_vec(), ); #[cfg(any(target_os = "android", target_os = "ios"))] let uuid = Bytes::from(hbb_common::get_uuid()); if uuid.is_empty() { log::error!("Failed to change id, uuid is_empty"); return UNKNOWN_ERROR; } #[cfg(not(any(target_os = "android", target_os = "ios")))] let rendezvous_servers = crate::ipc::get_rendezvous_servers(1_000).await; #[cfg(any(target_os = "android", target_os = "ios"))] let rendezvous_servers = Config::get_rendezvous_servers(); let mut futs = Vec::new(); let err: Arc> = Default::default(); for rendezvous_server in rendezvous_servers { let err = err.clone(); let id = id.to_owned(); let uuid = uuid.clone(); let old_id = old_id.clone(); futs.push(tokio::spawn(async move { let tmp = check_id(rendezvous_server, old_id, id, uuid).await; if !tmp.is_empty() { *err.lock().unwrap() = tmp; } })); } join_all(futs).await; let err = *err.lock().unwrap(); if err.is_empty() { #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::ipc::set_config_async("id", id.to_owned()).await.ok(); #[cfg(any(target_os = "android", target_os = "ios"))] { Config::set_key_confirmed(false); Config::set_id(&id); } } err } async fn check_id( rendezvous_server: String, old_id: String, id: String, uuid: Bytes, ) -> &'static str { if let Ok(mut socket) = hbb_common::socket_client::connect_tcp( crate::check_port(rendezvous_server, RENDEZVOUS_PORT), CONNECT_TIMEOUT, ) .await { let mut msg_out = Message::new(); msg_out.set_register_pk(RegisterPk { old_id, id, uuid, ..Default::default() }); let mut ok = false; if socket.send(&msg_out).await.is_ok() { if let Some(msg_in) = crate::common::get_next_nonkeyexchange_msg(&mut socket, None).await { match msg_in.union { Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { match rpr.result.enum_value() { Ok(register_pk_response::Result::OK) => { ok = true; } Ok(register_pk_response::Result::ID_EXISTS) => { return "Not available"; } Ok(register_pk_response::Result::TOO_FREQUENT) => { return "Too frequent"; } Ok(register_pk_response::Result::NOT_SUPPORT) => { return "server_not_support"; } Ok(register_pk_response::Result::SERVER_ERROR) => { return "Server error"; } Ok(register_pk_response::Result::INVALID_ID_FORMAT) => { return INVALID_FORMAT; } _ => {} } } _ => {} } } } if !ok { return UNKNOWN_ERROR; } } else { return "Failed to connect to rendezvous server"; } "" } // if it's relay id, return id processed, otherwise return original id pub fn handle_relay_id(id: &str) -> &str { if id.ends_with(r"\r") || id.ends_with(r"/r") { &id[0..id.len() - 2] } else { id } } pub fn support_remove_wallpaper() -> bool { #[cfg(any(target_os = "windows", target_os = "linux"))] return crate::platform::WallPaperRemover::support(); #[cfg(not(any(target_os = "windows", target_os = "linux")))] return false; }