mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	feat: add the settings window
This commit is contained in:
		| @@ -181,7 +181,7 @@ fn setup_window(window: &Window, event_tx: mpsc::Sender<AuthEvent>) -> EventHand | ||||
|     window.listen_global(AUTH_REQUEST_EVENT, move |event| { | ||||
|         if let Ok(payload) = TryInto::<AuthRequest>::try_into(event.payload()) { | ||||
|             let event_tx = event_tx.clone(); | ||||
|             send_auth_event(event_tx.clone(), AuthEvent::Request(payload)); | ||||
|             send_auth_event(event_tx, AuthEvent::Request(payload)); | ||||
|         } else { | ||||
|             warn!("Invalid auth request payload"); | ||||
|         } | ||||
| @@ -198,7 +198,7 @@ async fn process( | ||||
|     process_request(window, auth_request)?; | ||||
|  | ||||
|     let handle = tokio::spawn(show_window_after_timeout(window.clone())); | ||||
|     let auth_data = monitor_events(&window, event_rx).await; | ||||
|     let auth_data = monitor_events(window, event_rx).await; | ||||
|  | ||||
|     if !handle.is_finished() { | ||||
|         handle.abort(); | ||||
| @@ -254,12 +254,12 @@ async fn monitor_auth_event(window: &Window, mut event_rx: mpsc::Receiver<AuthEv | ||||
|         if let Some(auth_event) = event_rx.recv().await { | ||||
|             match auth_event { | ||||
|                 AuthEvent::Request(auth_request) => { | ||||
|                     attempt_times = attempt_times + 1; | ||||
|                     attempt_times += 1; | ||||
|                     info!( | ||||
|                         "Got auth request from auth-request event, attempt #{}", | ||||
|                         attempt_times | ||||
|                     ); | ||||
|                     if let Err(err) = process_request(&window, auth_request) { | ||||
|                     if let Err(err) = process_request(window, auth_request) { | ||||
|                         warn!("Error processing auth request: {}", err); | ||||
|                     } | ||||
|                 } | ||||
| @@ -316,7 +316,7 @@ async fn monitor_window_close_event(window: &Window) { | ||||
|         if matches!(event, WindowEvent::CloseRequested { .. }) { | ||||
|             if let Ok(mut close_tx_locked) = close_tx.try_lock() { | ||||
|                 if let Some(close_tx) = close_tx_locked.take() { | ||||
|                     if let Err(_) = close_tx.send(()) { | ||||
|                     if close_tx.send(()).is_err() { | ||||
|                         println!("Error sending close event"); | ||||
|                     } | ||||
|                 } | ||||
| @@ -352,14 +352,23 @@ async fn handle_token_not_found(window: Window, cancel_timeout_rx: Arc<Mutex<mps | ||||
| /// and send it to the event channel | ||||
| fn parse_auth_data(main_res: &WebResource, auth_event_tx: mpsc::Sender<AuthEvent>) { | ||||
|     if let Some(response) = main_res.response() { | ||||
|         if let Some(auth_data) = read_auth_data_from_response(&response) { | ||||
|             debug!("Got auth data from HTTP headers: {:?}", auth_data); | ||||
|             send_auth_data(auth_event_tx, auth_data); | ||||
|             return; | ||||
|         match read_auth_data_from_response(&response) { | ||||
|             Ok(auth_data) => { | ||||
|                 debug!("Got auth data from HTTP headers: {:?}", auth_data); | ||||
|                 send_auth_data(auth_event_tx, auth_data); | ||||
|                 return; | ||||
|             } | ||||
|             Err(AuthError::TokenInvalid) => { | ||||
|                 debug!("Received invalid token from HTTP headers"); | ||||
|                 send_auth_error(auth_event_tx, AuthError::TokenInvalid); | ||||
|                 return; | ||||
|             } | ||||
|             Err(AuthError::TokenNotFound) => { | ||||
|                 debug!("Token not found in HTTP headers, trying to read from HTML"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let auth_event_tx = auth_event_tx.clone(); | ||||
|     main_res.data(Cancellable::NONE, move |data| { | ||||
|         if let Ok(data) = data { | ||||
|             let html = String::from_utf8_lossy(&data); | ||||
| @@ -378,20 +387,27 @@ fn parse_auth_data(main_res: &WebResource, auth_event_tx: mpsc::Sender<AuthEvent | ||||
| } | ||||
|  | ||||
| /// Read the authentication data from the response headers | ||||
| fn read_auth_data_from_response(response: &webkit2gtk::URIResponse) -> Option<AuthData> { | ||||
|     response.http_headers().and_then(|mut headers| { | ||||
|         let auth_data = AuthData::new( | ||||
|             headers.get("saml-username").map(GString::into), | ||||
|             headers.get("prelogin-cookie").map(GString::into), | ||||
|             headers.get("portal-userauthcookie").map(GString::into), | ||||
|         ); | ||||
| fn read_auth_data_from_response(response: &webkit2gtk::URIResponse) -> Result<AuthData, AuthError> { | ||||
|     response | ||||
|         .http_headers() | ||||
|         .map_or(Err(AuthError::TokenNotFound), |mut headers| { | ||||
|             let saml_status: Option<String> = headers.get("saml-auth-status").map(GString::into); | ||||
|             if saml_status == Some("-1".to_string()) { | ||||
|                 return Err(AuthError::TokenInvalid); | ||||
|             } | ||||
|  | ||||
|         if auth_data.check() { | ||||
|             Some(auth_data) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     }) | ||||
|             let auth_data = AuthData::new( | ||||
|                 headers.get("saml-username").map(GString::into), | ||||
|                 headers.get("prelogin-cookie").map(GString::into), | ||||
|                 headers.get("portal-userauthcookie").map(GString::into), | ||||
|             ); | ||||
|  | ||||
|             if auth_data.check() { | ||||
|                 Ok(auth_data) | ||||
|             } else { | ||||
|                 Err(AuthError::TokenNotFound) | ||||
|             } | ||||
|         }) | ||||
| } | ||||
|  | ||||
| /// Read the authentication data from the HTML content | ||||
| @@ -441,7 +457,7 @@ fn send_auth_error(auth_event_tx: mpsc::Sender<AuthEvent>, err: AuthError) { | ||||
| } | ||||
|  | ||||
| fn send_auth_event(auth_event_tx: mpsc::Sender<AuthEvent>, auth_event: AuthEvent) { | ||||
|     let _ = tauri::async_runtime::spawn(async move { | ||||
|     tauri::async_runtime::spawn(async move { | ||||
|         if let Err(err) = auth_event_tx.send(auth_event).await { | ||||
|             warn!("Error sending event: {}", err); | ||||
|         } | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| use crate::auth::{self, AuthData, AuthRequest, SamlBinding, SamlLoginParams}; | ||||
| use crate::{ | ||||
|     auth::{self, AuthData, AuthRequest, SamlBinding, SamlLoginParams}, | ||||
|     utils::get_openssl_conf, | ||||
|     utils::get_openssl_conf_path, | ||||
| }; | ||||
| use gpcommon::{Client, ServerApiError, VpnStatus}; | ||||
| use std::sync::Arc; | ||||
| use tauri::{AppHandle, State}; | ||||
| use tokio::fs; | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) async fn service_online<'a>(client: State<'a, Arc<Client>>) -> Result<bool, ()> { | ||||
| @@ -47,3 +52,22 @@ pub(crate) async fn saml_login( | ||||
|     }; | ||||
|     auth::saml_login(params).await | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) fn os_version() -> String { | ||||
|     whoami::distro() | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) fn openssl_config() -> String { | ||||
|     get_openssl_conf() | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) async fn update_openssl_config(app_handle: AppHandle) -> tauri::Result<()> { | ||||
|     let openssl_conf = get_openssl_conf(); | ||||
|     let openssl_conf_path = get_openssl_conf_path(&app_handle); | ||||
|  | ||||
|     fs::write(openssl_conf_path, openssl_conf).await?; | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
| @@ -3,13 +3,15 @@ | ||||
|     windows_subsystem = "windows" | ||||
| )] | ||||
|  | ||||
| use crate::utils::get_openssl_conf_path; | ||||
| use env_logger::Env; | ||||
| use gpcommon::{Client, ClientStatus, VpnStatus}; | ||||
| use log::warn; | ||||
| use log::{info, warn}; | ||||
| use serde::Serialize; | ||||
| use std::sync::Arc; | ||||
| use tauri::Manager; | ||||
| use std::{path::PathBuf, sync::Arc}; | ||||
| use tauri::{Manager, Wry}; | ||||
| use tauri_plugin_log::LogTarget; | ||||
| use tauri_plugin_store::{with_store, StoreCollection}; | ||||
|  | ||||
| mod auth; | ||||
| mod commands; | ||||
| @@ -25,8 +27,24 @@ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let client_clone = client.clone(); | ||||
|     let app_handle = app.handle(); | ||||
|  | ||||
|     let stores = app.state::<StoreCollection<Wry>>(); | ||||
|     let path = PathBuf::from(".settings.dat"); | ||||
|     let _ = with_store(app_handle.clone(), stores, path, |store| { | ||||
|         let settings_data = store.get("SETTINGS_DATA"); | ||||
|         let custom_openssl = settings_data.map_or(false, |data| { | ||||
|             data["customOpenSSL"].as_bool().unwrap_or(false) | ||||
|         }); | ||||
|  | ||||
|         if custom_openssl { | ||||
|             info!("Using custom OpenSSL config"); | ||||
|             let openssl_conf = get_openssl_conf_path(&app_handle).into_os_string(); | ||||
|             std::env::set_var("OPENSSL_CONF", openssl_conf); | ||||
|         } | ||||
|         Ok(()) | ||||
|     }); | ||||
|  | ||||
|     tauri::async_runtime::spawn(async move { | ||||
|         let _ = client_clone.subscribe_status(move |client_status| match client_status { | ||||
|         client_clone.subscribe_status(move |client_status| match client_status { | ||||
|             ClientStatus::Vpn(vpn_status) => { | ||||
|                 let payload = VpnStatusPayload { status: vpn_status }; | ||||
|                 if let Err(err) = app_handle.emit_all("vpn-status-received", payload) { | ||||
| @@ -45,15 +63,12 @@ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|  | ||||
|     app.manage(client); | ||||
|  | ||||
|     match std::env::var("XDG_CURRENT_DESKTOP") { | ||||
|         Ok(desktop) => { | ||||
|             if desktop == "KDE" { | ||||
|                 if let Some(main_window) = app.get_window("main") { | ||||
|                     let _ = main_window.set_decorations(false); | ||||
|                 } | ||||
|     if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") { | ||||
|         if desktop == "KDE" { | ||||
|             if let Some(main_window) = app.get_window("main") { | ||||
|                 let _ = main_window.set_decorations(false); | ||||
|             } | ||||
|         } | ||||
|         Err(_) => (), | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| @@ -61,7 +76,6 @@ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|  | ||||
| fn main() { | ||||
|     // env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); | ||||
|  | ||||
|     tauri::Builder::default() | ||||
|         .plugin( | ||||
|             tauri_plugin_log::Builder::default() | ||||
| @@ -73,13 +87,17 @@ fn main() { | ||||
|                 .with_colors(Default::default()) | ||||
|                 .build(), | ||||
|         ) | ||||
|         .plugin(tauri_plugin_store::Builder::default().build()) | ||||
|         .setup(setup) | ||||
|         .invoke_handler(tauri::generate_handler![ | ||||
|             commands::service_online, | ||||
|             commands::vpn_status, | ||||
|             commands::vpn_connect, | ||||
|             commands::vpn_disconnect, | ||||
|             commands::saml_login | ||||
|             commands::saml_login, | ||||
|             commands::os_version, | ||||
|             commands::openssl_config, | ||||
|             commands::update_openssl_config, | ||||
|         ]) | ||||
|         .run(tauri::generate_context!()) | ||||
|         .expect("error while running tauri application"); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use log::{info, warn}; | ||||
| use std::time::Instant; | ||||
| use tauri::Window; | ||||
| use std::{path::PathBuf, time::Instant}; | ||||
| use tauri::{AppHandle, Window}; | ||||
| use tokio::sync::oneshot; | ||||
| use url::{form_urlencoded, Url}; | ||||
| use webkit2gtk::{ | ||||
| @@ -9,7 +9,7 @@ use webkit2gtk::{ | ||||
| }; | ||||
|  | ||||
| pub(crate) fn redact_url(url: &str) -> String { | ||||
|     if let Ok(mut url) = Url::parse(&url) { | ||||
|     if let Ok(mut url) = Url::parse(url) { | ||||
|         if let Err(err) = url.set_host(Some("redacted")) { | ||||
|             warn!("Error redacting URL: {}", err); | ||||
|         } | ||||
| @@ -20,7 +20,7 @@ pub(crate) fn redact_url(url: &str) -> String { | ||||
|             let redacted_query = redact_query(url.query().unwrap_or("")); | ||||
|             url.set_query(Some(&redacted_query)); | ||||
|         } | ||||
|         return url.to_string(); | ||||
|         url.to_string() | ||||
|     } else { | ||||
|         warn!("Error parsing URL: {}", url); | ||||
|         url.to_string() | ||||
| @@ -86,3 +86,40 @@ fn send_result(tx: oneshot::Sender<()>) { | ||||
|         warn!("Error sending clear cookies result"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) fn get_openssl_conf() -> String { | ||||
|     // OpenSSL version number format: 0xMNN00PP0L | ||||
|     // https://www.openssl.org/docs/man3.0/man3/OPENSSL_VERSION_NUMBER.html | ||||
|     let version_3_0_4: i64 = 0x30000040; | ||||
|     let openssl_version = openssl::version::number(); | ||||
|  | ||||
|     // See: https://stackoverflow.com/questions/75763525/curl-35-error0a000152ssl-routinesunsafe-legacy-renegotiation-disabled | ||||
|     let option = if openssl_version >= version_3_0_4 { | ||||
|         "UnsafeLegacyServerConnect" | ||||
|     } else { | ||||
|         "UnsafeLegacyRenegotiation" | ||||
|     }; | ||||
|  | ||||
|     format!( | ||||
|         "openssl_conf = openssl_init | ||||
|  | ||||
| [openssl_init] | ||||
| ssl_conf = ssl_sect | ||||
|  | ||||
| [ssl_sect] | ||||
| system_default = system_default_sect | ||||
|  | ||||
| [system_default_sect] | ||||
| Options = {}", | ||||
|         option | ||||
|     ) | ||||
| } | ||||
|  | ||||
| pub(crate) fn get_openssl_conf_path(app_handle: &AppHandle) -> PathBuf { | ||||
|     let app_dir = app_handle | ||||
|         .path_resolver() | ||||
|         .app_data_dir() | ||||
|         .expect("failed to resolve app dir"); | ||||
|  | ||||
|     app_dir.join("openssl.cnf") | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user