1106 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1106 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::{
 | |
|     collections::HashMap,
 | |
|     process::Child,
 | |
|     sync::{Arc, Mutex},
 | |
|     time::SystemTime,
 | |
| };
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios"))]
 | |
| use hbb_common::password_security;
 | |
| use hbb_common::{
 | |
|     allow_err,
 | |
|     config::{self, Config, LocalConfig, PeerConfig},
 | |
|     directories_next, log, sleep,
 | |
|     tokio::{self, sync::mpsc, time},
 | |
| };
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| use hbb_common::{
 | |
|     config::{RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
 | |
|     futures::future::join_all,
 | |
|     protobuf::Message as _,
 | |
|     rendezvous_proto::*,
 | |
|     tcp::FramedStream,
 | |
| };
 | |
| 
 | |
| #[cfg(feature = "flutter")]
 | |
| use crate::hbbs_http::account;
 | |
| use crate::{common::SOFTWARE_UPDATE_URL, ipc};
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| type Message = RendezvousMessage;
 | |
| 
 | |
| pub type Children = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
 | |
| type Status = (i32, bool, i64, String); // (status_num, key_confirmed, mouse_time, id)
 | |
| 
 | |
| lazy_static::lazy_static! {
 | |
|     static ref CHILDREN : Children = Default::default();
 | |
|     static ref UI_STATUS : Arc<Mutex<Status>> = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
 | |
|     static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options()));
 | |
|     static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
 | |
|     static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
 | |
|     pub static ref OPTION_SYNCED : Arc<Mutex<bool>> = Default::default();
 | |
| }
 | |
| 
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
| lazy_static::lazy_static! {
 | |
|     pub static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(check_connect_status(true));
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn recent_sessions_updated() -> bool {
 | |
|     let mut children = CHILDREN.lock().unwrap();
 | |
|     if children.0 {
 | |
|         children.0 = false;
 | |
|         true
 | |
|     } else {
 | |
|         false
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[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 get_remote_id() -> String {
 | |
|     LocalConfig::get_remote_id()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn set_remote_id(id: String) {
 | |
|     LocalConfig::set_remote_id(&id);
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn goto_install() {
 | |
|     allow_err!(crate::run_me(vec!["--install"]));
 | |
| }
 | |
| 
 | |
| #[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) {
 | |
|     #[cfg(target_os = "linux")]
 | |
|     {
 | |
|         std::process::Command::new("pkexec")
 | |
|             .args(&["apt", "install", "-f", &_path])
 | |
|             .spawn()
 | |
|             .ok();
 | |
|         std::fs::remove_file(&_path).ok();
 | |
|         crate::run_me(Vec::<&str>::new()).ok();
 | |
|     }
 | |
|     #[cfg(windows)]
 | |
|     {
 | |
|         let mut path = _path;
 | |
|         if path.is_empty() {
 | |
|             if let Ok(tmp) = std::env::current_exe() {
 | |
|                 path = tmp.to_string_lossy().to_string();
 | |
|             }
 | |
|         }
 | |
|         std::process::Command::new(path)
 | |
|             .arg("--update")
 | |
|             .spawn()
 | |
|             .ok();
 | |
|         std::process::exit(0);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[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 has_rendezvous_service() -> bool {
 | |
|     #[cfg(all(windows, feature = "hbbs"))]
 | |
|     return crate::platform::is_win_server() && crate::platform::windows::get_license().is_some();
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_license() -> String {
 | |
|     #[cfg(windows)]
 | |
|     if let Some(lic) = crate::platform::windows::get_license() {
 | |
|         #[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!(
 | |
|             "<br /> Key: {} <br /> Host: {} Api: {}",
 | |
|             lic.key, lic.host, lic.api
 | |
|         );
 | |
|     }
 | |
|     Default::default()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_option_opt(key: &str) -> Option<String> {
 | |
|     OPTIONS.lock().unwrap().get(key).map(|x| x.clone())
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_option(key: String) -> String {
 | |
|     get_option_(&key)
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn get_option_(key: &str) -> String {
 | |
|     let map = OPTIONS.lock().unwrap();
 | |
|     if let Some(v) = map.get(key) {
 | |
|         v.to_owned()
 | |
|     } else {
 | |
|         "".to_owned()
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[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_config(key: String) -> String {
 | |
|     LocalConfig::get_flutter_config(&key)
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| #[inline]
 | |
| pub fn set_local_flutter_config(key: String, value: String) {
 | |
|     LocalConfig::set_flutter_config(key, value);
 | |
| }
 | |
| 
 | |
| #[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]
 | |
| 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 {
 | |
|     crate::get_custom_rendezvous_server(get_option_("custom-rendezvous-server")).is_empty()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_options() -> String {
 | |
|     let options = OPTIONS.lock().unwrap();
 | |
|     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()
 | |
| }
 | |
| 
 | |
| #[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<String> {
 | |
|     let mut a = Vec::new();
 | |
|     #[cfg(not(target_os = "linux"))]
 | |
|     {
 | |
|         fn get_sound_inputs_() -> Vec<String> {
 | |
|             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<String> = 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<String, String>) {
 | |
|     *OPTIONS.lock().unwrap() = m.clone();
 | |
|     #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
|     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) {
 | |
|     let mut options = OPTIONS.lock().unwrap();
 | |
|     #[cfg(target_os = "macos")]
 | |
|     if &key == "stop-service" {
 | |
|         let is_stop = value == "Y";
 | |
|         if is_stop && crate::platform::macos::uninstall() {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if value.is_empty() {
 | |
|         options.remove(&key);
 | |
|     } else {
 | |
|         options.insert(key.clone(), value.clone());
 | |
|     }
 | |
|     #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
|     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<String> {
 | |
|     #[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]
 | |
| pub fn set_socks(proxy: String, username: String, password: String) {
 | |
|     #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
|     ipc::set_socks(config::Socks5Server {
 | |
|         proxy,
 | |
|         username,
 | |
|         password,
 | |
|     })
 | |
|     .ok();
 | |
| }
 | |
| 
 | |
| #[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 installed_version = crate::platform::windows::get_installed_version();
 | |
|         let a = hbb_common::get_version_number(crate::VERSION);
 | |
|         let b = hbb_common::get_version_number(&installed_version);
 | |
|         return a > b;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn closing(x: i32, y: i32, w: i32, h: i32) {
 | |
|     #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
|     crate::server::input_service::fix_key_down_timeout_at_exit();
 | |
|     LocalConfig::set_size(x, y, w, h);
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_size() -> Vec<i32> {
 | |
|     let s = LocalConfig::get_size();
 | |
|     let mut v = Vec::new();
 | |
|     v.push(s.0);
 | |
|     v.push(s.1);
 | |
|     v.push(s.2);
 | |
|     v.push(s.3);
 | |
|     v
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_mouse_time() -> f64 {
 | |
|     let ui_status = UI_STATUS.lock().unwrap();
 | |
|     let res = ui_status.2 as f64;
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| #[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]
 | |
| pub fn get_connect_status() -> Status {
 | |
|     let ui_statue = UI_STATUS.lock().unwrap();
 | |
|     let res = ui_statue.clone();
 | |
|     res
 | |
| }
 | |
| 
 | |
| #[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<String> {
 | |
|     LocalConfig::get_fav()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn store_fav(fav: Vec<String>) {
 | |
|     LocalConfig::set_fav(fav);
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_recent_sessions() -> Vec<(String, SystemTime, PeerConfig)> {
 | |
|     PeerConfig::peers()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
 | |
| pub fn get_icon() -> String {
 | |
|     crate::get_icon()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn remove_peer(id: String) {
 | |
|     PeerConfig::remove(&id);
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn new_remote(id: String, remote_type: String) {
 | |
|     let mut lock = CHILDREN.lock().unwrap();
 | |
|     let args = vec![format!("--{}", remote_type), id.clone()];
 | |
|     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);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[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]
 | |
| pub fn get_error() -> String {
 | |
|     #[cfg(not(any(feature = "cli")))]
 | |
|     #[cfg(target_os = "linux")]
 | |
|     {
 | |
|         let dtype = crate::platform::linux::get_display_server();
 | |
|         if "wayland" == dtype {
 | |
|             return crate::server::wayland::common_get_error();
 | |
|         }
 | |
|         if dtype != "x11" {
 | |
|             return format!(
 | |
|                 "{} {}, {}",
 | |
|                 t("Unsupported display server ".to_owned()),
 | |
|                 dtype,
 | |
|                 t("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 fix_login_wayland() {
 | |
|     #[cfg(target_os = "linux")]
 | |
|     crate::platform::linux::fix_login_wayland();
 | |
| }
 | |
| 
 | |
| #[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 modify_default_login() -> String {
 | |
|     #[cfg(target_os = "linux")]
 | |
|     return crate::platform::linux::modify_default_login();
 | |
|     #[cfg(not(target_os = "linux"))]
 | |
|     return "".to_owned();
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_software_update_url() -> String {
 | |
|     SOFTWARE_UPDATE_URL.lock().unwrap().clone()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_new_version() -> String {
 | |
|     hbb_common::get_version_from_url(&*SOFTWARE_UPDATE_URL.lock().unwrap())
 | |
| }
 | |
| 
 | |
| #[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()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
| pub fn get_software_ext() -> String {
 | |
|     #[cfg(windows)]
 | |
|     let p = "exe";
 | |
|     #[cfg(target_os = "macos")]
 | |
|     let p = "dmg";
 | |
|     #[cfg(target_os = "linux")]
 | |
|     let p = "deb";
 | |
|     p.to_owned()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
| pub fn get_software_store_path() -> String {
 | |
|     let mut p = std::env::temp_dir();
 | |
|     let name = SOFTWARE_UPDATE_URL
 | |
|         .lock()
 | |
|         .unwrap()
 | |
|         .split("/")
 | |
|         .last()
 | |
|         .map(|x| x.to_owned())
 | |
|         .unwrap_or(crate::get_app_name());
 | |
|     p.push(name);
 | |
|     format!("{}.{}", p.to_string_lossy(), get_software_ext())
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn create_shortcut(_id: String) {
 | |
|     #[cfg(windows)]
 | |
|     crate::platform::windows::create_shortcut(&_id).ok();
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| #[inline]
 | |
| pub fn discover() {
 | |
|     std::thread::spawn(move || {
 | |
|         allow_err!(crate::lan::discover());
 | |
|     });
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> {
 | |
|     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 get_uuid() -> String {
 | |
|     base64::encode(hbb_common::get_uuid())
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
 | |
| pub fn open_url(url: String) {
 | |
|     #[cfg(windows)]
 | |
|     let p = "explorer";
 | |
|     #[cfg(target_os = "macos")]
 | |
|     let p = "open";
 | |
|     #[cfg(target_os = "linux")]
 | |
|     let p = if std::path::Path::new("/usr/bin/firefox").exists() {
 | |
|         "firefox"
 | |
|     } else {
 | |
|         "xdg-open"
 | |
|     };
 | |
|     allow_err!(std::process::Command::new(p).arg(url).spawn());
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| #[inline]
 | |
| pub fn change_id(id: String) {
 | |
|     *ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned();
 | |
|     let old_id = get_id();
 | |
|     std::thread::spawn(move || {
 | |
|         *ASYNC_JOB_STATUS.lock().unwrap() = change_id_(id, old_id).to_owned();
 | |
|     });
 | |
| }
 | |
| 
 | |
| #[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]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
| pub fn is_ok_change_id() -> bool {
 | |
|     machine_uid::get().is_ok()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_async_job_status() -> String {
 | |
|     ASYNC_JOB_STATUS.lock().unwrap().clone()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
 | |
| pub fn t(name: String) -> String {
 | |
|     crate::client::translate(name)
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn get_langs() -> String {
 | |
|     crate::lang::LANGS.to_string()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn default_video_save_directory() -> String {
 | |
|     let appname = crate::get_app_name();
 | |
| 
 | |
|     #[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");
 | |
|         return path;
 | |
|     }
 | |
| 
 | |
|     if let Some(user) = directories_next::UserDirs::new() {
 | |
|         if let Some(video_dir) = user.video_dir() {
 | |
|             return video_dir.join(appname).to_string_lossy().to_string();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[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"
 | |
|         };
 | |
|         return home.join(name).join(appname).to_string_lossy().to_string();
 | |
|     }
 | |
| 
 | |
|     if let Ok(exe) = std::env::current_exe() {
 | |
|         if let Some(dir) = exe.parent() {
 | |
|             return dir.join("videos").to_string_lossy().to_string();
 | |
|         }
 | |
|     }
 | |
|     "".to_owned()
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| pub fn is_xfce() -> bool {
 | |
|     crate::platform::is_xfce()
 | |
| }
 | |
| 
 | |
| #[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 is_release() -> bool {
 | |
|     #[cfg(not(debug_assertions))]
 | |
|     return true;
 | |
|     #[cfg(debug_assertions)]
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| #[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"))]
 | |
|     return crate::platform::check_super_user_permission().unwrap_or(false);
 | |
|     #[cfg(not(any(windows, target_os = "linux")))]
 | |
|     true
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| pub fn check_zombie(children: Children) {
 | |
|     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));
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn start_option_status_sync() {
 | |
|     #[cfg(not(any(target_os = "android", target_os = "ios")))]
 | |
|     {
 | |
|         let _sender = SENDER.lock().unwrap();
 | |
|     }
 | |
| }
 | |
| 
 | |
| // not call directly
 | |
| fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender<ipc::Data> {
 | |
|     let (tx, rx) = mpsc::unbounded_channel::<ipc::Data>();
 | |
|     std::thread::spawn(move || check_connect_status_(reconnect, rx));
 | |
|     tx
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "flutter")]
 | |
| pub fn account_auth(op: String, id: String, uuid: String) {
 | |
|     account::OidcSession::account_auth(op, id, uuid);
 | |
| }
 | |
| 
 | |
| #[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()
 | |
| }
 | |
| 
 | |
| // notice: avoiding create ipc connecton repeatly,
 | |
| // because windows named pipe has serious memory leak issue.
 | |
| #[tokio::main(flavor = "current_thread")]
 | |
| async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc::Data>) {
 | |
|     let mut key_confirmed = false;
 | |
|     let mut rx = rx;
 | |
|     let mut mouse_time = 0;
 | |
|     let mut id = "".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;
 | |
|                             }
 | |
|                             Ok(Some(ipc::Data::MouseMoveTime(v))) => {
 | |
|                                 mouse_time = v;
 | |
|                                 UI_STATUS.lock().unwrap().2 = v;
 | |
|                             }
 | |
|                             Ok(Some(ipc::Data::Options(Some(v)))) => {
 | |
|                                 *OPTIONS.lock().unwrap() = v;
 | |
|                                 *OPTION_SYNCED.lock().unwrap() = true;
 | |
|                             }
 | |
|                             Ok(Some(ipc::Data::Config((name, Some(value))))) => {
 | |
|                                 if name == "id" {
 | |
|                                     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
 | |
|                                 }
 | |
|                                 key_confirmed = c;
 | |
|                                 *UI_STATUS.lock().unwrap() = (x as _, key_confirmed, mouse_time, 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() = (-1, key_confirmed, mouse_time, id.clone());
 | |
|         sleep(1.).await;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| pub fn option_synced() -> bool {
 | |
|     OPTION_SYNCED.lock().unwrap().clone()
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| #[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();
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| const INVALID_FORMAT: &'static str = "Invalid format";
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| const UNKNOWN_ERROR: &'static str = "Unknown error";
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| #[tokio::main(flavor = "current_thread")]
 | |
| async fn change_id_(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 = machine_uid::get().unwrap_or("".to_owned());
 | |
|     #[cfg(any(target_os = "android", target_os = "ios"))]
 | |
|     let uuid = base64::encode(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<Mutex<&str>> = 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
 | |
| }
 | |
| 
 | |
| #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
 | |
| async fn check_id(
 | |
|     rendezvous_server: String,
 | |
|     old_id: String,
 | |
|     id: String,
 | |
|     uuid: String,
 | |
| ) -> &'static str {
 | |
|     let any_addr = Config::get_any_listen_addr();
 | |
|     if let Ok(mut socket) = FramedStream::new(
 | |
|         crate::check_port(rendezvous_server, RENDEZVOUS_PORT),
 | |
|         any_addr,
 | |
|         RENDEZVOUS_TIMEOUT,
 | |
|     )
 | |
|     .await
 | |
|     {
 | |
|         let mut msg_out = Message::new();
 | |
|         msg_out.set_register_pk(RegisterPk {
 | |
|             old_id,
 | |
|             id,
 | |
|             uuid: uuid.into(),
 | |
|             ..Default::default()
 | |
|         });
 | |
|         let mut ok = false;
 | |
|         if socket.send(&msg_out).await.is_ok() {
 | |
|             if let Some(Ok(bytes)) = socket.next_timeout(3_000).await {
 | |
|                 if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
 | |
|                     match msg_in.union {
 | |
|                         Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => {
 | |
|                             match rpr.result.enum_value_or_default() {
 | |
|                                 register_pk_response::Result::OK => {
 | |
|                                     ok = true;
 | |
|                                 }
 | |
|                                 register_pk_response::Result::ID_EXISTS => {
 | |
|                                     return "Not available";
 | |
|                                 }
 | |
|                                 register_pk_response::Result::TOO_FREQUENT => {
 | |
|                                     return "Too frequent";
 | |
|                                 }
 | |
|                                 register_pk_response::Result::NOT_SUPPORT => {
 | |
|                                     return "server_not_support";
 | |
|                                 }
 | |
|                                 register_pk_response::Result::SERVER_ERROR => {
 | |
|                                     return "Server error";
 | |
|                                 }
 | |
|                                 register_pk_response::Result::INVALID_ID_FORMAT => {
 | |
|                                     return INVALID_FORMAT;
 | |
|                                 }
 | |
|                                 _ => {}
 | |
|                             }
 | |
|                         }
 | |
|                         _ => {}
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if !ok {
 | |
|             return UNKNOWN_ERROR;
 | |
|         }
 | |
|     } else {
 | |
|         return "Failed to connect to rendezvous server";
 | |
|     }
 | |
|     ""
 | |
| }
 |