mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			v2.4.2
			...
			gpauth_win
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3175d1083a | ||
|  | fe3d3df662 | ||
|  | 2f90b73683 | ||
|  | 5186e80c6f | ||
|  | 4ff1c1dc1f | ||
|  | c1427040f6 | 
							
								
								
									
										63
									
								
								.github/workflows/build.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								.github/workflows/build.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -44,7 +44,8 @@ jobs: | |||||||
|       with: |       with: | ||||||
|         version: 9 |         version: 9 | ||||||
|     - name: Prepare workspace |     - name: Prepare workspace | ||||||
|       run: rm -rf source && mkdir source |       run: rm -rf source && mkdir -p source/artifacts | ||||||
|  |  | ||||||
|     - name: Checkout GlobalProtect-openconnect |     - name: Checkout GlobalProtect-openconnect | ||||||
|       uses: actions/checkout@v4 |       uses: actions/checkout@v4 | ||||||
|       with: |       with: | ||||||
| @@ -52,6 +53,7 @@ jobs: | |||||||
|         repository: yuezk/GlobalProtect-openconnect |         repository: yuezk/GlobalProtect-openconnect | ||||||
|         ref: ${{ github.ref }} |         ref: ${{ github.ref }} | ||||||
|         path: source/gp |         path: source/gp | ||||||
|  |  | ||||||
|     - name: Create tarball |     - name: Create tarball | ||||||
|       run: | |       run: | | ||||||
|         cd source/gp |         cd source/gp | ||||||
| @@ -60,13 +62,69 @@ jobs: | |||||||
|           touch SNAPSHOT |           touch SNAPSHOT | ||||||
|         fi |         fi | ||||||
|         make tarball |         make tarball | ||||||
|  |  | ||||||
|  |         mv -v .build/tarball/*.tar.gz ../artifacts/ | ||||||
|  |  | ||||||
|  |     - name: Generate RPM spec file | ||||||
|  |       env: | ||||||
|  |         RELEASE_TAG: ${{ github.ref == 'refs/heads/dev' && 'snapshot' || github.ref_name }} | ||||||
|  |       run: | | ||||||
|  |         cd source/gp | ||||||
|  |  | ||||||
|  |         make init-rpm \ | ||||||
|  |           REVISION='1%{?dist}' \ | ||||||
|  |           RPM_SOURCE=https://github.com/yuezk/GlobalProtect-openconnect/releases/download/${RELEASE_TAG}/%{name}-%{version}.tar.gz | ||||||
|  |  | ||||||
|  |         mv -v .build/rpm/*.spec ../artifacts/ | ||||||
|  |  | ||||||
|     - name: Upload tarball |     - name: Upload tarball | ||||||
|       uses: actions/upload-artifact@v4 |       uses: actions/upload-artifact@v4 | ||||||
|       with: |       with: | ||||||
|         name: artifact-source |         name: artifact-source | ||||||
|         if-no-files-found: error |         if-no-files-found: error | ||||||
|         path: | |         path: | | ||||||
|           source/gp/.build/tarball/*.tar.gz |           source/artifacts/* | ||||||
|  |  | ||||||
|  |   tarball-offline: | ||||||
|  |     if: ${{ github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/') }} | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     needs: | ||||||
|  |     - tarball | ||||||
|  |     steps: | ||||||
|  |     - uses: pnpm/action-setup@v4 | ||||||
|  |       with: | ||||||
|  |         version: 9 | ||||||
|  |  | ||||||
|  |     - name: Prepare workspace | ||||||
|  |       run: rm -rf source-offline && mkdir source-offline | ||||||
|  |  | ||||||
|  |     - name: Download tarball | ||||||
|  |       uses: actions/download-artifact@v4 | ||||||
|  |       with: | ||||||
|  |         name: artifact-source | ||||||
|  |         path: source-offline | ||||||
|  |  | ||||||
|  |     - name: Create offline tarball | ||||||
|  |       run: | | ||||||
|  |         cd source-offline | ||||||
|  |  | ||||||
|  |         offline_tarball=$(basename *.tar.gz .tar.gz).offline.tar.gz | ||||||
|  |  | ||||||
|  |         # Extract the tarball | ||||||
|  |         tar -xzf *.tar.gz | ||||||
|  |  | ||||||
|  |         cd */ | ||||||
|  |         make tarball OFFLINE=1 | ||||||
|  |  | ||||||
|  |         # Rename the tarball to .offline.tar.gz | ||||||
|  |         mv -v .build/tarball/*.tar.gz ../$offline_tarball | ||||||
|  |  | ||||||
|  |     - name: Upload offline tarball | ||||||
|  |       uses: actions/upload-artifact@v4 | ||||||
|  |       with: | ||||||
|  |         path: source-offline/*.offline.tar.gz | ||||||
|  |         name: artifact-source-offline | ||||||
|  |         if-no-files-found: error | ||||||
|  |  | ||||||
|   build-gp: |   build-gp: | ||||||
|     needs: |     needs: | ||||||
| @@ -168,6 +226,7 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     needs: |     needs: | ||||||
|       - tarball |       - tarball | ||||||
|  |       - tarball-offline | ||||||
|       - build-gp |       - build-gp | ||||||
|       - build-gpgui |       - build-gpgui | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								.github/workflows/publish.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/publish.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -52,22 +52,26 @@ jobs: | |||||||
|         version: 9 |         version: 9 | ||||||
|     - name: Prepare workspace |     - name: Prepare workspace | ||||||
|       run: rm -rf publish-ppa && mkdir publish-ppa |       run: rm -rf publish-ppa && mkdir publish-ppa | ||||||
|     - name: Download ${{ inputs.tag }} source code |     - name: Download ${{ inputs.tag }} offline source code | ||||||
|       uses: robinraju/release-downloader@v1.9 |       env: | ||||||
|       with: |         GH_TOKEN: ${{ secrets.GH_PAT }} | ||||||
|         token: ${{ secrets.GH_PAT }} |       run: | | ||||||
|         tag: ${{ inputs.tag }} |         gh -R yuezk/GlobalProtect-openconnect \ | ||||||
|         fileName: globalprotect-openconnect-*.tar.gz |           release download ${{ inputs.tag }} \ | ||||||
|         tarBall: false |           --pattern '*.offline.tar.gz' \ | ||||||
|         zipBall: false |           --dir publish-ppa | ||||||
|         out-file-path: publish-ppa |     - name: Patch the source code | ||||||
|     - name: Make the offline tarball |  | ||||||
|       run: | |       run: | | ||||||
|         cd publish-ppa |         cd publish-ppa | ||||||
|         tar -xf globalprotect-openconnect-*.tar.gz |  | ||||||
|         cd globalprotect-openconnect-*/ |  | ||||||
|  |  | ||||||
|         make tarball OFFLINE=1 |         # Rename the source tarball without the offline suffix | ||||||
|  |         mv -v *.tar.gz $(basename *.tar.gz .offline.tar.gz).tar.gz | ||||||
|  |  | ||||||
|  |         # Extract the source tarball | ||||||
|  |         tar -xzf *.tar.gz | ||||||
|  |  | ||||||
|  |         # Prepare the debian directory with custom files | ||||||
|  |         cd globalprotect-openconnect-*/ | ||||||
|  |  | ||||||
|         # Prepare the debian directory with custom files |         # Prepare the debian directory with custom files | ||||||
|         mkdir -p .build/debian |         mkdir -p .build/debian | ||||||
| @@ -78,7 +82,6 @@ jobs: | |||||||
|         cp -v packaging/deb/postrm .build/debian/postrm |         cp -v packaging/deb/postrm .build/debian/postrm | ||||||
|  |  | ||||||
|         sed -i "s/@RUST@/cargo-1.80/g" .build/debian/control |         sed -i "s/@RUST@/cargo-1.80/g" .build/debian/control | ||||||
|  |  | ||||||
|         sed -i "s/@OFFLINE@/1/g" .build/debian/rules |         sed -i "s/@OFFLINE@/1/g" .build/debian/rules | ||||||
|         sed -i "s/@BUILD_GUI@/1/g" .build/debian/rules |         sed -i "s/@BUILD_GUI@/1/g" .build/debian/rules | ||||||
|         sed -i "s/@RUST_VERSION@/1.80/g" .build/debian/rules |         sed -i "s/@RUST_VERSION@/1.80/g" .build/debian/rules | ||||||
| @@ -89,7 +92,7 @@ jobs: | |||||||
|         repository: "yuezk/globalprotect-openconnect" |         repository: "yuezk/globalprotect-openconnect" | ||||||
|         gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} |         gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} | ||||||
|         gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} |         gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} | ||||||
|         tarball: publish-ppa/globalprotect-openconnect-*/.build/tarball/*.tar.gz |         tarball: publish-ppa/globalprotect-openconnect-*.tar.gz | ||||||
|         debian_dir: publish-ppa/globalprotect-openconnect-*/.build/debian |         debian_dir: publish-ppa/globalprotect-openconnect-*/.build/debian | ||||||
|         deb_email: "k3vinyue@gmail.com" |         deb_email: "k3vinyue@gmail.com" | ||||||
|         deb_fullname: "Kevin Yue" |         deb_fullname: "Kevin Yue" | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -96,15 +96,16 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|     - name: Prepare workspace |     - name: Prepare workspace | ||||||
|       run: rm -rf build-${{ matrix.package }} && mkdir -p build-${{ matrix.package }} |       run: rm -rf build-${{ matrix.package }} && mkdir -p build-${{ matrix.package }} | ||||||
|  |  | ||||||
|     - name: Download ${{ inputs.tag }} source code |     - name: Download ${{ inputs.tag }} source code | ||||||
|       uses: robinraju/release-downloader@v1.9 |       env: | ||||||
|       with: |         GH_TOKEN: ${{ secrets.GH_PAT }} | ||||||
|         token: ${{ secrets.GH_PAT }} |       run: | | ||||||
|         tag: ${{ inputs.tag }} |         gh -R yuezk/GlobalProtect-openconnect \ | ||||||
|         fileName: globalprotect-openconnect-*.tar.gz |           release download ${{ inputs.tag }} \ | ||||||
|         tarBall: false |           --pattern '*[^offline].tar.gz' \ | ||||||
|         zipBall: false |           --dir build-${{ matrix.package }} | ||||||
|         out-file-path: build-${{ matrix.package }} |  | ||||||
|     - name: Docker Login |     - name: Docker Login | ||||||
|       run: echo ${{ secrets.DOCKER_HUB_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin |       run: echo ${{ secrets.DOCKER_HUB_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin | ||||||
|     - name: Build ${{ matrix.package }} package in Docker |     - name: Build ${{ matrix.package }} package in Docker | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -176,7 +176,7 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "auth" | name = "auth" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "block2", |  "block2", | ||||||
| @@ -188,6 +188,7 @@ dependencies = [ | |||||||
|  "objc2-web-kit", |  "objc2-web-kit", | ||||||
|  "open", |  "open", | ||||||
|  "regex", |  "regex", | ||||||
|  |  "serde_json", | ||||||
|  "tauri", |  "tauri", | ||||||
|  "tiny_http", |  "tiny_http", | ||||||
|  "tokio", |  "tokio", | ||||||
| @@ -195,7 +196,10 @@ dependencies = [ | |||||||
|  "uuid", |  "uuid", | ||||||
|  "webbrowser", |  "webbrowser", | ||||||
|  "webkit2gtk", |  "webkit2gtk", | ||||||
|  |  "webview2-com", | ||||||
|  "which", |  "which", | ||||||
|  |  "windows 0.58.0", | ||||||
|  |  "windows-core 0.58.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -642,7 +646,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "common" | name = "common" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "is_executable", |  "is_executable", | ||||||
| ] | ] | ||||||
| @@ -1590,7 +1594,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpapi" | name = "gpapi" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "base64 0.22.1", |  "base64 0.22.1", | ||||||
| @@ -1626,7 +1630,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpauth" | name = "gpauth" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "auth", |  "auth", | ||||||
| @@ -1645,7 +1649,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpclient" | name = "gpclient" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "clap", |  "clap", | ||||||
| @@ -1667,7 +1671,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpgui-helper" | name = "gpgui-helper" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "clap", |  "clap", | ||||||
| @@ -1685,7 +1689,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpservice" | name = "gpservice" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "axum", |  "axum", | ||||||
| @@ -2951,7 +2955,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "openconnect" | name = "openconnect" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cc", |  "cc", | ||||||
|  "common", |  "common", | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ members = [ | |||||||
|  |  | ||||||
| [workspace.package] | [workspace.package] | ||||||
| rust-version = "1.80" | rust-version = "1.80" | ||||||
| version = "2.4.2" | version = "2.4.3" | ||||||
| authors = ["Kevin Yue <k3vinyue@gmail.com>"] | authors = ["Kevin Yue <k3vinyue@gmail.com>"] | ||||||
| homepage = "https://github.com/yuezk/GlobalProtect-openconnect" | homepage = "https://github.com/yuezk/GlobalProtect-openconnect" | ||||||
| edition = "2021" | edition = "2021" | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @@ -8,6 +8,8 @@ RUST_VERSION = 1.80 | |||||||
|  |  | ||||||
| VERSION = $(shell $(CARGO) metadata --no-deps --format-version 1 | jq -r '.packages[0].version') | VERSION = $(shell $(CARGO) metadata --no-deps --format-version 1 | jq -r '.packages[0].version') | ||||||
| REVISION ?= 1 | REVISION ?= 1 | ||||||
|  | RPM_SOURCE ?= %{name}.tar.gz | ||||||
|  |  | ||||||
| PPA_REVISION ?= 1 | PPA_REVISION ?= 1 | ||||||
| PKG_NAME = globalprotect-openconnect | PKG_NAME = globalprotect-openconnect | ||||||
| PKG = $(PKG_NAME)-$(VERSION) | PKG = $(PKG_NAME)-$(VERSION) | ||||||
| @@ -234,6 +236,7 @@ init-rpm: clean-rpm | |||||||
|  |  | ||||||
| 	sed -i "s/@VERSION@/$(VERSION)/g" .build/rpm/globalprotect-openconnect.spec | 	sed -i "s/@VERSION@/$(VERSION)/g" .build/rpm/globalprotect-openconnect.spec | ||||||
| 	sed -i "s/@REVISION@/$(REVISION)/g" .build/rpm/globalprotect-openconnect.spec | 	sed -i "s/@REVISION@/$(REVISION)/g" .build/rpm/globalprotect-openconnect.spec | ||||||
|  | 	sed -i "s|@SOURCE@|$(RPM_SOURCE)|g" .build/rpm/globalprotect-openconnect.spec | ||||||
| 	sed -i "s/@OFFLINE@/$(OFFLINE)/g" .build/rpm/globalprotect-openconnect.spec | 	sed -i "s/@OFFLINE@/$(OFFLINE)/g" .build/rpm/globalprotect-openconnect.spec | ||||||
| 	sed -i "s/@DATE@/$(shell LC_ALL=en.US date "+%a %b %d %Y")/g" .build/rpm/globalprotect-openconnect.spec | 	sed -i "s/@DATE@/$(shell LC_ALL=en.US date "+%a %b %d %Y")/g" .build/rpm/globalprotect-openconnect.spec | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ The GUI version is also available after you installed it. You can launch it from | |||||||
|  |  | ||||||
| ### Debian/Ubuntu based distributions | ### Debian/Ubuntu based distributions | ||||||
|  |  | ||||||
| #### Install from PPA (Ubuntu > 18.04) | #### Install from PPA | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| sudo add-apt-repository ppa:yuezk/globalprotect-openconnect | sudo add-apt-repository ppa:yuezk/globalprotect-openconnect | ||||||
| @@ -81,10 +81,6 @@ sudo apt-get install globalprotect-openconnect | |||||||
| > | > | ||||||
| > For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`. | > For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`. | ||||||
|  |  | ||||||
| #### **Ubuntu 18.04** |  | ||||||
|  |  | ||||||
| The latest package is not available in the PPA, but you still needs to add the `ppa:yuezk/globalprotect-openconnect` repo beforehand to use the required `openconnect` package. Then you can follow the [Install from deb package](#install-from-deb-package) section to install the latest package. |  | ||||||
|  |  | ||||||
| #### Install from deb package | #### Install from deb package | ||||||
|  |  | ||||||
| Download the latest deb package from [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page. Then install it with `apt`: | Download the latest deb package from [releases](https://github.com/yuezk/GlobalProtect-openconnect/releases) page. Then install it with `apt`: | ||||||
|   | |||||||
| @@ -1,17 +1,21 @@ | |||||||
| use std::{env::temp_dir, fs::File}; | use std::{env::temp_dir, fs::File, str::FromStr}; | ||||||
|  |  | ||||||
|  | use anyhow::bail; | ||||||
| use clap::{Parser, Subcommand}; | use clap::{Parser, Subcommand}; | ||||||
| use gpapi::{ | use gpapi::{ | ||||||
|   clap::{handle_error, Args, InfoLevelVerbosity}, |   clap::{handle_error, Args, InfoLevelVerbosity}, | ||||||
|   utils::openssl, |   utils::openssl, | ||||||
| }; | }; | ||||||
| use log::info; | use log::info; | ||||||
|  | use sysinfo::{Pid, System}; | ||||||
| use tempfile::NamedTempFile; | use tempfile::NamedTempFile; | ||||||
|  | use tokio::fs; | ||||||
|  |  | ||||||
| use crate::{ | use crate::{ | ||||||
|   connect::{ConnectArgs, ConnectHandler}, |   connect::{ConnectArgs, ConnectHandler}, | ||||||
|   disconnect::{DisconnectArgs, DisconnectHandler}, |   disconnect::{DisconnectArgs, DisconnectHandler}, | ||||||
|   launch_gui::{LaunchGuiArgs, LaunchGuiHandler}, |   launch_gui::{LaunchGuiArgs, LaunchGuiHandler}, | ||||||
|  |   GP_CLIENT_LOCK_FILE, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")"); | const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")"); | ||||||
| @@ -77,6 +81,25 @@ impl Args for Cli { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl Cli { | impl Cli { | ||||||
|  |   async fn is_running(&self) -> bool { | ||||||
|  |     let Ok(c) = fs::read_to_string(GP_CLIENT_LOCK_FILE).await else { | ||||||
|  |       return false; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let Ok(pid) = Pid::from_str(c.trim()) else { | ||||||
|  |       return false; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let s = System::new_all(); | ||||||
|  |     let Some(p) = s.process(pid) else { | ||||||
|  |       return false; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     p.exe() | ||||||
|  |       .map(|exe| exe.to_string_lossy().contains("gpclient")) | ||||||
|  |       .unwrap_or(false) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   fn fix_openssl(&self) -> anyhow::Result<Option<NamedTempFile>> { |   fn fix_openssl(&self) -> anyhow::Result<Option<NamedTempFile>> { | ||||||
|     if self.fix_openssl { |     if self.fix_openssl { | ||||||
|       let file = openssl::fix_openssl_env()?; |       let file = openssl::fix_openssl_env()?; | ||||||
| @@ -87,6 +110,11 @@ impl Cli { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   async fn run(&self) -> anyhow::Result<()> { |   async fn run(&self) -> anyhow::Result<()> { | ||||||
|  |     // check if an instance is running | ||||||
|  |     if self.is_running().await { | ||||||
|  |       bail!("Another instance of the client is already running"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // The temp file will be dropped automatically when the file handle is dropped |     // The temp file will be dropped automatically when the file handle is dropped | ||||||
|     // So, declare it here to ensure it's not dropped |     // So, declare it here to ensure it's not dropped | ||||||
|     let _file = self.fix_openssl()?; |     let _file = self.fix_openssl()?; | ||||||
|   | |||||||
| @@ -87,8 +87,8 @@ pub(crate) struct ConnectArgs { | |||||||
|   #[arg(long, value_enum, default_value_t = ConnectArgs::default_os())] |   #[arg(long, value_enum, default_value_t = ConnectArgs::default_os())] | ||||||
|   os: Os, |   os: Os, | ||||||
|  |  | ||||||
|   #[arg(long, default_value_t = ConnectArgs::default_os_version())] |   #[arg(long, help = "If not specified, it will be computed based on the --os option")] | ||||||
|   os_version: String, |   os_version: Option<String>, | ||||||
|  |  | ||||||
|   #[arg(long, help = "Disable DTLS and ESP")] |   #[arg(long, help = "Disable DTLS and ESP")] | ||||||
|   no_dtls: bool, |   no_dtls: bool, | ||||||
| @@ -121,8 +121,12 @@ impl ConnectArgs { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   fn default_os_version() -> String { |   fn os_version(&self) -> String { | ||||||
|     match ConnectArgs::default_os() { |     if let Some(os_version) = self.os_version.as_deref() { | ||||||
|  |       return os_version.to_string(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     match self.os { | ||||||
|       Os::Linux => format!("Linux {}", whoami::distro()), |       Os::Linux => format!("Linux {}", whoami::distro()), | ||||||
|       Os::Windows => String::from("Microsoft Windows 11 Pro , 64-bit"), |       Os::Windows => String::from("Microsoft Windows 11 Pro , 64-bit"), | ||||||
|       Os::Mac => String::from("Apple Mac OS X 13.4.0"), |       Os::Mac => String::from("Apple Mac OS X 13.4.0"), | ||||||
| @@ -149,7 +153,7 @@ impl<'a> ConnectHandler<'a> { | |||||||
|     GpParams::builder() |     GpParams::builder() | ||||||
|       .user_agent(&self.args.user_agent) |       .user_agent(&self.args.user_agent) | ||||||
|       .client_os(ClientOs::from(&self.args.os)) |       .client_os(ClientOs::from(&self.args.os)) | ||||||
|       .os_version(self.args.os_version.clone()) |       .os_version(self.args.os_version()) | ||||||
|       .ignore_tls_errors(self.shared_args.ignore_tls_errors) |       .ignore_tls_errors(self.shared_args.ignore_tls_errors) | ||||||
|       .certificate(self.args.certificate.clone()) |       .certificate(self.args.certificate.clone()) | ||||||
|       .sslkey(self.args.sslkey.clone()) |       .sslkey(self.args.sslkey.clone()) | ||||||
| @@ -359,7 +363,7 @@ impl<'a> ConnectHandler<'a> { | |||||||
|           None |           None | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let os_version = self.args.os_version.clone(); |         let os_version = self.args.os_version(); | ||||||
|         let verbose = self.shared_args.verbose.to_verbose_arg(); |         let verbose = self.shared_args.verbose.to_verbose_arg(); | ||||||
|         let auth_launcher = SamlAuthLauncher::new(&self.args.server) |         let auth_launcher = SamlAuthLauncher::new(&self.args.server) | ||||||
|           .gateway(is_gateway) |           .gateway(is_gateway) | ||||||
|   | |||||||
| @@ -1,5 +1,9 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 2.4.3 - 2025-01-21 | ||||||
|  |  | ||||||
|  | - Do not use static default value for `--os-version` option. | ||||||
|  |  | ||||||
| ## 2.4.2 - 2025-01-20 | ## 2.4.2 - 2025-01-20 | ||||||
|  |  | ||||||
| - Disconnect the VPN when sleep (fix [#166](https://github.com/yuezk/GlobalProtect-openconnect/issues/166), [#267](https://github.com/yuezk/GlobalProtect-openconnect/issues/267)) | - Disconnect the VPN when sleep (fix [#166](https://github.com/yuezk/GlobalProtect-openconnect/issues/166), [#267](https://github.com/yuezk/GlobalProtect-openconnect/issues/267)) | ||||||
|   | |||||||
| @@ -28,15 +28,21 @@ regex = { workspace = true, optional = true } | |||||||
| tokio-util = { workspace = true, optional = true } | tokio-util = { workspace = true, optional = true } | ||||||
| html-escape = { version = "0.2.13", optional = true } | html-escape = { version = "0.2.13", optional = true } | ||||||
|  |  | ||||||
| [target.'cfg(not(target_os = "macos"))'.dependencies] | [target.'cfg(not(any(target_os="macos", target_os="windows")))'.dependencies] | ||||||
| webkit2gtk = { version = "2", optional = true } | webkit2gtk = { version = "2", optional = true } | ||||||
|  |  | ||||||
| [target.'cfg(target_os = "macos")'.dependencies] | [target.'cfg(target_os="macos")'.dependencies] | ||||||
| block2 = { version = "0.5", optional = true } | block2 = { version = "0.5", optional = true } | ||||||
| objc2 = { version = "0.5", optional = true } | objc2 = { version = "0.5", optional = true } | ||||||
| objc2-foundation = { version = "0.2", optional = true } | objc2-foundation = { version = "0.2", optional = true } | ||||||
| objc2-web-kit = { version = "0.2", optional = true } | objc2-web-kit = { version = "0.2", optional = true } | ||||||
|  |  | ||||||
|  | [target.'cfg(target_os="windows")'.dependencies] | ||||||
|  | webview2-com = { version = "0.34", optional = true } | ||||||
|  | windows-core = { version = "0.58", optional = true } | ||||||
|  | windows = { version = "0.58", optional = true } | ||||||
|  | serde_json = { workspace = true, optional = true } | ||||||
|  |  | ||||||
| [features] | [features] | ||||||
| browser-auth = [ | browser-auth = [ | ||||||
|   "dep:webbrowser", |   "dep:webbrowser", | ||||||
| @@ -56,4 +62,8 @@ webview-auth = [ | |||||||
|   "dep:objc2", |   "dep:objc2", | ||||||
|   "dep:objc2-foundation", |   "dep:objc2-foundation", | ||||||
|   "dep:objc2-web-kit", |   "dep:objc2-web-kit", | ||||||
|  |   "dep:webview2-com", | ||||||
|  |   "dep:windows-core", | ||||||
|  |   "dep:windows", | ||||||
|  |   "dep:serde_json", | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| use std::{env::temp_dir, fs, os::unix::fs::PermissionsExt}; | use std::{env::temp_dir, fs}; | ||||||
|  |  | ||||||
| use gpapi::{auth::SamlAuthData, GP_CALLBACK_PORT_FILENAME}; | use gpapi::{auth::SamlAuthData, GP_CALLBACK_PORT_FILENAME}; | ||||||
| use log::info; | use log::info; | ||||||
| @@ -96,7 +96,11 @@ async fn wait_auth_data() -> anyhow::Result<SamlAuthData> { | |||||||
|  |  | ||||||
|   // Write the port to a file |   // Write the port to a file | ||||||
|   fs::write(&port_file, port.to_string())?; |   fs::write(&port_file, port.to_string())?; | ||||||
|  |   #[cfg(unix)] | ||||||
|  |   { | ||||||
|  |     use os::unix::fs::PermissionsExt; | ||||||
|     fs::set_permissions(&port_file, fs::Permissions::from_mode(0o600))?; |     fs::set_permissions(&port_file, fs::Permissions::from_mode(0o600))?; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Remove the previous log file |   // Remove the previous log file | ||||||
|   let callback_log = temp_dir().join("gpcallback.log"); |   let callback_log = temp_dir().join("gpcallback.log"); | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| mod auth_messenger; | mod auth_messenger; | ||||||
| mod webview_auth; | mod webview_auth; | ||||||
|  |  | ||||||
| #[cfg_attr(not(target_os = "macos"), path = "webview/unix.rs")] | #[cfg_attr(not(any(target_os = "macos", target_os = "windows")), path = "webview/unix.rs")] | ||||||
| #[cfg_attr(target_os = "macos", path = "webview/macos.rs")] | #[cfg_attr(target_os = "macos", path = "webview/macos.rs")] | ||||||
|  | #[cfg_attr(windows, path = "webview/windows.rs")] | ||||||
| mod platform_impl; | mod platform_impl; | ||||||
|  |  | ||||||
| pub use webview_auth::WebviewAuthenticator; | pub use webview_auth::WebviewAuthenticator; | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ pub(crate) enum AuthDataLocation { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub(crate) enum AuthError { | pub(crate) enum AuthError { | ||||||
|   /// Failed to load page due to TLS error |   /// Failed to load page due to TLS error | ||||||
|   #[cfg(not(target_os = "macos"))] |   #[cfg(not(any(target_os = "macos", target_os = "windows")))] | ||||||
|   TlsError, |   TlsError, | ||||||
|   /// 1. Found auth data in headers/body but it's invalid |   /// 1. Found auth data in headers/body but it's invalid | ||||||
|   /// 2. Loaded an empty page, failed to load page. etc. |   /// 2. Loaded an empty page, failed to load page. etc. | ||||||
|   | |||||||
| @@ -115,7 +115,7 @@ impl<'a> WebviewAuthenticator<'a> { | |||||||
|       match auth_messenger.subscribe().await? { |       match auth_messenger.subscribe().await? { | ||||||
|         AuthEvent::Close => bail!("Authentication cancelled"), |         AuthEvent::Close => bail!("Authentication cancelled"), | ||||||
|         AuthEvent::RaiseWindow => self.raise_window(&auth_window), |         AuthEvent::RaiseWindow => self.raise_window(&auth_window), | ||||||
|         #[cfg(not(target_os = "macos"))] |         #[cfg(not(any(target_os = "macos", target_os = "windows")))] | ||||||
|         AuthEvent::Error(AuthError::TlsError) => bail!(gpapi::error::PortalError::TlsError), |         AuthEvent::Error(AuthError::TlsError) => bail!(gpapi::error::PortalError::TlsError), | ||||||
|         AuthEvent::Error(AuthError::NotFound(location)) => { |         AuthEvent::Error(AuthError::NotFound(location)) => { | ||||||
|           info!( |           info!( | ||||||
| @@ -261,10 +261,10 @@ impl<'a> WebviewAuthenticator<'a> { | |||||||
|  |  | ||||||
|     info!("Raising auth window..."); |     info!("Raising auth window..."); | ||||||
|  |  | ||||||
|     #[cfg(target_os = "macos")] |     #[cfg(any(target_os = "macos", target_os = "windows"))] | ||||||
|     let result = auth_window.show(); |     let result = auth_window.show(); | ||||||
|  |  | ||||||
|     #[cfg(not(target_os = "macos"))] |     #[cfg(not(any(target_os = "macos", target_os = "windows")))] | ||||||
|     let result = { |     let result = { | ||||||
|       use gpapi::utils::window::WindowExt; |       use gpapi::utils::window::WindowExt; | ||||||
|       auth_window.raise() |       auth_window.raise() | ||||||
|   | |||||||
							
								
								
									
										142
									
								
								crates/auth/src/webview/windows.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								crates/auth/src/webview/windows.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | use log::warn; | ||||||
|  | use tauri::webview::PlatformWebview; | ||||||
|  | use webview2_com::{ | ||||||
|  |   pwstr_from_str, take_pwstr, ExecuteScriptCompletedHandler, | ||||||
|  |   Microsoft::Web::WebView2::Win32::{ | ||||||
|  |     ICoreWebView2WebResourceResponseView, ICoreWebView2_14, ICoreWebView2_2, | ||||||
|  |     COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW, | ||||||
|  |   }, | ||||||
|  |   ServerCertificateErrorDetectedEventHandler, WebResourceResponseReceivedEventHandler, | ||||||
|  | }; | ||||||
|  | use windows_core::{Interface, PWSTR}; | ||||||
|  |  | ||||||
|  | use super::{ | ||||||
|  |   auth_messenger::AuthError, | ||||||
|  |   webview_auth::{GetHeader, PlatformWebviewExt}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | impl PlatformWebviewExt for PlatformWebview { | ||||||
|  |   fn ignore_tls_errors(&self) -> anyhow::Result<()> { | ||||||
|  |     unsafe { | ||||||
|  |       let wv = self.controller().CoreWebView2()?.cast::<ICoreWebView2_14>()?; | ||||||
|  |       let handler = ServerCertificateErrorDetectedEventHandler::create(Box::new(|_, e| { | ||||||
|  |         if let Some(e) = e { | ||||||
|  |           let _ = e.SetAction(COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |       })); | ||||||
|  |  | ||||||
|  |       wv.add_ServerCertificateErrorDetected(&handler, &mut Default::default())?; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fn load_url(&self, url: &str) -> anyhow::Result<()> { | ||||||
|  |     let url = pwstr_from_str(url); | ||||||
|  |  | ||||||
|  |     unsafe { self.controller().CoreWebView2()?.Navigate(url)? } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fn load_html(&self, html: &str) -> anyhow::Result<()> { | ||||||
|  |     let html = pwstr_from_str(html); | ||||||
|  |  | ||||||
|  |     unsafe { self.controller().CoreWebView2()?.NavigateToString(html)? } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fn get_html(&self, callback: Box<dyn Fn(anyhow::Result<String>) + 'static>) { | ||||||
|  |     unsafe { | ||||||
|  |       match self.controller().CoreWebView2() { | ||||||
|  |         Ok(wv) => { | ||||||
|  |           let js = "document.documentElement.outerHTML"; | ||||||
|  |           let js = pwstr_from_str(js); | ||||||
|  |  | ||||||
|  |           let handler = ExecuteScriptCompletedHandler::create(Box::new(move |err, html| { | ||||||
|  |             if let Err(err) = err { | ||||||
|  |               callback(Err(anyhow::anyhow!(err))); | ||||||
|  |               return Ok(()); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // The returned HTML is JSON.stringify'd string, so we need to parse it | ||||||
|  |             let res = match serde_json::from_str(&html) { | ||||||
|  |               Ok(Some(html)) => Ok(html), | ||||||
|  |               Ok(None) => Err(anyhow::anyhow!("No HTML returned")), | ||||||
|  |               Err(err) => Err(anyhow::anyhow!(err)), | ||||||
|  |             }; | ||||||
|  |             callback(res); | ||||||
|  |  | ||||||
|  |             Ok(()) | ||||||
|  |           })); | ||||||
|  |  | ||||||
|  |           if let Err(err) = wv.ExecuteScript(js, &handler) { | ||||||
|  |             warn!("Failed to execute script: {}", err); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         Err(err) => callback(Err(anyhow::anyhow!(err))), | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl GetHeader for ICoreWebView2WebResourceResponseView { | ||||||
|  |   fn get_header(&self, key: &str) -> Option<String> { | ||||||
|  |     unsafe { | ||||||
|  |       let headers = self.Headers().ok()?; | ||||||
|  |       let key = pwstr_from_str(key); | ||||||
|  |  | ||||||
|  |       let mut contains = Default::default(); | ||||||
|  |       headers.Contains(key, &mut contains).ok()?; | ||||||
|  |  | ||||||
|  |       if contains.as_bool() { | ||||||
|  |         let mut value = PWSTR::null(); | ||||||
|  |         headers.GetHeader(key, &mut value).ok()?; | ||||||
|  |         let value = take_pwstr(value); | ||||||
|  |  | ||||||
|  |         Some(value) | ||||||
|  |       } else { | ||||||
|  |         None | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub trait PlatformWebviewOnResponse { | ||||||
|  |   fn on_response( | ||||||
|  |     &self, | ||||||
|  |     callback: Box<dyn Fn(anyhow::Result<ICoreWebView2WebResourceResponseView, AuthError>) + 'static>, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PlatformWebviewOnResponse for PlatformWebview { | ||||||
|  |   fn on_response( | ||||||
|  |     &self, | ||||||
|  |     callback: Box<dyn Fn(anyhow::Result<ICoreWebView2WebResourceResponseView, AuthError>) + 'static>, | ||||||
|  |   ) { | ||||||
|  |     unsafe { | ||||||
|  |       let _ = self | ||||||
|  |         .controller() | ||||||
|  |         .CoreWebView2() | ||||||
|  |         .and_then(|wv| wv.cast::<ICoreWebView2_2>()) | ||||||
|  |         .map(|wv| { | ||||||
|  |           let handler = WebResourceResponseReceivedEventHandler::create(Box::new(move |_, e| { | ||||||
|  |             let Some(e) = e else { | ||||||
|  |               return Ok(()); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             match e.Response() { | ||||||
|  |               Ok(res) => callback(Ok(res)), | ||||||
|  |               Err(err) => warn!("Failed to get response: {}", err), | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Ok(()) | ||||||
|  |           })); | ||||||
|  |  | ||||||
|  |           let _ = wv.add_WebResourceResponseReceived(&handler, &mut Default::default()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -27,7 +27,7 @@ chacha20poly1305 = { version = "0.10", features = ["std"] } | |||||||
| redact-engine.workspace = true | redact-engine.workspace = true | ||||||
| url.workspace = true | url.workspace = true | ||||||
| regex.workspace = true | regex.workspace = true | ||||||
| uzers.workspace = true |  | ||||||
| serde_urlencoded.workspace = true | serde_urlencoded.workspace = true | ||||||
| md5.workspace = true | md5.workspace = true | ||||||
| sha256.workspace = true | sha256.workspace = true | ||||||
| @@ -39,6 +39,9 @@ clap-verbosity-flag = { workspace = true, optional = true } | |||||||
| env_logger = { workspace = true, optional = true } | env_logger = { workspace = true, optional = true } | ||||||
| log-reload = { version = "0.1", optional = true } | log-reload = { version = "0.1", optional = true } | ||||||
|  |  | ||||||
|  | [target.'cfg(target_family="unix")'.dependencies] | ||||||
|  | uzers.workspace = true | ||||||
|  |  | ||||||
| [features] | [features] | ||||||
| tauri = ["dep:tauri"] | tauri = ["dep:tauri"] | ||||||
| clap = ["dep:clap", "dep:clap-verbosity-flag"] | clap = ["dep:clap", "dep:clap-verbosity-flag"] | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ impl SamlAuthData { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     let auth_data = decode_to_string(auth_data).map_err(|e| { |     let auth_data = decode_to_string(auth_data).map_err(|e| { | ||||||
|       warn!("Failed to decode SAML auth data: {}", e); |       warn!("Failed to decode SAML auth data: {}", auth_data); | ||||||
|       AuthDataParseError::Invalid(anyhow::anyhow!(e)) |       AuthDataParseError::Invalid(anyhow::anyhow!(e)) | ||||||
|     })?; |     })?; | ||||||
|     let auth_data = Self::from_html(&auth_data)?; |     let auth_data = Self::from_html(&auth_data)?; | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| mod login; | mod login; | ||||||
| mod parse_gateways; | mod parse_gateways; | ||||||
|  |  | ||||||
|  | #[cfg(unix)] | ||||||
| pub mod hip; | pub mod hip; | ||||||
|  |  | ||||||
| pub use login::*; | pub use login::*; | ||||||
|   | |||||||
| @@ -4,7 +4,10 @@ pub mod error; | |||||||
| pub mod gateway; | pub mod gateway; | ||||||
| pub mod gp_params; | pub mod gp_params; | ||||||
| pub mod portal; | pub mod portal; | ||||||
|  |  | ||||||
|  | #[cfg(unix)] | ||||||
| pub mod process; | pub mod process; | ||||||
|  |  | ||||||
| pub mod service; | pub mod service; | ||||||
| pub mod utils; | pub mod utils; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| pub(crate) mod command_traits; | pub(crate) mod command_traits; | ||||||
|  |  | ||||||
| pub(crate) mod gui_helper_launcher; | pub(crate) mod gui_helper_launcher; | ||||||
|  |  | ||||||
| pub mod auth_launcher; | pub mod auth_launcher; | ||||||
| pub mod gui_launcher; | pub mod gui_launcher; | ||||||
| pub mod hip_launcher; | pub mod hip_launcher; | ||||||
| pub mod service_launcher; | pub mod service_launcher; | ||||||
|  |  | ||||||
|  | #[cfg(unix)] | ||||||
| pub mod users; | pub mod users; | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ pub mod lock_file; | |||||||
| pub mod openssl; | pub mod openssl; | ||||||
| pub mod redact; | pub mod redact; | ||||||
| pub mod request; | pub mod request; | ||||||
| #[cfg(feature = "tauri")] | #[cfg(all(feature = "tauri", not(any(target_os = "macos", target_os = "windows"))))] | ||||||
| pub mod window; | pub mod window; | ||||||
|  |  | ||||||
| mod shutdown_signal; | mod shutdown_signal; | ||||||
|   | |||||||
| @@ -6,15 +6,21 @@ pub async fn shutdown_signal() { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   #[cfg(unix)] |   #[cfg(unix)] | ||||||
|  |   { | ||||||
|     let terminate = async { |     let terminate = async { | ||||||
|       signal::unix::signal(signal::unix::SignalKind::terminate()) |       signal::unix::signal(signal::unix::SignalKind::terminate()) | ||||||
|         .expect("failed to install signal handler") |         .expect("failed to install signal handler") | ||||||
|         .recv() |         .recv() | ||||||
|         .await; |         .await; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     tokio::select! { |     tokio::select! { | ||||||
|         _ = ctrl_c => {}, |         _ = ctrl_c => {}, | ||||||
|         _ = terminate => {}, |         _ = terminate => {}, | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #[cfg(not(unix))] | ||||||
|  |   { | ||||||
|  |     ctrl_c.await; | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ Group:          Productivity/Networking/PPP | |||||||
|  |  | ||||||
| License:        GPL-3.0 | License:        GPL-3.0 | ||||||
| URL:            https://github.com/yuezk/GlobalProtect-openconnect | URL:            https://github.com/yuezk/GlobalProtect-openconnect | ||||||
| Source:         %{name}.tar.gz | Source:         @SOURCE@ | ||||||
|  |  | ||||||
| BuildRequires:  make | BuildRequires:  make | ||||||
| BuildRequires:  rust | BuildRequires:  rust | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ release_snapshot() { | |||||||
|  |  | ||||||
|   echo "Uploading new assets..." |   echo "Uploading new assets..." | ||||||
|   gh -R "$REPO" release upload "$TAG" \ |   gh -R "$REPO" release upload "$TAG" \ | ||||||
|     "$PROJECT_DIR"/.build/artifacts/artifact-source/* \ |     "$PROJECT_DIR"/.build/artifacts/artifact-source*/* \ | ||||||
|     "$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/* |     "$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/* | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -40,7 +40,7 @@ release_tag() { | |||||||
|   gh -R "$REPO" release create $TAG \ |   gh -R "$REPO" release create $TAG \ | ||||||
|     --title "$TAG" \ |     --title "$TAG" \ | ||||||
|     --notes "$RELEASE_NOTES" \ |     --notes "$RELEASE_NOTES" \ | ||||||
|     "$PROJECT_DIR"/.build/artifacts/artifact-source/* \ |     "$PROJECT_DIR"/.build/artifacts/artifact-source*/* \ | ||||||
|     "$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/* |     "$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/* | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user