diff --git a/flutter/macos/Podfile.lock b/flutter/macos/Podfile.lock index 4dfab95fd..a9f3c7388 100644 --- a/flutter/macos/Podfile.lock +++ b/flutter/macos/Podfile.lock @@ -112,4 +112,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 703c98b0b..880d360a5 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -5,7 +5,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, - sync::{Arc, Mutex, RwLock}, + sync::{Mutex, RwLock}, time::{Duration, Instant, SystemTime}, }; @@ -46,41 +46,42 @@ pub const CONFIG_OPTION_ALLOW_LINUX_HEADLESS: &str = "allow-linux-headless"; #[cfg(target_os = "macos")] lazy_static::lazy_static! { - pub static ref ORG: Arc> = Arc::new(RwLock::new("com.carriez".to_owned())); + pub static ref ORG: RwLock = RwLock::new("com.carriez".to_owned()); } type Size = (i32, i32, i32, i32); type KeyPair = (Vec, Vec); lazy_static::lazy_static! { - static ref CONFIG: Arc> = Arc::new(RwLock::new(Config::load())); - static ref CONFIG2: Arc> = Arc::new(RwLock::new(Config2::load())); - static ref LOCAL_CONFIG: Arc> = Arc::new(RwLock::new(LocalConfig::load())); - static ref ONLINE: Arc>> = Default::default(); - pub static ref PROD_RENDEZVOUS_SERVER: Arc> = Arc::new(RwLock::new(match option_env!("RENDEZVOUS_SERVER") { + static ref CONFIG: RwLock = RwLock::new(Config::load()); + static ref CONFIG2: RwLock = RwLock::new(Config2::load()); + static ref LOCAL_CONFIG: RwLock = RwLock::new(LocalConfig::load()); + static ref ONLINE: Mutex> = Default::default(); + pub static ref PROD_RENDEZVOUS_SERVER: RwLock = RwLock::new(match option_env!("RENDEZVOUS_SERVER") { Some(key) if !key.is_empty() => key, _ => "", - }.to_owned())); - pub static ref EXE_RENDEZVOUS_SERVER: Arc> = Default::default(); - pub static ref APP_NAME: Arc> = Arc::new(RwLock::new("RustDesk".to_owned())); - static ref KEY_PAIR: Arc>> = Default::default(); - static ref USER_DEFAULT_CONFIG: Arc> = Arc::new(RwLock::new((UserDefaultConfig::load(), Instant::now()))); - pub static ref NEW_STORED_PEER_CONFIG: Arc>> = Default::default(); - pub static ref DEFAULT_SETTINGS: Arc>> = Default::default(); - pub static ref OVERWRITE_SETTINGS: Arc>> = Default::default(); - pub static ref DEFAULT_DISPLAY_SETTINGS: Arc>> = Default::default(); - pub static ref OVERWRITE_DISPLAY_SETTINGS: Arc>> = Default::default(); - pub static ref DEFAULT_LOCAL_SETTINGS: Arc>> = Default::default(); - pub static ref OVERWRITE_LOCAL_SETTINGS: Arc>> = Default::default(); + }.to_owned()); + pub static ref EXE_RENDEZVOUS_SERVER: RwLock = Default::default(); + pub static ref APP_NAME: RwLock = RwLock::new("RustDesk".to_owned()); + static ref KEY_PAIR: Mutex> = Default::default(); + static ref USER_DEFAULT_CONFIG: RwLock<(UserDefaultConfig, Instant)> = RwLock::new((UserDefaultConfig::load(), Instant::now())); + pub static ref NEW_STORED_PEER_CONFIG: Mutex> = Default::default(); + pub static ref DEFAULT_SETTINGS: RwLock> = Default::default(); + pub static ref OVERWRITE_SETTINGS: RwLock> = Default::default(); + pub static ref DEFAULT_DISPLAY_SETTINGS: RwLock> = Default::default(); + pub static ref OVERWRITE_DISPLAY_SETTINGS: RwLock> = Default::default(); + pub static ref DEFAULT_LOCAL_SETTINGS: RwLock> = Default::default(); + pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock> = Default::default(); + pub static ref HARD_SETTINGS: RwLock> = Default::default(); } lazy_static::lazy_static! { - pub static ref APP_DIR: Arc> = Default::default(); + pub static ref APP_DIR: RwLock = Default::default(); } #[cfg(any(target_os = "android", target_os = "ios"))] lazy_static::lazy_static! { - pub static ref APP_HOME_DIR: Arc> = Default::default(); + pub static ref APP_HOME_DIR: RwLock = Default::default(); } pub const LINK_DOCS_HOME: &str = "https://rustdesk.com/docs/en/"; @@ -922,17 +923,13 @@ impl Config { } pub fn get_option(k: &str) -> String { - if let Some(v) = OVERWRITE_SETTINGS.read().unwrap().get(k) { - return v.clone(); - } - if let Some(v) = CONFIG2.read().unwrap().options.get(k) { - v.clone() - } else { - if let Some(v) = DEFAULT_SETTINGS.read().unwrap().get(k) { - return v.clone(); - } - "".to_owned() - } + get_or( + &OVERWRITE_SETTINGS, + &CONFIG2.read().unwrap().options, + &DEFAULT_SETTINGS, + k, + ) + .unwrap_or_default() } pub fn set_option(k: String, v: String) { @@ -1381,17 +1378,13 @@ impl LocalConfig { } pub fn get_option(k: &str) -> String { - if let Some(v) = OVERWRITE_LOCAL_SETTINGS.read().unwrap().get(k) { - return v.clone(); - } - if let Some(v) = LOCAL_CONFIG.read().unwrap().options.get(k) { - v.clone() - } else { - if let Some(v) = DEFAULT_LOCAL_SETTINGS.read().unwrap().get(k) { - return v.clone(); - } - "".to_owned() - } + get_or( + &OVERWRITE_LOCAL_SETTINGS, + &LOCAL_CONFIG.read().unwrap().options, + &DEFAULT_LOCAL_SETTINGS, + k, + ) + .unwrap_or_default() } pub fn set_option(k: String, v: String) { @@ -1608,18 +1601,13 @@ impl UserDefaultConfig { } } - fn get_after(&self, key: &str) -> Option { - if let Some(v) = OVERWRITE_DISPLAY_SETTINGS.read().unwrap().get(key) { - return Some(v.clone()); - } - if let Some(v) = self.options.get(key) { - Some(v.clone()) - } else { - if let Some(v) = DEFAULT_DISPLAY_SETTINGS.read().unwrap().get(key) { - return Some(v.clone()); - } - None - } + fn get_after(&self, k: &str) -> Option { + get_or( + &OVERWRITE_DISPLAY_SETTINGS, + &self.options, + &DEFAULT_DISPLAY_SETTINGS, + k, + ) } } @@ -1854,6 +1842,21 @@ deserialize_default!(deserialize_hashmap_string_string, HashMap) deserialize_default!(deserialize_hashmap_string_bool, HashMap); deserialize_default!(deserialize_hashmap_resolutions, HashMap); +#[inline] +fn get_or( + a: &RwLock>, + b: &HashMap, + c: &RwLock>, + k: &str, +) -> Option { + a.read() + .unwrap() + .get(k) + .or(b.get(k)) + .or(c.read().unwrap().get(k)) + .cloned() +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/common.rs b/src/common.rs index 3db34026f..dddeaddc2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1467,6 +1467,102 @@ impl ClipboardContext { } } +pub fn load_custom_client() { + let Ok(cmd) = std::env::current_exe() else { + return; + }; + let Some(path) = cmd.parent().map(|x| x.join("custom.txt")) else { + return; + }; + if path.is_file() { + let Ok(data) = std::fs::read_to_string(&path) else { + log::error!("Failed to read custom client config"); + return; + }; + read_custom_client(&data); + } +} + +pub fn read_custom_client(config: &str) { + let Ok(data) = decode64(config) else { + log::error!("Failed to decode custom client config"); + return; + }; + const KEY: &str = "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM="; + let Some(pk) = get_rs_pk(KEY) else { + log::error!("Failed to parse public key of custom client"); + return; + }; + let Ok(data) = sign::verify(&data, &pk) else { + log::error!("Failed to dec custom client config"); + return; + }; + let Ok(mut data) = + serde_json::from_slice::>(&data) + else { + log::error!("Failed to parse custom client config"); + return; + }; + if let Some(default_settings) = data.remove("default_settings") { + if let Some(default_settings) = default_settings.as_object() { + for (k, v) in default_settings { + let Some(v) = v.as_str() else { + continue; + }; + if k.starts_with("$$") { + config::DEFAULT_DISPLAY_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v[2..].to_owned()); + } else if k.starts_with("$") { + config::DEFAULT_LOCAL_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v[1..].to_owned()); + } else { + config::DEFAULT_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v.to_owned()); + } + } + } + } + if let Some(overwrite_settings) = data.remove("overwrite_settings") { + if let Some(overwrite_settings) = overwrite_settings.as_object() { + for (k, v) in overwrite_settings { + let Some(v) = v.as_str() else { + continue; + }; + if k.starts_with("$$") { + config::OVERWRITE_DISPLAY_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v[2..].to_owned()); + } else if k.starts_with("$") { + config::OVERWRITE_LOCAL_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v[1..].to_owned()); + } else { + config::OVERWRITE_SETTINGS + .write() + .unwrap() + .insert(k.clone(), v.to_owned()); + } + } + } + } + for (k, v) in data { + if let Some(v) = v.as_str() { + config::HARD_SETTINGS + .write() + .unwrap() + .insert(k, v.to_owned()); + }; + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/core_main.rs b/src/core_main.rs index 22db6f5e1..324024ce3 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -38,6 +38,7 @@ fn is_empty_uni_link(arg: &str) -> bool { /// If it returns [`Some`], then the process will continue, and flutter gui will be started. #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn core_main() -> Option> { + crate::load_custom_client(); #[cfg(windows)] crate::platform::windows::bootstrap(); let mut args = Vec::new(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 64b0f54f1..f4197c679 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1814,7 +1814,7 @@ pub fn main_support_remove_wallpaper() -> bool { } pub fn is_qs() -> SyncReturn { - SyncReturn(false) + SyncReturn(get_hard_option("connection-type") == "incoming"); } /// Send a url scheme throught the ipc. @@ -2052,6 +2052,10 @@ pub fn main_has_valid_2fa_sync() -> SyncReturn { SyncReturn(has_valid_2fa()) } +pub fn main_get_hard_option(key: String) -> SyncReturn { + SyncReturn(get_hard_option(key)) +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/ui_interface.rs b/src/ui_interface.rs index a6352e1c0..25a11b45e 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -3,8 +3,9 @@ use hbb_common::password_security; use hbb_common::{ allow_err, bytes::Bytes, - config::{self, Config, LocalConfig, PeerConfig}, - config::{CONNECT_TIMEOUT, RENDEZVOUS_PORT}, + config::{ + self, Config, LocalConfig, PeerConfig, CONNECT_TIMEOUT, HARD_SETTINGS, RENDEZVOUS_PORT, + }, directories_next, futures::future::join_all, log, @@ -171,6 +172,16 @@ pub fn get_local_option(key: String) -> String { LocalConfig::get_option(&key) } +#[inline] +pub fn get_hard_option(key: String) -> String { + config::HARD_SETTINGS + .read() + .unwrap() + .get(&key) + .cloned() + .unwrap_or_default() +} + #[inline] pub fn set_local_option(key: String, value: String) { LocalConfig::set_option(key, value);