mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	upgrade gpauth
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "gpapi"
 | 
			
		||||
rust-version.workspace = true
 | 
			
		||||
version.workspace = true
 | 
			
		||||
edition.workspace = true
 | 
			
		||||
license = "MIT"
 | 
			
		||||
@@ -29,14 +30,11 @@ uzers.workspace = true
 | 
			
		||||
serde_urlencoded.workspace = true
 | 
			
		||||
md5.workspace = true
 | 
			
		||||
sha256.workspace = true
 | 
			
		||||
which.workspace = true
 | 
			
		||||
 | 
			
		||||
tauri = { workspace = true, optional = true }
 | 
			
		||||
clap = { workspace = true, optional = true }
 | 
			
		||||
open = { version = "5", optional = true }
 | 
			
		||||
webbrowser = { version = "1", optional = true }
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
tauri = ["dep:tauri"]
 | 
			
		||||
clap = ["dep:clap"]
 | 
			
		||||
browser-auth = ["dep:open", "dep:webbrowser"]
 | 
			
		||||
webview-auth = []
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,14 @@
 | 
			
		||||
use std::borrow::{Borrow, Cow};
 | 
			
		||||
 | 
			
		||||
use anyhow::bail;
 | 
			
		||||
use log::{info, warn};
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{error::AuthDataParseError, utils::base64::decode_to_string};
 | 
			
		||||
 | 
			
		||||
pub type AuthDataParseResult = anyhow::Result<SamlAuthData, AuthDataParseError>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct SamlAuthData {
 | 
			
		||||
@@ -33,33 +36,51 @@ impl SamlAuthResult {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SamlAuthData {
 | 
			
		||||
  pub fn new(username: String, prelogin_cookie: Option<String>, portal_userauthcookie: Option<String>) -> Self {
 | 
			
		||||
    Self {
 | 
			
		||||
      username,
 | 
			
		||||
      prelogin_cookie,
 | 
			
		||||
      portal_userauthcookie,
 | 
			
		||||
      token: None,
 | 
			
		||||
  pub fn new(
 | 
			
		||||
    username: Option<String>,
 | 
			
		||||
    prelogin_cookie: Option<String>,
 | 
			
		||||
    portal_userauthcookie: Option<String>,
 | 
			
		||||
  ) -> anyhow::Result<Self> {
 | 
			
		||||
    let username = username.unwrap_or_default();
 | 
			
		||||
    if username.is_empty() {
 | 
			
		||||
      bail!("Invalid username: <empty>");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let prelogin_cookie = prelogin_cookie.unwrap_or_default();
 | 
			
		||||
    let portal_userauthcookie = portal_userauthcookie.unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
    if prelogin_cookie.len() <= 5 && portal_userauthcookie.len() <= 5 {
 | 
			
		||||
      bail!(
 | 
			
		||||
        "Invalid prelogin-cookie: {}, portal-userauthcookie: {}",
 | 
			
		||||
        prelogin_cookie,
 | 
			
		||||
        portal_userauthcookie
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Self {
 | 
			
		||||
      username,
 | 
			
		||||
      prelogin_cookie: Some(prelogin_cookie),
 | 
			
		||||
      portal_userauthcookie: Some(portal_userauthcookie),
 | 
			
		||||
      token: None,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn from_html(html: &str) -> anyhow::Result<SamlAuthData, AuthDataParseError> {
 | 
			
		||||
  pub fn from_html(html: &str) -> AuthDataParseResult {
 | 
			
		||||
    match parse_xml_tag(html, "saml-auth-status") {
 | 
			
		||||
      Some(saml_status) if saml_status == "1" => {
 | 
			
		||||
      Some(status) if status == "1" => {
 | 
			
		||||
        let username = parse_xml_tag(html, "saml-username");
 | 
			
		||||
        let prelogin_cookie = parse_xml_tag(html, "prelogin-cookie");
 | 
			
		||||
        let portal_userauthcookie = parse_xml_tag(html, "portal-userauthcookie");
 | 
			
		||||
 | 
			
		||||
        if SamlAuthData::check(&username, &prelogin_cookie, &portal_userauthcookie) {
 | 
			
		||||
          Ok(SamlAuthData::new(
 | 
			
		||||
            username.unwrap(),
 | 
			
		||||
            prelogin_cookie,
 | 
			
		||||
            portal_userauthcookie,
 | 
			
		||||
          ))
 | 
			
		||||
        } else {
 | 
			
		||||
          Err(AuthDataParseError::Invalid)
 | 
			
		||||
        }
 | 
			
		||||
        SamlAuthData::new(username, prelogin_cookie, portal_userauthcookie).map_err(|e| {
 | 
			
		||||
          warn!("Failed to parse auth data: {}", e);
 | 
			
		||||
          AuthDataParseError::Invalid
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      Some(status) => {
 | 
			
		||||
        warn!("Found invalid auth status: {}", status);
 | 
			
		||||
        Err(AuthDataParseError::Invalid)
 | 
			
		||||
      }
 | 
			
		||||
      Some(_) => Err(AuthDataParseError::Invalid),
 | 
			
		||||
      None => Err(AuthDataParseError::NotFound),
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -105,27 +126,6 @@ impl SamlAuthData {
 | 
			
		||||
  pub fn token(&self) -> Option<&str> {
 | 
			
		||||
    self.token.as_deref()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn check(
 | 
			
		||||
    username: &Option<String>,
 | 
			
		||||
    prelogin_cookie: &Option<String>,
 | 
			
		||||
    portal_userauthcookie: &Option<String>,
 | 
			
		||||
  ) -> bool {
 | 
			
		||||
    let username_valid = username.as_ref().is_some_and(|username| !username.is_empty());
 | 
			
		||||
    let prelogin_cookie_valid = prelogin_cookie.as_ref().is_some_and(|val| val.len() > 5);
 | 
			
		||||
    let portal_userauthcookie_valid = portal_userauthcookie.as_ref().is_some_and(|val| val.len() > 5);
 | 
			
		||||
 | 
			
		||||
    let is_valid = username_valid && (prelogin_cookie_valid || portal_userauthcookie_valid);
 | 
			
		||||
 | 
			
		||||
    if !is_valid {
 | 
			
		||||
      warn!(
 | 
			
		||||
        "Invalid SAML auth data: username: {:?}, prelogin-cookie: {:?}, portal-userauthcookie: {:?}",
 | 
			
		||||
        username, prelogin_cookie, portal_userauthcookie
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    is_valid
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_xml_tag(html: &str, tag: &str) -> Option<String> {
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,28 @@
 | 
			
		||||
use crate::error::PortalError;
 | 
			
		||||
 | 
			
		||||
pub mod args;
 | 
			
		||||
 | 
			
		||||
pub trait Args {
 | 
			
		||||
  fn fix_openssl(&self) -> bool;
 | 
			
		||||
  fn ignore_tls_errors(&self) -> bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn handle_error(err: anyhow::Error, args: &impl Args) {
 | 
			
		||||
  eprintln!("\nError: {}", err);
 | 
			
		||||
 | 
			
		||||
  let Some(err) = err.downcast_ref::<PortalError>() else {
 | 
			
		||||
    return;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if err.is_legacy_openssl_error() && !args.fix_openssl() {
 | 
			
		||||
    eprintln!("\nRe-run it with the `--fix-openssl` option to work around this issue, e.g.:\n");
 | 
			
		||||
    let args = std::env::args().collect::<Vec<_>>();
 | 
			
		||||
    eprintln!("{} --fix-openssl {}\n", args[0], args[1..].join(" "));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if err.is_tls_error() && !args.ignore_tls_errors() {
 | 
			
		||||
    eprintln!("\nRe-run it with the `--ignore-tls-errors` option to ignore the certificate error, e.g.:\n");
 | 
			
		||||
    let args = std::env::args().collect::<Vec<_>>();
 | 
			
		||||
    eprintln!("{} --ignore-tls-errors {}\n", args[0], args[1..].join(" "));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,19 @@ pub enum PortalError {
 | 
			
		||||
  #[error("Portal config error: {0}")]
 | 
			
		||||
  ConfigError(String),
 | 
			
		||||
  #[error("Network error: {0}")]
 | 
			
		||||
  NetworkError(String),
 | 
			
		||||
  NetworkError(#[from] reqwest::Error),
 | 
			
		||||
  #[error("TLS error")]
 | 
			
		||||
  TlsError,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PortalError {
 | 
			
		||||
  pub fn is_legacy_openssl_error(&self) -> bool {
 | 
			
		||||
    format!("{:?}", self).contains("unsafe legacy renegotiation")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn is_tls_error(&self) -> bool {
 | 
			
		||||
    matches!(self, PortalError::TlsError) || format!("{:?}", self).contains("certificate verify failed")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
@@ -17,3 +29,9 @@ pub enum AuthDataParseError {
 | 
			
		||||
  #[error("Invalid auth data")]
 | 
			
		||||
  Invalid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AuthDataParseError {
 | 
			
		||||
  pub fn is_invalid(&self) -> bool {
 | 
			
		||||
    matches!(self, AuthDataParseError::Invalid)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ pub async fn gateway_login(gateway: &str, cred: &Credential, gp_params: &GpParam
 | 
			
		||||
    .form(¶ms)
 | 
			
		||||
    .send()
 | 
			
		||||
    .await
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e.to_string())))?;
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e)))?;
 | 
			
		||||
 | 
			
		||||
  let res = parse_gp_response(res).await.map_err(|err| {
 | 
			
		||||
    warn!("{err}");
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ pub const GP_API_KEY: &[u8; 32] = &[0; 32];
 | 
			
		||||
 | 
			
		||||
pub const GP_USER_AGENT: &str = "PAN GlobalProtect";
 | 
			
		||||
pub const GP_SERVICE_LOCK_FILE: &str = "/var/run/gpservice.lock";
 | 
			
		||||
pub const GP_CALLBACK_PORT_FILENAME: &str = "gpcallback.port";
 | 
			
		||||
 | 
			
		||||
#[cfg(not(debug_assertions))]
 | 
			
		||||
pub const GP_CLIENT_BINARY: &str = "/usr/bin/gpclient";
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ pub async fn retrieve_config(portal: &str, cred: &Credential, gp_params: &GpPara
 | 
			
		||||
    .form(¶ms)
 | 
			
		||||
    .send()
 | 
			
		||||
    .await
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e.to_string())))?;
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e)))?;
 | 
			
		||||
 | 
			
		||||
  let res_xml = parse_gp_response(res).await.or_else(|err| {
 | 
			
		||||
    if err.status == StatusCode::NOT_FOUND {
 | 
			
		||||
 
 | 
			
		||||
@@ -116,14 +116,12 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prel
 | 
			
		||||
 | 
			
		||||
  let client = Client::try_from(gp_params)?;
 | 
			
		||||
 | 
			
		||||
  info!("Perform prelogin, user_agent: {}", gp_params.user_agent());
 | 
			
		||||
 | 
			
		||||
  let res = client
 | 
			
		||||
    .post(&prelogin_url)
 | 
			
		||||
    .form(¶ms)
 | 
			
		||||
    .send()
 | 
			
		||||
    .await
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e.to_string())))?;
 | 
			
		||||
    .map_err(|e| anyhow::anyhow!(PortalError::NetworkError(e)))?;
 | 
			
		||||
 | 
			
		||||
  let res_xml = parse_gp_response(res).await.or_else(|err| {
 | 
			
		||||
    if err.status == StatusCode::NOT_FOUND {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,13 @@ pub struct SamlAuthLauncher<'a> {
 | 
			
		||||
  user_agent: Option<&'a str>,
 | 
			
		||||
  os: Option<&'a str>,
 | 
			
		||||
  os_version: Option<&'a str>,
 | 
			
		||||
  hidpi: bool,
 | 
			
		||||
  fix_openssl: bool,
 | 
			
		||||
  ignore_tls_errors: bool,
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  hidpi: bool,
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  clean: bool,
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  default_browser: bool,
 | 
			
		||||
  browser: Option<&'a str>,
 | 
			
		||||
}
 | 
			
		||||
@@ -31,10 +34,13 @@ impl<'a> SamlAuthLauncher<'a> {
 | 
			
		||||
      user_agent: None,
 | 
			
		||||
      os: None,
 | 
			
		||||
      os_version: None,
 | 
			
		||||
      hidpi: false,
 | 
			
		||||
      fix_openssl: false,
 | 
			
		||||
      ignore_tls_errors: false,
 | 
			
		||||
      #[cfg(feature = "webview-auth")]
 | 
			
		||||
      hidpi: false,
 | 
			
		||||
      #[cfg(feature = "webview-auth")]
 | 
			
		||||
      clean: false,
 | 
			
		||||
      #[cfg(feature = "webview-auth")]
 | 
			
		||||
      default_browser: false,
 | 
			
		||||
      browser: None,
 | 
			
		||||
    }
 | 
			
		||||
@@ -65,11 +71,6 @@ impl<'a> SamlAuthLauncher<'a> {
 | 
			
		||||
    self
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn hidpi(mut self, hidpi: bool) -> Self {
 | 
			
		||||
    self.hidpi = hidpi;
 | 
			
		||||
    self
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn fix_openssl(mut self, fix_openssl: bool) -> Self {
 | 
			
		||||
    self.fix_openssl = fix_openssl;
 | 
			
		||||
    self
 | 
			
		||||
@@ -80,11 +81,19 @@ impl<'a> SamlAuthLauncher<'a> {
 | 
			
		||||
    self
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  pub fn hidpi(mut self, hidpi: bool) -> Self {
 | 
			
		||||
    self.hidpi = hidpi;
 | 
			
		||||
    self
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  pub fn clean(mut self, clean: bool) -> Self {
 | 
			
		||||
    self.clean = clean;
 | 
			
		||||
    self
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[cfg(feature = "webview-auth")]
 | 
			
		||||
  pub fn default_browser(mut self, default_browser: bool) -> Self {
 | 
			
		||||
    self.default_browser = default_browser;
 | 
			
		||||
    self
 | 
			
		||||
@@ -120,10 +129,6 @@ impl<'a> SamlAuthLauncher<'a> {
 | 
			
		||||
      auth_cmd.arg("--os-version").arg(os_version);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if self.hidpi {
 | 
			
		||||
      auth_cmd.arg("--hidpi");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if self.fix_openssl {
 | 
			
		||||
      auth_cmd.arg("--fix-openssl");
 | 
			
		||||
    }
 | 
			
		||||
@@ -132,12 +137,19 @@ impl<'a> SamlAuthLauncher<'a> {
 | 
			
		||||
      auth_cmd.arg("--ignore-tls-errors");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if self.clean {
 | 
			
		||||
      auth_cmd.arg("--clean");
 | 
			
		||||
    }
 | 
			
		||||
    #[cfg(feature = "webview-auth")]
 | 
			
		||||
    {
 | 
			
		||||
      if self.hidpi {
 | 
			
		||||
        auth_cmd.arg("--hidpi");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    if self.default_browser {
 | 
			
		||||
      auth_cmd.arg("--default-browser");
 | 
			
		||||
      if self.clean {
 | 
			
		||||
        auth_cmd.arg("--clean");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if self.default_browser {
 | 
			
		||||
        auth_cmd.arg("--default-browser");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(browser) = self.browser {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
use std::{borrow::Cow, env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt};
 | 
			
		||||
 | 
			
		||||
use anyhow::bail;
 | 
			
		||||
use log::{info, warn};
 | 
			
		||||
 | 
			
		||||
pub struct BrowserAuthenticator<'a> {
 | 
			
		||||
  auth_request: &'a str,
 | 
			
		||||
  browser: Option<&'a str>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BrowserAuthenticator<'_> {
 | 
			
		||||
  pub fn new(auth_request: &str) -> BrowserAuthenticator {
 | 
			
		||||
    BrowserAuthenticator {
 | 
			
		||||
      auth_request,
 | 
			
		||||
      browser: None,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn new_with_browser<'a>(auth_request: &'a str, browser: &'a str) -> BrowserAuthenticator<'a> {
 | 
			
		||||
    let browser = browser.trim();
 | 
			
		||||
    BrowserAuthenticator {
 | 
			
		||||
      auth_request,
 | 
			
		||||
      browser: if browser.is_empty() || browser == "default" {
 | 
			
		||||
        None
 | 
			
		||||
      } else {
 | 
			
		||||
        Some(browser)
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn authenticate(&self) -> anyhow::Result<()> {
 | 
			
		||||
    let path = if self.auth_request.starts_with("http") {
 | 
			
		||||
      Cow::Borrowed(self.auth_request)
 | 
			
		||||
    } else {
 | 
			
		||||
      let html_file = temp_dir().join("gpauth.html");
 | 
			
		||||
 | 
			
		||||
      // Remove the file and error if permission denied
 | 
			
		||||
      if let Err(err) = fs::remove_file(&html_file) {
 | 
			
		||||
        if err.kind() != std::io::ErrorKind::NotFound {
 | 
			
		||||
          warn!("Failed to remove the temporary file: {}", err);
 | 
			
		||||
          bail!("Please remove the file manually: {:?}", html_file);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let mut file = fs::File::create(&html_file)?;
 | 
			
		||||
 | 
			
		||||
      file.set_permissions(fs::Permissions::from_mode(0o600))?;
 | 
			
		||||
      file.write_all(self.auth_request.as_bytes())?;
 | 
			
		||||
 | 
			
		||||
      Cow::Owned(html_file.to_string_lossy().to_string())
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Some(browser) = self.browser {
 | 
			
		||||
      let app = find_browser_path(browser);
 | 
			
		||||
 | 
			
		||||
      info!("Launching browser: {}", app);
 | 
			
		||||
      open::with_detached(path.as_ref(), app)?;
 | 
			
		||||
    } else {
 | 
			
		||||
      info!("Launching the default browser...");
 | 
			
		||||
      webbrowser::open(path.as_ref())?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn find_browser_path(browser: &str) -> String {
 | 
			
		||||
  if browser == "chrome" {
 | 
			
		||||
    which::which("google-chrome-stable")
 | 
			
		||||
      .or_else(|_| which::which("google-chrome"))
 | 
			
		||||
      .or_else(|_| which::which("chromium"))
 | 
			
		||||
      .map(|path| path.to_string_lossy().to_string())
 | 
			
		||||
      .unwrap_or_else(|_| browser.to_string())
 | 
			
		||||
  } else {
 | 
			
		||||
    browser.into()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,8 +2,6 @@ pub(crate) mod command_traits;
 | 
			
		||||
pub(crate) mod gui_helper_launcher;
 | 
			
		||||
 | 
			
		||||
pub mod auth_launcher;
 | 
			
		||||
#[cfg(feature = "browser-auth")]
 | 
			
		||||
pub mod browser_authenticator;
 | 
			
		||||
pub mod gui_launcher;
 | 
			
		||||
pub mod hip_launcher;
 | 
			
		||||
pub mod service_launcher;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,4 @@ use super::vpn_state::VpnState;
 | 
			
		||||
pub enum WsEvent {
 | 
			
		||||
  VpnState(VpnState),
 | 
			
		||||
  ActiveGui,
 | 
			
		||||
  /// External authentication data
 | 
			
		||||
  AuthData(String),
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,12 @@ use tokio::process::Command;
 | 
			
		||||
 | 
			
		||||
pub trait WindowExt {
 | 
			
		||||
  fn raise(&self) -> anyhow::Result<()>;
 | 
			
		||||
  fn hide_menu(&self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WindowExt for WebviewWindow {
 | 
			
		||||
  fn raise(&self) -> anyhow::Result<()> {
 | 
			
		||||
    raise_window(self)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn hide_menu(&self) {
 | 
			
		||||
    hide_menu(self);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn raise_window(win: &WebviewWindow) -> anyhow::Result<()> {
 | 
			
		||||
@@ -40,7 +35,7 @@ pub fn raise_window(win: &WebviewWindow) -> anyhow::Result<()> {
 | 
			
		||||
 | 
			
		||||
  // Calling window.show() on Windows will cause the menu to be shown.
 | 
			
		||||
  // We need to hide it again.
 | 
			
		||||
  hide_menu(win);
 | 
			
		||||
  win.hide_menu()?;
 | 
			
		||||
 | 
			
		||||
  Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -76,22 +71,3 @@ async fn wmctrl_try_raise_window(title: &str) -> anyhow::Result<ExitStatus> {
 | 
			
		||||
 | 
			
		||||
  Ok(exit_status)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn hide_menu(win: &WebviewWindow) {
 | 
			
		||||
  // let menu_handle = win.menu_handle();
 | 
			
		||||
 | 
			
		||||
  // tokio::spawn(async move {
 | 
			
		||||
  //   loop {
 | 
			
		||||
  //     let menu_visible = menu_handle.is_visible().unwrap_or(false);
 | 
			
		||||
 | 
			
		||||
  //     if !menu_visible {
 | 
			
		||||
  //       break;
 | 
			
		||||
  //     }
 | 
			
		||||
 | 
			
		||||
  //     if menu_visible {
 | 
			
		||||
  //       let _ = menu_handle.hide();
 | 
			
		||||
  //       tokio::time::sleep(Duration::from_millis(10)).await;
 | 
			
		||||
  //     }
 | 
			
		||||
  //   }
 | 
			
		||||
  // });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user