use crate::{data::*, log_if_err}; use anyhow::{bail, Result}; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use std::sync::Arc; use sysproxy::Sysproxy; use tauri::{async_runtime::Mutex, utils::platform::current_exe}; pub struct Sysopt { /// current system proxy setting cur_sysproxy: Option, /// record the original system proxy /// recover it when exit old_sysproxy: Option, /// helps to auto launch the app auto_launch: Option, /// record whether the guard async is running or not guard_state: Arc>, } #[cfg(target_os = "windows")] static DEFAULT_BYPASS: &str = "localhost;127.*;192.168.*;"; #[cfg(target_os = "linux")] static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1"; #[cfg(target_os = "macos")] static DEFAULT_BYPASS: &str = "127.0.0.1,localhost,"; impl Sysopt { pub fn new() -> Sysopt { Sysopt { cur_sysproxy: None, old_sysproxy: None, auto_launch: None, guard_state: Arc::new(Mutex::new(false)), } } /// init the sysproxy pub fn init_sysproxy(&mut self) -> Result<()> { let data = Data::global(); let clash = data.clash.lock(); let port = clash.info.port.clone(); if port.is_none() { bail!("clash port is none"); } let verge = data.verge.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let bypass = verge.system_proxy_bypass.clone(); let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); let port = port.unwrap().parse::()?; let host = String::from("127.0.0.1"); self.cur_sysproxy = Some(Sysproxy { enable, host, port, bypass, }); if enable { self.old_sysproxy = Sysproxy::get_system_proxy().map_or(None, |p| Some(p)); self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?; } // launchs the system proxy guard self.guard_proxy(); Ok(()) } /// update the system proxy pub fn update_sysproxy(&mut self) -> Result<()> { if self.cur_sysproxy.is_none() { return self.init_sysproxy(); } let data = Data::global(); let verge = data.verge.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let bypass = verge.system_proxy_bypass.clone(); let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); let mut sysproxy = self.cur_sysproxy.take().unwrap(); sysproxy.enable = enable; sysproxy.bypass = bypass; self.cur_sysproxy = Some(sysproxy); self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?; Ok(()) } /// reset the sysproxy pub fn reset_sysproxy(&mut self) -> Result<()> { if self.cur_sysproxy.is_none() { return Ok(()); } let mut cur = self.cur_sysproxy.take().unwrap(); match self.old_sysproxy.take() { Some(old) => { // 如果原代理设置和当前的设置是一样的,需要关闭 // 否则就恢复原代理设置 if old.enable && old.host == cur.host && old.port == cur.port { cur.enable = false; cur.set_system_proxy()?; } else { old.set_system_proxy()?; } } None => { if cur.enable { cur.enable = false; cur.set_system_proxy()?; } } } Ok(()) } /// init the auto launch pub fn init_launch(&mut self) -> Result<()> { let data = Data::global(); let verge = data.verge.lock(); let enable = verge.enable_auto_launch.clone().unwrap_or(false); if !enable { return Ok(()); } let app_exe = current_exe().unwrap(); let app_exe = dunce::canonicalize(app_exe).unwrap(); let app_name = app_exe.file_stem().unwrap().to_str().unwrap(); let app_path = app_exe.as_os_str().to_str().unwrap(); // fix issue #26 #[cfg(target_os = "windows")] let app_path = format!("\"{app_path}\""); #[cfg(target_os = "windows")] let app_path = app_path.as_str(); let auto = AutoLaunchBuilder::new() .set_app_name(app_name) .set_app_path(app_path) .build()?; // fix issue #26 auto.enable()?; self.auto_launch = Some(auto); Ok(()) } /// update the startup pub fn update_launch(&mut self) -> Result<()> { if self.auto_launch.is_none() { return self.init_launch(); } let data = Data::global(); let verge = data.verge.lock(); let enable = verge.enable_auto_launch.clone().unwrap_or(false); let auto_launch = self.auto_launch.as_ref().unwrap(); match enable { true => auto_launch.enable()?, false => auto_launch.disable()?, }; Ok(()) } /// launch a system proxy guard /// read config from file directly pub fn guard_proxy(&self) { use tokio::time::{sleep, Duration}; let guard_state = self.guard_state.clone(); tauri::async_runtime::spawn(async move { // if it is running, exit let mut state = guard_state.lock().await; if *state { return; } *state = true; drop(state); // default duration is 10s let mut wait_secs = 10u64; loop { sleep(Duration::from_secs(wait_secs)).await; let global = Data::global(); let verge = global.verge.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let guard = verge.enable_proxy_guard.clone().unwrap_or(false); let guard_duration = verge.proxy_guard_duration.clone().unwrap_or(10); let bypass = verge.system_proxy_bypass.clone(); drop(verge); // stop loop if !enable || !guard { break; } // update duration wait_secs = guard_duration; let clash = global.clash.lock(); let port = clash.info.port.clone(); let port = port.unwrap_or("".into()).parse::(); drop(clash); log::debug!(target: "app", "try to guard the system proxy"); match port { Ok(port) => { let sysproxy = Sysproxy { enable: true, host: "127.0.0.1".into(), port, bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()), }; log_if_err!(sysproxy.set_system_proxy()); } Err(_) => log::error!(target: "app", "failed to parse clash port"), } } let mut state = guard_state.lock().await; *state = false; }); } }