mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	refactor: encrypt the sensitive data
This commit is contained in:
		
							
								
								
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -5,11 +5,13 @@ | ||||
|     "clickaway", | ||||
|     "clientgpversion", | ||||
|     "clientos", | ||||
|     "consts", | ||||
|     "devicename", | ||||
|     "distro", | ||||
|     "gpcommon", | ||||
|     "gpgui", | ||||
|     "gpservice", | ||||
|     "humantime", | ||||
|     "Immer", | ||||
|     "jnlp", | ||||
|     "oneshot", | ||||
| @@ -17,7 +19,10 @@ | ||||
|     "prelogin", | ||||
|     "prelogon", | ||||
|     "prelogonuserauthcookie", | ||||
|     "repr", | ||||
|     "rustc", | ||||
|     "tauri", | ||||
|     "thiserror", | ||||
|     "unlisten", | ||||
|     "userauthcookie", | ||||
|     "vpnc", | ||||
|   | ||||
							
								
								
									
										695
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										695
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -8,6 +8,53 @@ version = "1.0.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||||
|  | ||||
| [[package]] | ||||
| name = "aead" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" | ||||
| dependencies = [ | ||||
|  "crypto-common", | ||||
|  "generic-array", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "aes" | ||||
| version = "0.7.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "cipher 0.3.0", | ||||
|  "cpufeatures", | ||||
|  "opaque-debug", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "aes" | ||||
| version = "0.8.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "cipher 0.4.4", | ||||
|  "cpufeatures", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "aes-gcm" | ||||
| version = "0.10.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" | ||||
| dependencies = [ | ||||
|  "aead", | ||||
|  "aes 0.8.3", | ||||
|  "cipher 0.4.4", | ||||
|  "ctr", | ||||
|  "ghash", | ||||
|  "subtle", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "aho-corasick" | ||||
| version = "0.7.20" | ||||
| @@ -51,8 +98,11 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" | ||||
| name = "app" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "env_logger", | ||||
|  "aes-gcm", | ||||
|  "anyhow", | ||||
|  "gpcommon", | ||||
|  "hex", | ||||
|  "keyring", | ||||
|  "log", | ||||
|  "openssl", | ||||
|  "regex", | ||||
| @@ -69,6 +119,117 @@ dependencies = [ | ||||
|  "whoami", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-broadcast" | ||||
| version = "0.5.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" | ||||
| dependencies = [ | ||||
|  "event-listener", | ||||
|  "futures-core", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-channel" | ||||
| version = "1.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" | ||||
| dependencies = [ | ||||
|  "concurrent-queue", | ||||
|  "event-listener", | ||||
|  "futures-core", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-executor" | ||||
| version = "1.5.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" | ||||
| dependencies = [ | ||||
|  "async-lock", | ||||
|  "async-task", | ||||
|  "concurrent-queue", | ||||
|  "fastrand", | ||||
|  "futures-lite", | ||||
|  "slab", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-fs" | ||||
| version = "1.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" | ||||
| dependencies = [ | ||||
|  "async-lock", | ||||
|  "autocfg", | ||||
|  "blocking", | ||||
|  "futures-lite", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-io" | ||||
| version = "1.13.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" | ||||
| dependencies = [ | ||||
|  "async-lock", | ||||
|  "autocfg", | ||||
|  "cfg-if", | ||||
|  "concurrent-queue", | ||||
|  "futures-lite", | ||||
|  "log", | ||||
|  "parking", | ||||
|  "polling", | ||||
|  "rustix", | ||||
|  "slab", | ||||
|  "socket2", | ||||
|  "waker-fn", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-lock" | ||||
| version = "2.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" | ||||
| dependencies = [ | ||||
|  "event-listener", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-process" | ||||
| version = "1.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" | ||||
| dependencies = [ | ||||
|  "async-io", | ||||
|  "async-lock", | ||||
|  "autocfg", | ||||
|  "blocking", | ||||
|  "cfg-if", | ||||
|  "event-listener", | ||||
|  "futures-lite", | ||||
|  "rustix", | ||||
|  "signal-hook", | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-recursion" | ||||
| version = "1.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.16", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "async-task" | ||||
| version = "4.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" | ||||
|  | ||||
| [[package]] | ||||
| name = "async-trait" | ||||
| version = "0.1.66" | ||||
| @@ -104,6 +265,12 @@ dependencies = [ | ||||
|  "system-deps 6.0.3", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "atomic-waker" | ||||
| version = "1.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" | ||||
|  | ||||
| [[package]] | ||||
| name = "attohttpc" | ||||
| version = "0.22.0" | ||||
| @@ -170,6 +337,37 @@ dependencies = [ | ||||
|  "generic-array", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "block-modes" | ||||
| version = "0.8.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" | ||||
| dependencies = [ | ||||
|  "block-padding", | ||||
|  "cipher 0.3.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "block-padding" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" | ||||
|  | ||||
| [[package]] | ||||
| name = "blocking" | ||||
| version = "1.3.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" | ||||
| dependencies = [ | ||||
|  "async-channel", | ||||
|  "async-lock", | ||||
|  "async-task", | ||||
|  "atomic-waker", | ||||
|  "fastrand", | ||||
|  "futures-lite", | ||||
|  "log", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "brotli" | ||||
| version = "3.3.4" | ||||
| @@ -329,6 +527,25 @@ dependencies = [ | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "cipher" | ||||
| version = "0.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" | ||||
| dependencies = [ | ||||
|  "generic-array", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "cipher" | ||||
| version = "0.4.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" | ||||
| dependencies = [ | ||||
|  "crypto-common", | ||||
|  "inout", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "cocoa" | ||||
| version = "0.24.1" | ||||
| @@ -387,6 +604,15 @@ dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "concurrent-queue" | ||||
| version = "2.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" | ||||
| dependencies = [ | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "convert_case" | ||||
| version = "0.4.0" | ||||
| @@ -478,6 +704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" | ||||
| dependencies = [ | ||||
|  "generic-array", | ||||
|  "rand_core 0.6.4", | ||||
|  "typenum", | ||||
| ] | ||||
|  | ||||
| @@ -518,6 +745,15 @@ dependencies = [ | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ctr" | ||||
| version = "0.9.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" | ||||
| dependencies = [ | ||||
|  "cipher 0.4.4", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "cty" | ||||
| version = "0.2.2" | ||||
| @@ -571,6 +807,17 @@ version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" | ||||
|  | ||||
| [[package]] | ||||
| name = "derivative" | ||||
| version = "2.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "derive_more" | ||||
| version = "0.99.17" | ||||
| @@ -592,6 +839,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" | ||||
| dependencies = [ | ||||
|  "block-buffer", | ||||
|  "crypto-common", | ||||
|  "subtle", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -671,16 +919,24 @@ dependencies = [ | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "env_logger" | ||||
| version = "0.10.0" | ||||
| name = "enumflags2" | ||||
| version = "0.7.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" | ||||
| checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" | ||||
| dependencies = [ | ||||
|  "humantime", | ||||
|  "is-terminal", | ||||
|  "log", | ||||
|  "regex", | ||||
|  "termcolor", | ||||
|  "enumflags2_derive", | ||||
|  "serde", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "enumflags2_derive" | ||||
| version = "0.7.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.16", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -704,6 +960,12 @@ dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "event-listener" | ||||
| version = "2.5.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" | ||||
|  | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "1.8.0" | ||||
| @@ -729,7 +991,7 @@ version = "0.3.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" | ||||
| dependencies = [ | ||||
|  "memoffset", | ||||
|  "memoffset 0.6.5", | ||||
|  "rustc_version 0.3.3", | ||||
| ] | ||||
|  | ||||
| @@ -827,6 +1089,21 @@ version = "0.3.26" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-lite" | ||||
| version = "1.13.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" | ||||
| dependencies = [ | ||||
|  "fastrand", | ||||
|  "futures-core", | ||||
|  "futures-io", | ||||
|  "memchr", | ||||
|  "parking", | ||||
|  "pin-project-lite", | ||||
|  "waker-fn", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-macro" | ||||
| version = "0.3.26" | ||||
| @@ -857,8 +1134,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-io", | ||||
|  "futures-macro", | ||||
|  "futures-sink", | ||||
|  "futures-task", | ||||
|  "memchr", | ||||
|  "pin-project-lite", | ||||
|  "pin-utils", | ||||
|  "slab", | ||||
| @@ -1004,6 +1284,16 @@ dependencies = [ | ||||
|  "wasi 0.11.0+wasi-snapshot-preview1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ghash" | ||||
| version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" | ||||
| dependencies = [ | ||||
|  "opaque-debug", | ||||
|  "polyval", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "gio" | ||||
| version = "0.15.12" | ||||
| @@ -1139,8 +1429,9 @@ dependencies = [ | ||||
| name = "gpservice" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "env_logger", | ||||
|  "fern", | ||||
|  "gpcommon", | ||||
|  "humantime", | ||||
|  "log", | ||||
|  "tokio", | ||||
| ] | ||||
| @@ -1251,6 +1542,24 @@ version = "0.4.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||||
|  | ||||
| [[package]] | ||||
| name = "hkdf" | ||||
| version = "0.12.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" | ||||
| dependencies = [ | ||||
|  "hmac", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "hmac" | ||||
| version = "0.12.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" | ||||
| dependencies = [ | ||||
|  "digest", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "html5ever" | ||||
| version = "0.25.2" | ||||
| @@ -1388,6 +1697,15 @@ dependencies = [ | ||||
|  "cfb", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "inout" | ||||
| version = "0.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" | ||||
| dependencies = [ | ||||
|  "generic-array", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "instant" | ||||
| version = "0.1.12" | ||||
| @@ -1408,18 +1726,6 @@ dependencies = [ | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "is-terminal" | ||||
| version = "0.4.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" | ||||
| dependencies = [ | ||||
|  "hermit-abi 0.3.1", | ||||
|  "io-lifetimes", | ||||
|  "rustix", | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "is_executable" | ||||
| version = "1.0.1" | ||||
| @@ -1505,6 +1811,20 @@ dependencies = [ | ||||
|  "treediff", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "keyring" | ||||
| version = "2.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "04ac4b8b0884cdf23c4619d139acf43839eac4f0739b92980c2a6d460d9c84f5" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "lazy_static", | ||||
|  "linux-keyutils", | ||||
|  "secret-service", | ||||
|  "security-framework", | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "kuchiki" | ||||
| version = "0.8.1" | ||||
| @@ -1538,6 +1858,16 @@ dependencies = [ | ||||
|  "safemem", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "linux-keyutils" | ||||
| version = "0.2.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.3.7" | ||||
| @@ -1638,6 +1968,15 @@ dependencies = [ | ||||
|  "autocfg", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "memoffset" | ||||
| version = "0.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.6.2" | ||||
| @@ -1711,6 +2050,19 @@ version = "1.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" | ||||
|  | ||||
| [[package]] | ||||
| name = "nix" | ||||
| version = "0.26.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  "memoffset 0.7.1", | ||||
|  "static_assertions", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "nodrop" | ||||
| version = "0.1.14" | ||||
| @@ -1727,6 +2079,40 @@ dependencies = [ | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num" | ||||
| version = "0.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" | ||||
| dependencies = [ | ||||
|  "num-bigint", | ||||
|  "num-complex", | ||||
|  "num-integer", | ||||
|  "num-iter", | ||||
|  "num-rational", | ||||
|  "num-traits", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num-bigint" | ||||
| version = "0.4.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "num-integer", | ||||
|  "num-traits", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num-complex" | ||||
| version = "0.4.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" | ||||
| dependencies = [ | ||||
|  "num-traits", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num-integer" | ||||
| version = "0.1.45" | ||||
| @@ -1737,6 +2123,17 @@ dependencies = [ | ||||
|  "num-traits", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num-iter" | ||||
| version = "0.1.43" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "num-integer", | ||||
|  "num-traits", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "num-rational" | ||||
| version = "0.4.1" | ||||
| @@ -1744,6 +2141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "num-bigint", | ||||
|  "num-integer", | ||||
|  "num-traits", | ||||
| ] | ||||
| @@ -1831,6 +2229,12 @@ version = "1.17.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" | ||||
|  | ||||
| [[package]] | ||||
| name = "opaque-debug" | ||||
| version = "0.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" | ||||
|  | ||||
| [[package]] | ||||
| name = "open" | ||||
| version = "3.2.0" | ||||
| @@ -1886,6 +2290,16 @@ dependencies = [ | ||||
|  "vcpkg", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ordered-stream" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "pin-project-lite", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "overload" | ||||
| version = "0.1.1" | ||||
| @@ -1917,6 +2331,12 @@ dependencies = [ | ||||
|  "system-deps 6.0.3", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "parking" | ||||
| version = "2.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" | ||||
|  | ||||
| [[package]] | ||||
| name = "parking_lot" | ||||
| version = "0.12.1" | ||||
| @@ -2104,6 +2524,34 @@ dependencies = [ | ||||
|  "miniz_oxide", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "polling" | ||||
| version = "2.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "bitflags", | ||||
|  "cfg-if", | ||||
|  "concurrent-queue", | ||||
|  "libc", | ||||
|  "log", | ||||
|  "pin-project-lite", | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "polyval" | ||||
| version = "0.6.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "cpufeatures", | ||||
|  "opaque-debug", | ||||
|  "universal-hash", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ppv-lite86" | ||||
| version = "0.2.17" | ||||
| @@ -2423,6 +2871,25 @@ version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" | ||||
|  | ||||
| [[package]] | ||||
| name = "secret-service" | ||||
| version = "3.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" | ||||
| dependencies = [ | ||||
|  "aes 0.7.5", | ||||
|  "block-modes", | ||||
|  "futures-util", | ||||
|  "generic-array", | ||||
|  "hkdf", | ||||
|  "num", | ||||
|  "once_cell", | ||||
|  "rand 0.8.5", | ||||
|  "serde", | ||||
|  "sha2", | ||||
|  "zbus", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "security-framework" | ||||
| version = "2.8.2" | ||||
| @@ -2616,6 +3083,17 @@ dependencies = [ | ||||
|  "stable_deref_trait", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "sha1" | ||||
| version = "0.10.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "cpufeatures", | ||||
|  "digest", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "sha2" | ||||
| version = "0.10.6" | ||||
| @@ -2636,6 +3114,16 @@ dependencies = [ | ||||
|  "lazy_static", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "signal-hook" | ||||
| version = "0.3.16" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "signal-hook-registry", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "signal-hook-registry" | ||||
| version = "1.4.0" | ||||
| @@ -2725,6 +3213,12 @@ dependencies = [ | ||||
|  "loom", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "static_assertions" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||||
|  | ||||
| [[package]] | ||||
| name = "string_cache" | ||||
| version = "0.8.4" | ||||
| @@ -2757,6 +3251,12 @@ version = "0.10.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||||
|  | ||||
| [[package]] | ||||
| name = "subtle" | ||||
| version = "2.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.107" | ||||
| @@ -3112,15 +3612,6 @@ dependencies = [ | ||||
|  "utf-8", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "termcolor" | ||||
| version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" | ||||
| dependencies = [ | ||||
|  "winapi-util", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "thin-slice" | ||||
| version = "0.1.1" | ||||
| @@ -3354,6 +3845,16 @@ version = "0.1.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" | ||||
|  | ||||
| [[package]] | ||||
| name = "uds_windows" | ||||
| version = "1.0.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" | ||||
| dependencies = [ | ||||
|  "tempfile", | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "unicode-bidi" | ||||
| version = "0.3.10" | ||||
| @@ -3381,6 +3882,16 @@ version = "1.10.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" | ||||
|  | ||||
| [[package]] | ||||
| name = "universal-hash" | ||||
| version = "0.5.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" | ||||
| dependencies = [ | ||||
|  "crypto-common", | ||||
|  "subtle", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "untrusted" | ||||
| version = "0.7.1" | ||||
| @@ -3501,6 +4012,12 @@ dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "waker-fn" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" | ||||
|  | ||||
| [[package]] | ||||
| name = "walkdir" | ||||
| version = "2.3.2" | ||||
| @@ -4041,3 +4558,117 @@ checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "xdg-home" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" | ||||
| dependencies = [ | ||||
|  "nix", | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zbus" | ||||
| version = "3.14.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" | ||||
| dependencies = [ | ||||
|  "async-broadcast", | ||||
|  "async-executor", | ||||
|  "async-fs", | ||||
|  "async-io", | ||||
|  "async-lock", | ||||
|  "async-process", | ||||
|  "async-recursion", | ||||
|  "async-task", | ||||
|  "async-trait", | ||||
|  "blocking", | ||||
|  "byteorder", | ||||
|  "derivative", | ||||
|  "enumflags2", | ||||
|  "event-listener", | ||||
|  "futures-core", | ||||
|  "futures-sink", | ||||
|  "futures-util", | ||||
|  "hex", | ||||
|  "nix", | ||||
|  "once_cell", | ||||
|  "ordered-stream", | ||||
|  "rand 0.8.5", | ||||
|  "serde", | ||||
|  "serde_repr", | ||||
|  "sha1", | ||||
|  "static_assertions", | ||||
|  "tracing", | ||||
|  "uds_windows", | ||||
|  "winapi", | ||||
|  "xdg-home", | ||||
|  "zbus_macros", | ||||
|  "zbus_names", | ||||
|  "zvariant", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zbus_macros" | ||||
| version = "3.14.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" | ||||
| dependencies = [ | ||||
|  "proc-macro-crate", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "regex", | ||||
|  "syn 1.0.107", | ||||
|  "zvariant_utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zbus_names" | ||||
| version = "2.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
|  "static_assertions", | ||||
|  "zvariant", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zvariant" | ||||
| version = "3.15.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "enumflags2", | ||||
|  "libc", | ||||
|  "serde", | ||||
|  "static_assertions", | ||||
|  "zvariant_derive", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zvariant_derive" | ||||
| version = "3.15.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" | ||||
| dependencies = [ | ||||
|  "proc-macro-crate", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 1.0.107", | ||||
|  "zvariant_utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "zvariant_utils" | ||||
| version = "1.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 1.0.107", | ||||
| ] | ||||
|   | ||||
| @@ -201,8 +201,14 @@ impl Client { | ||||
|             }) | ||||
|     } | ||||
|  | ||||
|     pub async fn connect(&self, server: String, cookie: String) -> Result<(), ServerApiError> { | ||||
|         self.send_command(Connect::new(server, cookie).into()).await | ||||
|     pub async fn connect( | ||||
|         &self, | ||||
|         server: String, | ||||
|         cookie: String, | ||||
|         user_agent: String, | ||||
|     ) -> Result<(), ServerApiError> { | ||||
|         self.send_command(Connect::new(server, cookie, user_agent).into()) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     pub async fn disconnect(&self) -> Result<(), ServerApiError> { | ||||
|   | ||||
| @@ -7,11 +7,16 @@ use serde::{Deserialize, Serialize}; | ||||
| pub struct Connect { | ||||
|     server: String, | ||||
|     cookie: String, | ||||
|     user_agent: String, | ||||
| } | ||||
|  | ||||
| impl Connect { | ||||
|     pub fn new(server: String, cookie: String) -> Self { | ||||
|         Self { server, cookie } | ||||
|     pub fn new(server: String, cookie: String, user_agent: String) -> Self { | ||||
|         Self { | ||||
|             server, | ||||
|             cookie, | ||||
|             user_agent, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -25,7 +30,7 @@ impl Command for Connect { | ||||
|             return Err(format!("VPN is already in state: {:?}", status).into()); | ||||
|         } | ||||
|  | ||||
|         if let Err(err) = vpn.connect(&self.server, &self.cookie).await { | ||||
|         if let Err(err) = vpn.connect(&self.server, &self.cookie, &self.user_agent).await { | ||||
|             return Err(err.to_string().into()); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -5,16 +5,17 @@ use tokio::sync::mpsc; | ||||
| #[repr(C)] | ||||
| #[derive(Debug, Copy, Clone)] | ||||
| pub(crate) struct Options { | ||||
|     pub server: *const ::std::os::raw::c_char, | ||||
|     pub cookie: *const ::std::os::raw::c_char, | ||||
|     pub script: *const ::std::os::raw::c_char, | ||||
|     pub server: *const std::os::raw::c_char, | ||||
|     pub cookie: *const std::os::raw::c_char, | ||||
|     pub script: *const std::os::raw::c_char, | ||||
|     pub user_agent: *const std::os::raw::c_char, | ||||
|     pub user_data: *mut c_void, | ||||
| } | ||||
|  | ||||
| #[link(name = "vpn")] | ||||
| extern "C" { | ||||
|     #[link_name = "vpn_connect"] | ||||
|     pub(crate) fn connect(options: *const Options) -> ::std::os::raw::c_int; | ||||
|     pub(crate) fn connect(options: *const Options) -> std::os::raw::c_int; | ||||
|  | ||||
|     #[link_name = "vpn_disconnect"] | ||||
|     pub(crate) fn disconnect(); | ||||
| @@ -32,7 +33,7 @@ extern "C" fn on_vpn_connected(value: i32, sender: *mut c_void) { | ||||
| // level: 0 = error, 1 = info, 2 = debug, 3 = trace | ||||
| // map the error level log in openconnect to the warning level | ||||
| #[no_mangle] | ||||
| extern "C" fn vpn_log(level: i32, message: *const ::std::os::raw::c_char) { | ||||
| extern "C" fn vpn_log(level: i32, message: *const std::os::raw::c_char) { | ||||
|     let message = unsafe { std::ffi::CStr::from_ptr(message) }; | ||||
|     let message = message.to_str().unwrap_or("Invalid log message"); | ||||
|     // Strip the trailing newline | ||||
|   | ||||
| @@ -56,6 +56,7 @@ pub(crate) struct VpnOptions { | ||||
|     server: CString, | ||||
|     cookie: CString, | ||||
|     script: CString, | ||||
|     user_agent: CString, | ||||
| } | ||||
|  | ||||
| impl VpnOptions { | ||||
| @@ -64,6 +65,7 @@ impl VpnOptions { | ||||
|             server: self.server.as_ptr(), | ||||
|             cookie: self.cookie.as_ptr(), | ||||
|             script: self.script.as_ptr(), | ||||
|             user_agent: self.user_agent.as_ptr(), | ||||
|             user_data, | ||||
|         } | ||||
|     } | ||||
| @@ -88,6 +90,7 @@ impl Vpn { | ||||
|         &self, | ||||
|         server: &str, | ||||
|         cookie: &str, | ||||
|         user_agent: &str, | ||||
|     ) -> Result<(), Box<dyn std::error::Error>> { | ||||
|         let script = match find_default_vpnc_script() { | ||||
|             Some(script) => { | ||||
| @@ -104,6 +107,7 @@ impl Vpn { | ||||
|             server: VpnOptions::to_cstr(server), | ||||
|             cookie: VpnOptions::to_cstr(cookie), | ||||
|             script: VpnOptions::to_cstr(script), | ||||
|             user_agent: VpnOptions::to_cstr(user_agent), | ||||
|         }); | ||||
|  | ||||
|         let vpn_options = self.vpn_options.clone(); | ||||
|   | ||||
| @@ -53,7 +53,7 @@ int vpn_connect(const vpn_options *options) | ||||
|     g_user_data = options->user_data; | ||||
|     g_vpnc_script = options->script; | ||||
|  | ||||
|     vpninfo = openconnect_vpninfo_new("PAN GlobalProtect", validate_peer_cert, NULL, NULL, print_progress, NULL); | ||||
|     vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL); | ||||
|  | ||||
|     if (!vpninfo) | ||||
|     { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ typedef struct vpn_options | ||||
|     const char *server; | ||||
|     const char *cookie; | ||||
|     const char *script; | ||||
|     const char *user_agent; | ||||
|     void *user_data; | ||||
| } vpn_options; | ||||
|  | ||||
|   | ||||
| @@ -23,8 +23,7 @@ | ||||
|     "react": "^18.2.0", | ||||
|     "react-dom": "^18.2.0", | ||||
|     "react-spinners": "^0.13.8", | ||||
|     "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log", | ||||
|     "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1" | ||||
|     "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@tauri-apps/cli": "^1.3.1", | ||||
|   | ||||
							
								
								
									
										28
									
								
								gpgui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								gpgui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| lockfileVersion: '6.1' | ||||
| lockfileVersion: '6.0' | ||||
|  | ||||
| settings: | ||||
|   autoInstallPeers: true | ||||
| @@ -48,11 +48,8 @@ dependencies: | ||||
|     specifier: ^0.13.8 | ||||
|     version: 0.13.8(react-dom@18.2.0)(react@18.2.0) | ||||
|   tauri-plugin-log-api: | ||||
|     specifier: github:tauri-apps/tauri-plugin-log | ||||
|     version: github.com/tauri-apps/tauri-plugin-log/21921031d74f871180381317a338559f588ad8e9 | ||||
|   tauri-plugin-store-api: | ||||
|     specifier: github:tauri-apps/tauri-plugin-store#v1 | ||||
|     version: github.com/tauri-apps/tauri-plugin-store/1467ba770623ab1d41d825841c3d9435d9eaa0f1 | ||||
|     specifier: github:tauri-apps/tauri-plugin-log#v1 | ||||
|     version: github.com/tauri-apps/tauri-plugin-log/fbbb126e6d7fba7a7e6772d33f99c0fb689f32b6 | ||||
|  | ||||
| devDependencies: | ||||
|   '@tauri-apps/cli': | ||||
| @@ -878,6 +875,11 @@ packages: | ||||
|     engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} | ||||
|     dev: false | ||||
|  | ||||
|   /@tauri-apps/api@1.4.0: | ||||
|     resolution: {integrity: sha512-Jd6HPoTM1PZSFIzq7FB8VmMu3qSSyo/3lSwLpoapW+lQ41CL5Dow2KryLg+gyazA/58DRWI9vu/XpEeHK4uMdw==} | ||||
|     engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} | ||||
|     dev: false | ||||
|  | ||||
|   /@tauri-apps/cli-darwin-arm64@1.3.1: | ||||
|     resolution: {integrity: sha512-QlepYVPgOgspcwA/u4kGG4ZUijlXfdRtno00zEy+LxinN/IRXtk+6ErVtsmoLi1ZC9WbuMwzAcsRvqsD+RtNAg==} | ||||
|     engines: {node: '>= 10'} | ||||
| @@ -1465,18 +1467,10 @@ packages: | ||||
|     engines: {node: '>= 6'} | ||||
|     dev: false | ||||
|  | ||||
|   github.com/tauri-apps/tauri-plugin-log/21921031d74f871180381317a338559f588ad8e9: | ||||
|     resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/21921031d74f871180381317a338559f588ad8e9} | ||||
|   github.com/tauri-apps/tauri-plugin-log/fbbb126e6d7fba7a7e6772d33f99c0fb689f32b6: | ||||
|     resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/fbbb126e6d7fba7a7e6772d33f99c0fb689f32b6} | ||||
|     name: tauri-plugin-log-api | ||||
|     version: 0.0.0 | ||||
|     dependencies: | ||||
|       '@tauri-apps/api': 1.3.0 | ||||
|     dev: false | ||||
|  | ||||
|   github.com/tauri-apps/tauri-plugin-store/1467ba770623ab1d41d825841c3d9435d9eaa0f1: | ||||
|     resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/1467ba770623ab1d41d825841c3d9435d9eaa0f1} | ||||
|     name: tauri-plugin-store-api | ||||
|     version: 0.0.0 | ||||
|     dependencies: | ||||
|       '@tauri-apps/api': 1.3.0 | ||||
|       '@tauri-apps/api': 1.4.0 | ||||
|     dev: false | ||||
|   | ||||
| @@ -23,7 +23,6 @@ tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", br | ||||
| serde_json = "1.0" | ||||
| serde = { version = "1.0", features = ["derive"] } | ||||
| log = "0.4" | ||||
| env_logger = "0.10" | ||||
| webkit2gtk = "0.18.2" | ||||
| regex = "1" | ||||
| url = "2.3" | ||||
| @@ -32,6 +31,10 @@ veil = "0.1.6" | ||||
| whoami = "1.4.1" | ||||
| tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } | ||||
| openssl = "0.10" | ||||
| keyring = "2" | ||||
| aes-gcm = { version = "0.10", features = ["std"] } | ||||
| hex = "0.4" | ||||
| anyhow = "1.0" | ||||
|  | ||||
| [features] | ||||
| # by default Tauri runs in production mode | ||||
|   | ||||
| @@ -133,7 +133,7 @@ fn build_window(app_handle: &AppHandle, ua: &str) -> tauri::Result<Window> { | ||||
|     Window::builder(app_handle, AUTH_WINDOW_LABEL, url) | ||||
|         .visible(false) | ||||
|         .title("GlobalProtect Login") | ||||
|         .inner_size(400.0, 647.0) | ||||
|         .inner_size(600.0, 500.0) | ||||
|         .min_inner_size(370.0, 600.0) | ||||
|         .user_agent(ua) | ||||
|         .always_on_top(true) | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| use crate::{ | ||||
|     auth::{self, AuthData, AuthRequest, SamlBinding, SamlLoginParams}, | ||||
|     storage::{AppStorage, KeyHint}, | ||||
|     utils::get_openssl_conf, | ||||
|     utils::get_openssl_conf_path, | ||||
| }; | ||||
| use gpcommon::{Client, ServerApiError, VpnStatus}; | ||||
| use serde_json::Value; | ||||
| use std::sync::Arc; | ||||
| use tauri::{AppHandle, State}; | ||||
| use tokio::fs; | ||||
| @@ -24,9 +26,10 @@ pub(crate) async fn vpn_status<'a>( | ||||
| pub(crate) async fn vpn_connect<'a>( | ||||
|     server: String, | ||||
|     cookie: String, | ||||
|     user_agent: String, | ||||
|     client: State<'a, Arc<Client>>, | ||||
| ) -> Result<(), ServerApiError> { | ||||
|     client.connect(server, cookie).await | ||||
|     client.connect(server, cookie, user_agent).await | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| @@ -40,10 +43,10 @@ pub(crate) async fn vpn_disconnect<'a>( | ||||
| pub(crate) async fn saml_login( | ||||
|     binding: SamlBinding, | ||||
|     request: String, | ||||
|     user_agent: String, | ||||
|     clear_cookies: bool, | ||||
|     app_handle: AppHandle, | ||||
| ) -> tauri::Result<Option<AuthData>> { | ||||
|     let user_agent = String::from("PAN GlobalProtect"); | ||||
|     let params = SamlLoginParams { | ||||
|         auth_request: AuthRequest::new(binding, request), | ||||
|         user_agent, | ||||
| @@ -71,3 +74,29 @@ pub(crate) async fn update_openssl_config(app_handle: AppHandle) -> tauri::Resul | ||||
|     fs::write(openssl_conf_path, openssl_conf).await?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) async fn store_get<'a>( | ||||
|     hint: KeyHint<'_>, | ||||
|     app_storage: State<'_, AppStorage<'_>>, | ||||
| ) -> Result<Option<Value>, ()> { | ||||
|     Ok(app_storage.get(hint)) | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) fn store_set( | ||||
|     hint: KeyHint, | ||||
|     value: Value, | ||||
|     app_storage: State<'_, AppStorage>, | ||||
| ) -> Result<(), tauri_plugin_store::Error> { | ||||
|     app_storage.set(hint, &value)?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| #[tauri::command] | ||||
| pub(crate) fn store_save( | ||||
|     app_storage: State<'_, AppStorage>, | ||||
| ) -> Result<(), tauri_plugin_store::Error> { | ||||
|     app_storage.save()?; | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
							
								
								
									
										46
									
								
								gpgui/src-tauri/src/crypto.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								gpgui/src-tauri/src/crypto.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| use aes_gcm::{ | ||||
|     aead::{consts::U12, Aead, OsRng}, | ||||
|     AeadCore, Aes256Gcm, Key, KeyInit, Nonce, | ||||
| }; | ||||
| use keyring::Entry; | ||||
|  | ||||
| const SERVICE_NAME: &str = "GlobalProtect-openconnect"; | ||||
| const ENTRY_KEY: &str = "master-key"; | ||||
|  | ||||
| fn get_master_key() -> Result<Key<Aes256Gcm>, anyhow::Error> { | ||||
|     let key_entry = Entry::new(SERVICE_NAME, ENTRY_KEY)?; | ||||
|  | ||||
|     if let Ok(key) = key_entry.get_password() { | ||||
|         let key = hex::decode(key)?; | ||||
|         return Ok(Key::<Aes256Gcm>::clone_from_slice(&key)); | ||||
|     } | ||||
|  | ||||
|     let key = Aes256Gcm::generate_key(OsRng); | ||||
|     let encoded_key = hex::encode(key); | ||||
|  | ||||
|     key_entry.set_password(&encoded_key)?; | ||||
|  | ||||
|     Ok(key) | ||||
| } | ||||
|  | ||||
| pub(crate) fn encrypt(data: &str) -> Result<String, anyhow::Error> { | ||||
|     let master_key = get_master_key()?; | ||||
|     let cipher = Aes256Gcm::new(&master_key); | ||||
|     let nonce = Aes256Gcm::generate_nonce(&mut OsRng); | ||||
|     let cipher_text = cipher.encrypt(&nonce, data.as_bytes())?; | ||||
|  | ||||
|     let mut encrypted = nonce.to_vec(); | ||||
|     encrypted.extend_from_slice(&cipher_text); | ||||
|     Ok(hex::encode(encrypted)) | ||||
| } | ||||
|  | ||||
| pub(crate) fn decrypt(encrypted: &str) -> Result<String, anyhow::Error> { | ||||
|     let master_key = get_master_key()?; | ||||
|     let encrypted = hex::decode(encrypted)?; | ||||
|     let nonce = Nonce::<U12>::from_slice(&encrypted[..12]); | ||||
|     let cipher_text = &encrypted[12..]; | ||||
|     let cipher = Aes256Gcm::new(&master_key); | ||||
|     let plain_text = cipher.decrypt(nonce, cipher_text)?; | ||||
|  | ||||
|     String::from_utf8(plain_text).map_err(|err| err.into()) | ||||
| } | ||||
| @@ -2,93 +2,26 @@ | ||||
|     all(not(debug_assertions), target_os = "windows"), | ||||
|     windows_subsystem = "windows" | ||||
| )] | ||||
|  | ||||
| use crate::utils::get_openssl_conf_path; | ||||
| use env_logger::Env; | ||||
| use gpcommon::{Client, ClientStatus, VpnStatus}; | ||||
| use log::{info, warn}; | ||||
| use serde::Serialize; | ||||
| 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; | ||||
| mod crypto; | ||||
| mod settings; | ||||
| mod setup; | ||||
| mod storage; | ||||
| mod utils; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| struct VpnStatusPayload { | ||||
|     status: VpnStatus, | ||||
| } | ||||
|  | ||||
| fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let client = Arc::new(Client::default()); | ||||
|     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 { | ||||
|         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) { | ||||
|                     warn!("Error emitting event: {}", err); | ||||
|                 } | ||||
|             } | ||||
|             ClientStatus::Service(is_online) => { | ||||
|                 if let Err(err) = app_handle.emit_all("service-status-changed", is_online) { | ||||
|                     warn!("Error emitting event: {}", err); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         let _ = client_clone.run().await; | ||||
|     }); | ||||
|  | ||||
|     app.manage(client); | ||||
|  | ||||
|     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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     // env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); | ||||
|     tauri::Builder::default() | ||||
|         .plugin( | ||||
|             tauri_plugin_log::Builder::default() | ||||
|                 .targets([ | ||||
|                     LogTarget::LogDir, | ||||
|                     LogTarget::Stdout, /*LogTarget::Webview*/ | ||||
|                 ]) | ||||
|                 .targets([LogTarget::LogDir, LogTarget::Stdout]) | ||||
|                 .level(log::LevelFilter::Info) | ||||
|                 .with_colors(Default::default()) | ||||
|                 .build(), | ||||
|         ) | ||||
|         .plugin(tauri_plugin_store::Builder::default().build()) | ||||
|         .setup(setup) | ||||
|         .setup(setup::setup) | ||||
|         .invoke_handler(tauri::generate_handler![ | ||||
|             commands::service_online, | ||||
|             commands::vpn_status, | ||||
| @@ -98,6 +31,9 @@ fn main() { | ||||
|             commands::os_version, | ||||
|             commands::openssl_config, | ||||
|             commands::update_openssl_config, | ||||
|             commands::store_get, | ||||
|             commands::store_set, | ||||
|             commands::store_save, | ||||
|         ]) | ||||
|         .run(tauri::generate_context!()) | ||||
|         .expect("error while running tauri application"); | ||||
|   | ||||
							
								
								
									
										19
									
								
								gpgui/src-tauri/src/settings.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								gpgui/src-tauri/src/settings.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| use crate::storage::{AppStorage, KeyHint}; | ||||
| use serde::Deserialize; | ||||
| use tauri::{AppHandle, Manager}; | ||||
|  | ||||
| const STORAGE_KEY: &str = "SETTINGS_DATA"; | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| struct Settings { | ||||
|     #[serde(rename = "customOpenSSL")] | ||||
|     custom_openssl: bool, | ||||
| } | ||||
|  | ||||
| pub(crate) fn is_custom_openssl_enabled(app_handle: &AppHandle) -> bool { | ||||
|     let app_storage = app_handle.state::<AppStorage>(); | ||||
|     let hint = KeyHint::new(STORAGE_KEY, false); | ||||
|     let settings = app_storage.get::<Settings>(hint); | ||||
|  | ||||
|     settings.map_or(false, |settings| settings.custom_openssl) | ||||
| } | ||||
							
								
								
									
										81
									
								
								gpgui/src-tauri/src/setup.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								gpgui/src-tauri/src/setup.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| use crate::{settings, storage::AppStorage, utils::get_openssl_conf_path}; | ||||
| use gpcommon::{Client, ClientStatus, VpnStatus}; | ||||
| use log::{info, warn}; | ||||
| use serde::Serialize; | ||||
| use std::sync::Arc; | ||||
| use tauri::Manager; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| struct VpnStatusPayload { | ||||
|     status: VpnStatus, | ||||
| } | ||||
|  | ||||
| fn setup_window(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn setup_app_storage(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let app_handle = app.app_handle(); | ||||
|     let app_storage = AppStorage::new(app_handle); | ||||
|  | ||||
|     app.manage(app_storage); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn setup_app_env(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let app_handle = app.app_handle(); | ||||
|     let use_custom_openssl = settings::is_custom_openssl_enabled(&app_handle); | ||||
|  | ||||
|     if use_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(()) | ||||
| } | ||||
|  | ||||
| fn setup_vpn_client(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let app_handle = app.handle(); | ||||
|     let client = Arc::new(Client::default()); | ||||
|     let client_clone = client.clone(); | ||||
|  | ||||
|     app.manage(client_clone); | ||||
|  | ||||
|     tauri::async_runtime::spawn(async move { | ||||
|         client.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) { | ||||
|                     warn!("Error emitting event: {}", err); | ||||
|                 } | ||||
|             } | ||||
|             ClientStatus::Service(is_online) => { | ||||
|                 if let Err(err) = app_handle.emit_all("service-status-changed", is_online) { | ||||
|                     warn!("Error emitting event: {}", err); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         let _ = client.run().await; | ||||
|     }); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub(crate) fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     setup_window(app)?; | ||||
|     setup_app_storage(app)?; | ||||
|     setup_app_env(app)?; | ||||
|     setup_vpn_client(app)?; | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										87
									
								
								gpgui/src-tauri/src/storage.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								gpgui/src-tauri/src/storage.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| use crate::crypto::{decrypt, encrypt}; | ||||
| use log::warn; | ||||
| use serde::{de::DeserializeOwned, Deserialize}; | ||||
| use std::fmt::Debug; | ||||
| use tauri::{AppHandle, Manager, Wry}; | ||||
| use tauri_plugin_store::{with_store, Error, StoreCollection}; | ||||
|  | ||||
| const STORE_PATH: &str = ".data.json"; | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub(crate) struct KeyHint<'a> { | ||||
|     key: &'a str, | ||||
|     encrypted: bool, | ||||
| } | ||||
|  | ||||
| impl<'a> KeyHint<'a> { | ||||
|     pub(crate) fn new(key: &'a str, encrypted: bool) -> Self { | ||||
|         Self { key, encrypted } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) struct AppStorage<'a> { | ||||
|     path: &'a str, | ||||
|     app_handle: AppHandle<Wry>, | ||||
| } | ||||
|  | ||||
| impl AppStorage<'_> { | ||||
|     pub(crate) fn new(app_handle: AppHandle<Wry>) -> Self { | ||||
|         Self { | ||||
|             path: STORE_PATH, | ||||
|             app_handle, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get<T: DeserializeOwned + Debug>(&self, hint: KeyHint) -> Option<T> { | ||||
|         let stores = self.app_handle.state::<StoreCollection<Wry>>(); | ||||
|         with_store(self.app_handle.clone(), stores, self.path, |store| { | ||||
|             store | ||||
|                 .get(hint.key) | ||||
|                 .ok_or_else(|| Error::Deserialize("Value not found".into())) | ||||
|                 .and_then(|value| { | ||||
|                     if !hint.encrypted { | ||||
|                         return Ok(serde_json::from_value::<T>(value.clone())?); | ||||
|                     } | ||||
|  | ||||
|                     let value = value | ||||
|                         .as_str() | ||||
|                         .ok_or_else(|| Error::Deserialize("Value is not a string".into()))?; | ||||
|                     let value = decrypt(value).map_err(|err| { | ||||
|                         Error::Deserialize(format!("Failed to decrypt value: {}", err).into()) | ||||
|                     })?; | ||||
|  | ||||
|                     Ok(serde_json::from_str::<T>(&value)?) | ||||
|                 }) | ||||
|         }) | ||||
|         .map_err(|err| warn!("Error getting value: {:?}", err)) | ||||
|         .ok() | ||||
|     } | ||||
|  | ||||
|     pub fn set<T: serde::Serialize>(&self, hint: KeyHint, value: &T) -> Result<(), Error> { | ||||
|         let stores = self.app_handle.state::<StoreCollection<Wry>>(); | ||||
|  | ||||
|         with_store(self.app_handle.clone(), stores, self.path, |store| { | ||||
|             let value = if hint.encrypted { | ||||
|                 let json_str = serde_json::to_string(value)?; | ||||
|                 let encrypted = encrypt(&json_str).map_err(|err| { | ||||
|                     Error::Serialize(format!("Failed to encrypt value: {}", err).into()) | ||||
|                 })?; | ||||
|                 serde_json::to_value(encrypted)? | ||||
|             } else { | ||||
|                 serde_json::to_value(value)? | ||||
|             }; | ||||
|  | ||||
|             store.insert(hint.key.to_string(), value)?; | ||||
|             Ok(()) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn save(&self) -> Result<(), Error> { | ||||
|         let stores = self.app_handle.state::<StoreCollection<Wry>>(); | ||||
|          | ||||
|         with_store(self.app_handle.clone(), stores, self.path, |store| { | ||||
|             store.save()?; | ||||
|             Ok(()) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @@ -14,7 +14,11 @@ export const portalGatewaysAtom = atom<GatewayData[]>((get) => { | ||||
| }); | ||||
|  | ||||
| export const selectedGatewayAtom = atom( | ||||
|   (get) => get(currentPortalDataAtom).selectedGateway, | ||||
|   (get) => { | ||||
|     const { selectedGateway } = get(currentPortalDataAtom); | ||||
|     const gateways = get(portalGatewaysAtom); | ||||
|     return gateways.find(({ name }) => name === selectedGateway); | ||||
|   }, | ||||
|   async (get, set, update: string) => { | ||||
|     const portalData = get(currentPortalDataAtom); | ||||
|     await set(updatePortalDataAtom, { ...portalData, selectedGateway: update }); | ||||
|   | ||||
| @@ -62,7 +62,7 @@ export const loginPortalAtom = atom( | ||||
|     } | ||||
|  | ||||
|     // Here, we have got the portal config successfully, refresh the cached portal data | ||||
|     const previousSelectedGateway = get(selectedGatewayAtom); | ||||
|     const previousSelectedGateway = get(selectedGatewayAtom)?.name; | ||||
|     const selectedGateway = gateways.find( | ||||
|       ({ name }) => name === previousSelectedGateway | ||||
|     ); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { atom } from "jotai"; | ||||
| import { atomWithDefault } from "jotai/utils"; | ||||
| import { PortalCredential } from "../services/portalService"; | ||||
| import { atomWithTauriStorage } from "../services/storeService"; | ||||
| import { atomWithTauriStorage } from "../services/storageService"; | ||||
| import { unwrap } from "./unwrap"; | ||||
|  | ||||
| export type GatewayData = { | ||||
| @@ -31,7 +31,11 @@ const DEFAULT_APP_DATA: AppData = { | ||||
|   clearCookies: true, | ||||
| }; | ||||
|  | ||||
| export const appDataAtom = atomWithTauriStorage("APP_DATA", DEFAULT_APP_DATA); | ||||
| const keyHint = { | ||||
|   key: "APP_DATA", | ||||
|   encrypted: true, | ||||
| }; | ||||
| export const appDataAtom = atomWithTauriStorage(keyHint, DEFAULT_APP_DATA); | ||||
| const unwrappedAppDataAtom = atom( | ||||
|   (get) => get(unwrap(appDataAtom)) || DEFAULT_APP_DATA | ||||
| ); | ||||
| @@ -51,6 +55,11 @@ export const currentPortalDataAtom = atom<PortalData>((get) => { | ||||
|   return portalData || { address: portalAddress, gateways: [] }; | ||||
| }); | ||||
|  | ||||
| export const allPortalsAtom = atom((get) => { | ||||
|   const { portals } = get(unwrappedAppDataAtom); | ||||
|   return portals.map(({ address }) => address); | ||||
| }); | ||||
|  | ||||
| export const updatePortalDataAtom = atom( | ||||
|   null, | ||||
|   async (get, set, update: PortalData) => { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import settingsService, { | ||||
|   DEFAULT_SETTINGS_DATA, | ||||
|   SETTINGS_DATA, | ||||
| } from "../services/settingsService"; | ||||
| import { atomWithTauriStorage } from "../services/storeService"; | ||||
| import { atomWithTauriStorage } from "../services/storageService"; | ||||
| import { unwrap } from "./unwrap"; | ||||
|  | ||||
| const settingsDataAtom = atomWithTauriStorage( | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import vpnService from "../services/vpnService"; | ||||
| import { selectedGatewayAtom, switchGatewayAtom } from "./gateway"; | ||||
| import { notifyErrorAtom, notifySuccessAtom } from "./notification"; | ||||
| import { unwrap } from "./unwrap"; | ||||
| import { portalAddressAtom } from "./portal"; | ||||
|  | ||||
| export type Status = | ||||
|   | "disconnected" | ||||
| @@ -75,14 +76,16 @@ export const statusTextAtom = atom<string>((get) => { | ||||
|  | ||||
|   if (status === "connected") { | ||||
|     const selectedGateway = get(selectedGatewayAtom); | ||||
|     return selectedGateway | ||||
|       ? `Gateway: ${selectedGateway}` | ||||
|       : statusTextMap[status]; | ||||
|     const portalAddress = get(portalAddressAtom); | ||||
|  | ||||
|     return selectedGateway?.address === portalAddress | ||||
|       ? statusTextMap[status] | ||||
|       : selectedGateway?.address!; | ||||
|   } | ||||
|  | ||||
|   if (switchingGateway) { | ||||
|     const selectedGateway = get(selectedGatewayAtom); | ||||
|     return `Switching to ${selectedGateway}`; | ||||
|     return `Switching to ${selectedGateway?.name}`; | ||||
|   } | ||||
|  | ||||
|   return statusTextMap[status]; | ||||
|   | ||||
| @@ -1,13 +1,8 @@ | ||||
| import { | ||||
|   Box, | ||||
|   CssBaseline, | ||||
|   ThemeProvider, | ||||
|   createTheme, | ||||
|   useMediaQuery, | ||||
| } from "@mui/material"; | ||||
| import React, { Suspense, useMemo } from "react"; | ||||
| import { Box, CssBaseline, ThemeProvider } from "@mui/material"; | ||||
| import React, { Suspense } from "react"; | ||||
| import { createRoot } from "react-dom/client"; | ||||
| import "./styles.css"; | ||||
| import useGlobalTheme from "./useGlobalTheme"; | ||||
|  | ||||
| function Loading() { | ||||
|   console.warn("Loading rendered"); | ||||
| @@ -27,16 +22,7 @@ function Loading() { | ||||
| } | ||||
|  | ||||
| function AppShell({ children }: { children: React.ReactNode }) { | ||||
|   const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); | ||||
|   const theme = useMemo( | ||||
|     () => | ||||
|       createTheme({ | ||||
|         palette: { | ||||
|           mode: prefersDarkMode ? "dark" : "light", | ||||
|         }, | ||||
|       }), | ||||
|     [prefersDarkMode] | ||||
|   ); | ||||
|   const theme = useGlobalTheme(); | ||||
|  | ||||
|   return ( | ||||
|     <React.StrictMode> | ||||
|   | ||||
							
								
								
									
										31
									
								
								gpgui/src/components/AppShell/useGlobalTheme.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								gpgui/src/components/AppShell/useGlobalTheme.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { createTheme, useMediaQuery } from "@mui/material"; | ||||
| import { useMemo } from "react"; | ||||
|  | ||||
| export default function useGlobalTheme() { | ||||
|   const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); | ||||
|   return useMemo( | ||||
|     () => | ||||
|       createTheme({ | ||||
|         palette: { | ||||
|           mode: prefersDarkMode ? "dark" : "light", | ||||
|         }, | ||||
|         components: { | ||||
|           MuiButton: { | ||||
|             styleOverrides: { | ||||
|               root: { | ||||
|                 textTransform: "none", | ||||
|               }, | ||||
|             }, | ||||
|           }, | ||||
|           MuiTab: { | ||||
|             styleOverrides: { | ||||
|               root: { | ||||
|                 textTransform: "none", | ||||
|               }, | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }), | ||||
|     [prefersDarkMode] | ||||
|   ); | ||||
| } | ||||
| @@ -57,7 +57,7 @@ export default function PasswordAuth() { | ||||
|             <Button | ||||
|               variant="outlined" | ||||
|               onClick={cancelPasswordAuth} | ||||
|               sx={{ flex: 1, textTransform: "none" }} | ||||
|               sx={{ flex: 1 }} | ||||
|             > | ||||
|               Cancel | ||||
|             </Button> | ||||
| @@ -66,7 +66,6 @@ export default function PasswordAuth() { | ||||
|               type="submit" | ||||
|               loading={loading} | ||||
|               disabled={loading} | ||||
|               sx={{ flex: 1, textTransform: "none" }} | ||||
|             > | ||||
|               Login | ||||
|             </LoadingButton> | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| import { Button, TextField } from "@mui/material"; | ||||
| import { Autocomplete, Button, TextField } from "@mui/material"; | ||||
| import { useAtom, useAtomValue, useSetAtom } from "jotai"; | ||||
| import { ChangeEvent } from "react"; | ||||
| import { ChangeEvent, useState } from "react"; | ||||
| import { | ||||
|   cancelConnectPortalAtom, | ||||
|   connectPortalAtom, | ||||
| } from "../../atoms/connectPortal"; | ||||
| import { switchGatewayAtom } from "../../atoms/gateway"; | ||||
| import { portalAddressAtom } from "../../atoms/portal"; | ||||
| import { allPortalsAtom, portalAddressAtom } from "../../atoms/portal"; | ||||
| import { | ||||
|   backgroundServiceStartedAtom, | ||||
|   isProcessingAtom, | ||||
| @@ -26,6 +26,7 @@ function normalizePortalAddress(input: string) { | ||||
|  | ||||
| export default function PortalForm() { | ||||
|   const backgroundServiceStarted = useAtomValue(backgroundServiceStartedAtom); | ||||
|   const allPortals = useAtomValue(allPortalsAtom); | ||||
|   const [portalAddress, setPortalAddress] = useAtom(portalAddressAtom); | ||||
|   // Use useAtom instead of useSetAtom, otherwise the onMount of the atom is not triggered | ||||
|   const [, connectPortal] = useAtom(connectPortalAtom); | ||||
| @@ -35,8 +36,10 @@ export default function PortalForm() { | ||||
|   const disconnectVpn = useSetAtom(disconnectVpnAtom); | ||||
|   const switchingGateway = useAtomValue(switchGatewayAtom); | ||||
|  | ||||
|   function handlePortalAddressChange(e: ChangeEvent<HTMLInputElement>) { | ||||
|     setPortalAddress(normalizePortalAddress(e.target.value)); | ||||
|   const readOnly = status !== "disconnected" || switchingGateway; | ||||
|  | ||||
|   function handlePortalAddressChange(e: unknown, value: string) { | ||||
|     setPortalAddress(normalizePortalAddress(value)); | ||||
|   } | ||||
|  | ||||
|   function handleSubmit(e: ChangeEvent<HTMLFormElement>) { | ||||
| @@ -46,16 +49,35 @@ export default function PortalForm() { | ||||
|  | ||||
|   return ( | ||||
|     <form onSubmit={handleSubmit} data-tauri-drag-region> | ||||
|       <TextField | ||||
|         autoFocus | ||||
|         label="Portal address" | ||||
|         placeholder="Hostname or IP address" | ||||
|         fullWidth | ||||
|       <Autocomplete | ||||
|         freeSolo | ||||
|         options={allPortals} | ||||
|         inputValue={portalAddress} | ||||
|         onInputChange={handlePortalAddressChange} | ||||
|         readOnly={readOnly} | ||||
|         forcePopupIcon={!readOnly} | ||||
|         disableClearable | ||||
|         size="small" | ||||
|         value={portalAddress} | ||||
|         onChange={handlePortalAddressChange} | ||||
|         InputProps={{ readOnly: status !== "disconnected" || switchingGateway }} | ||||
|         sx={{ mb: 1 }} | ||||
|         sx={{ | ||||
|           mb: 1, | ||||
|         }} | ||||
|         componentsProps={{ | ||||
|           paper: { | ||||
|             sx: { | ||||
|               "& .MuiAutocomplete-listbox .MuiAutocomplete-option": { | ||||
|                 minHeight: "auto", | ||||
|               }, | ||||
|             }, | ||||
|           }, | ||||
|         }} | ||||
|         renderInput={(params) => ( | ||||
|           <TextField | ||||
|             {...params} | ||||
|             autoFocus | ||||
|             label="Portal address" | ||||
|             placeholder="Hostname or IP address" | ||||
|           /> | ||||
|         )} | ||||
|       /> | ||||
|  | ||||
|       {status === "disconnected" && !switchingGateway && ( | ||||
| @@ -64,7 +86,6 @@ export default function PortalForm() { | ||||
|           type="submit" | ||||
|           variant="contained" | ||||
|           disabled={!backgroundServiceStarted} | ||||
|           sx={{ textTransform: "none" }} | ||||
|         > | ||||
|           Connect | ||||
|         </Button> | ||||
| @@ -81,19 +102,13 @@ export default function PortalForm() { | ||||
|             switchingGateway | ||||
|           } | ||||
|           onClick={cancelConnectPortal} | ||||
|           sx={{ textTransform: "none" }} | ||||
|         > | ||||
|           Cancel | ||||
|         </Button> | ||||
|       )} | ||||
|  | ||||
|       {status === "connected" && ( | ||||
|         <Button | ||||
|           fullWidth | ||||
|           variant="contained" | ||||
|           onClick={disconnectVpn} | ||||
|           sx={{ textTransform: "none" }} | ||||
|         > | ||||
|         <Button fullWidth variant="contained" onClick={disconnectVpn}> | ||||
|           Disconnect | ||||
|         </Button> | ||||
|       )} | ||||
|   | ||||
| @@ -1,7 +1,18 @@ | ||||
| import { GppBad, VerifiedUser as VerifiedIcon } from "@mui/icons-material"; | ||||
| import { Box, CircularProgress, styled, useTheme } from "@mui/material"; | ||||
| import { useAtomValue } from "jotai"; | ||||
| import { | ||||
|   Box, | ||||
|   Button, | ||||
|   CircularProgress, | ||||
|   Tooltip, | ||||
|   styled, | ||||
|   useTheme, | ||||
| } from "@mui/material"; | ||||
| import { useAtomValue, useSetAtom } from "jotai"; | ||||
| import { BeatLoader } from "react-spinners"; | ||||
| import { | ||||
|   openGatewaySwitcherAtom, | ||||
|   selectedGatewayAtom, | ||||
| } from "../../atoms/gateway"; | ||||
| import { isProcessingAtom, statusAtom } from "../../atoms/status"; | ||||
|  | ||||
| function useStatusColor() { | ||||
| @@ -59,11 +70,48 @@ function ProcessingIcon() { | ||||
|   return <BeatLoader color={theme.palette.info.main} />; | ||||
| } | ||||
|  | ||||
| const ConnectedIcon = styled(VerifiedIcon)(({ theme }) => ({ | ||||
|   position: "relative", | ||||
|   fontSize: 80, | ||||
|   color: theme.palette.success.main, | ||||
| })); | ||||
| const ConnectedIcon = () => { | ||||
|   const selectedGateway = useAtomValue(selectedGatewayAtom); | ||||
|   const openGatewaySwitcher = useSetAtom(openGatewaySwitcherAtom); | ||||
|  | ||||
|   return ( | ||||
|     <Box | ||||
|       sx={{ | ||||
|         display: "flex", | ||||
|         flexDirection: "column", | ||||
|         alignItems: "center", | ||||
|       }} | ||||
|     > | ||||
|       <VerifiedIcon | ||||
|         sx={{ | ||||
|           fontSize: 70, | ||||
|           color: (theme) => theme.palette.success.main, | ||||
|         }} | ||||
|       /> | ||||
|       <Tooltip title={`Connected to ${selectedGateway?.name}`}> | ||||
|         <Button | ||||
|           sx={{ | ||||
|             position: "relative", | ||||
|             zIndex: 1, | ||||
|             fontSize: "0.75rem", | ||||
|             fontWeight: "bold", | ||||
|             display: "block", | ||||
|             width: 100, | ||||
|             mt: 0.2, | ||||
|             padding: 0.2, | ||||
|             overflow: "hidden", | ||||
|             textOverflow: "ellipsis", | ||||
|           }} | ||||
|           size="small" | ||||
|           color="success" | ||||
|           onClick={openGatewaySwitcher} | ||||
|         > | ||||
|           {selectedGateway?.name} | ||||
|         </Button> | ||||
|       </Tooltip> | ||||
|     </Box> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const IconContainer = styled(Box)(({ theme }) => | ||||
|   theme.unstable_sx({ | ||||
|   | ||||
| @@ -20,7 +20,7 @@ export default function GatewaySwitcher() { | ||||
|     gatewaySwitcherVisibleAtom | ||||
|   ); | ||||
|   const gateways = useAtomValue(portalGatewaysAtom); | ||||
|   const selectedGateway = useAtomValue(selectedGatewayAtom); | ||||
|   const selectedGateway = useAtomValue(selectedGatewayAtom)?.name; | ||||
|   const switchGateway = useSetAtom(switchGatewayAtom); | ||||
|  | ||||
|   const handleClose = () => { | ||||
|   | ||||
| @@ -43,14 +43,12 @@ export default function SettingsPanel() { | ||||
|               value="simulation" | ||||
|               icon={<Devices />} | ||||
|               iconPosition="start" | ||||
|               sx={{ textTransform: "none" }} | ||||
|             /> | ||||
|             <Tab | ||||
|               label="OpenSSL" | ||||
|               value="openssl" | ||||
|               icon={<Https />} | ||||
|               iconPosition="start" | ||||
|               sx={{ textTransform: "none" }} | ||||
|             /> | ||||
|           </TabList> | ||||
|           <Box sx={{ flex: 1 }}> | ||||
| @@ -61,12 +59,8 @@ export default function SettingsPanel() { | ||||
|       </Box> | ||||
|       <Box sx={{ flexShrink: 0, borderTop: 1, borderColor: "divider" }}> | ||||
|         <DialogActions> | ||||
|           <Button sx={{ textTransform: "none" }} onClick={closeWindow}> | ||||
|             Cancel | ||||
|           </Button> | ||||
|           <Button sx={{ textTransform: "none" }} onClick={save}> | ||||
|             Save | ||||
|           </Button> | ||||
|           <Button onClick={closeWindow}>Cancel</Button> | ||||
|           <Button onClick={save}>Save</Button> | ||||
|         </DialogActions> | ||||
|       </Box> | ||||
|     </Box> | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| import { Box } from "@mui/material"; | ||||
| import { useAtomValue } from "jotai"; | ||||
| import { appDataAtom } from "../atoms/portal"; | ||||
| import { renderToRoot } from "../components/AppShell"; | ||||
| import ConnectForm from "../components/ConnectForm"; | ||||
| import ConnectionStatus from "../components/ConnectionStatus"; | ||||
| @@ -8,6 +10,10 @@ import MainMenu from "../components/MainMenu"; | ||||
| import Notification from "../components/Notification"; | ||||
|  | ||||
| export default function App() { | ||||
|   // Use the this atom to trigger loading data from the storage | ||||
|   // And render the loading indicator | ||||
|   useAtomValue(appDataAtom); | ||||
|  | ||||
|   return ( | ||||
|     <Box data-tauri-drag-region padding={2} paddingBottom={0}> | ||||
|       <MainMenu /> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { emit, listen } from "@tauri-apps/api/event"; | ||||
| import invokeCommand from "../utils/invokeCommand"; | ||||
| import settingsService from "./settingsService"; | ||||
|  | ||||
| export type AuthData = { | ||||
|   username: string; | ||||
| @@ -30,9 +31,12 @@ class AuthService { | ||||
|  | ||||
|   // binding: "POST" | "REDIRECT" | ||||
|   async samlLogin(binding: string, request: string, clearCookies: boolean) { | ||||
|     const { userAgent } = await settingsService.getSimulation(); | ||||
|  | ||||
|     return invokeCommand<AuthData>("saml_login", { | ||||
|       binding, | ||||
|       request, | ||||
|       userAgent: `${userAgent} ${navigator.userAgent}`, | ||||
|       clearCookies, | ||||
|     }); | ||||
|   } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { Body, ResponseType, fetch } from "@tauri-apps/api/http"; | ||||
| import { parseXml } from "../utils/parseXml"; | ||||
| import settingsService from "./settingsService"; | ||||
|  | ||||
| type LoginParams = { | ||||
|   user: string; | ||||
| @@ -15,6 +16,9 @@ class GatewayService { | ||||
|       throw new Error("Gateway address is required"); | ||||
|     } | ||||
|  | ||||
|     const { userAgent, clientOS, osVersion } = | ||||
|       await settingsService.getSimulation(); | ||||
|  | ||||
|     const loginUrl = `https://${gateway}/ssl-vpn/login.esp`; | ||||
|     const body = Body.form({ | ||||
|       prot: "https:", | ||||
| @@ -25,8 +29,8 @@ class GatewayService { | ||||
|       direct: "yes", | ||||
|       "ipv6-support": "yes", | ||||
|       clientVer: "4100", | ||||
|       clientos: "Linux", | ||||
|       "os-version": "Linux", | ||||
|       clientos: clientOS, | ||||
|       "os-version": osVersion, | ||||
|       server: gateway, | ||||
|       user, | ||||
|       passwd: passwd || "", | ||||
| @@ -38,7 +42,7 @@ class GatewayService { | ||||
|     const response = await fetch<string>(loginUrl, { | ||||
|       method: "POST", | ||||
|       headers: { | ||||
|         "User-Agent": "PAN GlobalProtect", | ||||
|         "User-Agent": userAgent, | ||||
|       }, | ||||
|       responseType: ResponseType.Text, | ||||
|       body, | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { Body, ResponseType, fetch } from "@tauri-apps/api/http"; | ||||
| import ErrorWithTitle from "../utils/ErrorWithTitle"; | ||||
| import { parseXml } from "../utils/parseXml"; | ||||
| import { Gateway } from "./types"; | ||||
| import settingsService from "./settingsService"; | ||||
|  | ||||
| export type SamlPrelogin = { | ||||
|   isSamlAuth: true; | ||||
| @@ -37,12 +38,15 @@ export type PortalCredential = { | ||||
| class PortalService { | ||||
|   async prelogin(portal: string): Promise<Prelogin> { | ||||
|     const preloginUrl = `https://${portal}/global-protect/prelogin.esp`; | ||||
|     const { userAgent, clientOS, osVersion } = | ||||
|       await settingsService.getSimulation(); | ||||
|  | ||||
|     let response; | ||||
|     try { | ||||
|       response = await fetch<string>(preloginUrl, { | ||||
|         method: "POST", | ||||
|         headers: { | ||||
|           "User-Agent": "PAN GlobalProtect", | ||||
|           "User-Agent": userAgent, | ||||
|         }, | ||||
|         responseType: ResponseType.Text, | ||||
|         query: { | ||||
| @@ -51,8 +55,8 @@ class PortalService { | ||||
|         body: Body.form({ | ||||
|           tmp: "tmp", | ||||
|           clientVer: "4100", | ||||
|           clientos: "Linux", | ||||
|           "os-version": "Linux", | ||||
|           clientos: clientOS, | ||||
|           "os-version": osVersion, | ||||
|           "ipv6-support": "yes", | ||||
|           "default-browser": "0", | ||||
|           "cas-support": "yes", | ||||
| @@ -118,6 +122,9 @@ class PortalService { | ||||
|   } | ||||
|  | ||||
|   async fetchConfig(portal: string, params: PortalCredential) { | ||||
|     const { userAgent, clientOS, osVersion, clientVersion } = | ||||
|       await settingsService.getSimulation(); | ||||
|  | ||||
|     const { | ||||
|       user, | ||||
|       passwd, | ||||
| @@ -132,12 +139,12 @@ class PortalService { | ||||
|       inputStr: "", | ||||
|       jnlpReady: "jnlpReady", | ||||
|       computer: "Linux", // TODO | ||||
|       clientos: "Linux", | ||||
|       clientos: clientOS, | ||||
|       ok: "Login", | ||||
|       direct: "yes", | ||||
|       clientVer: "4100", | ||||
|       "os-version": "Linux", | ||||
|       clientgpversion: "6.0.1-19", | ||||
|       "os-version": osVersion, | ||||
|       clientgpversion: clientVersion, | ||||
|       "ipv6-support": "yes", | ||||
|       server: portal, | ||||
|       host: portal, | ||||
| @@ -151,7 +158,7 @@ class PortalService { | ||||
|     const response = await fetch<string>(configUrl, { | ||||
|       method: "POST", | ||||
|       headers: { | ||||
|         "User-Agent": "PAN GlobalProtect", | ||||
|         "User-Agent": userAgent, | ||||
|       }, | ||||
|       responseType: ResponseType.Text, | ||||
|       body, | ||||
| @@ -166,8 +173,6 @@ class PortalService { | ||||
|   } | ||||
|  | ||||
|   private parsePortalConfigResponse(response: string): PortalConfig { | ||||
|     // console.log(response); | ||||
|  | ||||
|     const result = parseXml(response); | ||||
|     const gateways = result.all("gateways list > entry").map((entry) => { | ||||
|       const address = entry.attr("name"); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { UserAttentionType, WebviewWindow } from "@tauri-apps/api/window"; | ||||
| import invokeCommand from "../utils/invokeCommand"; | ||||
| import { appStore } from "./storeService"; | ||||
| import { appStorage } from "./storageService"; | ||||
|  | ||||
| export type TabValue = "simulation" | "openssl"; | ||||
| const SETTINGS_WINDOW_LABEL = "settings"; | ||||
| @@ -68,9 +68,10 @@ export const DEFAULT_SETTINGS_DATA: SettingsData = { | ||||
|   customOpenSSL: false, | ||||
| }; | ||||
|  | ||||
| async function getSimulationSettings(): Promise<SimulationSettings> { | ||||
| async function getSimulation(): Promise<SimulationSettings> { | ||||
|   const { clientOS, osVersion, clientVersion } = | ||||
|     (await appStore.get<SettingsData>(SETTINGS_DATA)) || DEFAULT_SETTINGS_DATA; | ||||
|     (await appStorage.get<SettingsData>(SETTINGS_DATA)) || | ||||
|     DEFAULT_SETTINGS_DATA; | ||||
|   const currentOsVersion = await getCurrentOsVersion(); | ||||
|  | ||||
|   return { | ||||
| @@ -81,8 +82,8 @@ async function getSimulationSettings(): Promise<SimulationSettings> { | ||||
|       clientVersion | ||||
|     ), | ||||
|     clientOS, | ||||
|     osVersion, | ||||
|     clientVersion, | ||||
|     osVersion: determineOsVersion(clientOS, osVersion, currentOsVersion), | ||||
|     clientVersion: clientVersion || DEFAULT_CLIENT_VERSION, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @@ -131,7 +132,7 @@ export default { | ||||
|   openSettings, | ||||
|   closeSettings, | ||||
|   getCurrentOsVersion, | ||||
|   getSimulationSettings, | ||||
|   getSimulation, | ||||
|   buildUserAgent, | ||||
|   determineOsVersion, | ||||
|   getOpenSSLConfig, | ||||
|   | ||||
							
								
								
									
										70
									
								
								gpgui/src/services/storageService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								gpgui/src/services/storageService.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| import { atom } from "jotai"; | ||||
| import { RESET, atomWithDefault } from "jotai/utils"; | ||||
| import invokeCommand from "../utils/invokeCommand"; | ||||
|  | ||||
| type SetStateActionWithReset<T> = | ||||
|   | T | ||||
|   | typeof RESET | ||||
|   | ((prev: T) => T | typeof RESET); | ||||
|  | ||||
| type KeyHint = | ||||
|   | { | ||||
|       key: string; | ||||
|       encrypted: boolean; | ||||
|     } | ||||
|   | string; | ||||
|  | ||||
| type AppStorage = { | ||||
|   get: <T>(key: KeyHint) => Promise<T | undefined>; | ||||
|   set: <T>(key: KeyHint, value: T) => Promise<void>; | ||||
|   save: () => Promise<void>; | ||||
| }; | ||||
|  | ||||
| export const appStorage: AppStorage = { | ||||
|   get: async (key) => { | ||||
|     const hint = typeof key === "string" ? { key, encrypted: false } : key; | ||||
|     return invokeCommand("store_get", { hint }); | ||||
|   }, | ||||
|   set: async (key, value) => { | ||||
|     const hint = typeof key === "string" ? { key, encrypted: false } : key; | ||||
|     return invokeCommand("store_set", { hint, value }); | ||||
|   }, | ||||
|   save: async () => { | ||||
|     return invokeCommand("store_save"); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| export function atomWithTauriStorage<T>(key: KeyHint, initialValue: T) { | ||||
|   const baseAtom = atomWithDefault<T | Promise<T>>(async () => { | ||||
|     const storedValue = await appStorage.get<T>(key); | ||||
|     if (!storedValue) { | ||||
|       return initialValue; | ||||
|     } | ||||
|     return storedValue; | ||||
|   }); | ||||
|  | ||||
|   const anAtom = atom( | ||||
|     (get) => get(baseAtom), | ||||
|     async (get, set, update: SetStateActionWithReset<T>) => { | ||||
|       const value = await get(baseAtom); | ||||
|       let newValue: T | typeof RESET; | ||||
|       if (typeof update === "function") { | ||||
|         newValue = (update as (prev: T) => T | typeof RESET)(value); | ||||
|       } else { | ||||
|         newValue = update as T | typeof RESET; | ||||
|       } | ||||
|  | ||||
|       if (newValue === RESET) { | ||||
|         set(baseAtom, initialValue); | ||||
|         await appStorage.set(key, initialValue); | ||||
|       } else { | ||||
|         set(baseAtom, newValue); | ||||
|         await appStorage.set(key, newValue); | ||||
|       } | ||||
|  | ||||
|       await appStorage.save(); | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   return anAtom; | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| import { atom } from "jotai"; | ||||
| import { RESET, atomWithDefault } from "jotai/utils"; | ||||
| import { Store } from "tauri-plugin-store-api"; | ||||
|  | ||||
| type SetStateActionWithReset<T> = | ||||
|   | T | ||||
|   | typeof RESET | ||||
|   | ((prev: T) => T | typeof RESET); | ||||
|  | ||||
| export const appStore = new Store(".settings.dat"); | ||||
|  | ||||
| export function atomWithTauriStorage<T>(key: string, initialValue: T) { | ||||
|   const baseAtom = atomWithDefault<T | Promise<T>>(async () => { | ||||
|     const storedValue = await appStore.get<T>(key); | ||||
|     if (!storedValue) { | ||||
|       return initialValue; | ||||
|     } | ||||
|     return storedValue; | ||||
|   }); | ||||
|  | ||||
|   const anAtom = atom( | ||||
|     (get) => get(baseAtom), | ||||
|     async (get, set, update: SetStateActionWithReset<T>) => { | ||||
|       const value = await get(baseAtom); | ||||
|       let newValue: T | typeof RESET; | ||||
|       if (typeof update === "function") { | ||||
|         newValue = (update as (prev: T) => T | typeof RESET)(value); | ||||
|       } else { | ||||
|         newValue = update as T | typeof RESET; | ||||
|       } | ||||
|  | ||||
|       if (newValue === RESET) { | ||||
|         set(baseAtom, initialValue); | ||||
|         await appStore.set(key, initialValue); | ||||
|       } else { | ||||
|         set(baseAtom, newValue); | ||||
|         await appStore.set(key, newValue); | ||||
|       } | ||||
|  | ||||
|       await appStore.save(); | ||||
|     } | ||||
|   ); | ||||
|  | ||||
|   return anAtom; | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { Event, listen } from "@tauri-apps/api/event"; | ||||
| import invokeCommand from "../utils/invokeCommand"; | ||||
| import settingsService from "./settingsService"; | ||||
|  | ||||
| type VpnStatus = "disconnected" | "connecting" | "connected" | "disconnecting"; | ||||
| type VpnStatusCallback = (status: VpnStatus) => void; | ||||
| @@ -64,7 +65,8 @@ class VpnService { | ||||
|   } | ||||
|  | ||||
|   async connect(server: string, cookie: string) { | ||||
|     return invokeCommand("vpn_connect", { server, cookie }); | ||||
|     const { userAgent } = await settingsService.getSimulation(); | ||||
|     return invokeCommand("vpn_connect", { server, cookie, userAgent }); | ||||
|   } | ||||
|  | ||||
|   async disconnect() { | ||||
|   | ||||
| @@ -8,8 +8,9 @@ edition = "2021" | ||||
| [dependencies] | ||||
| gpcommon = { path = "../gpcommon" } | ||||
| tokio = { version = "1", features = ["full"] } | ||||
| env_logger = "0.10" | ||||
| log = "0.4" | ||||
| fern = "0.6" | ||||
| humantime = "2.1" | ||||
| # warp = "0.3" | ||||
| # aes-gcm = "0.10" | ||||
| # procfs = "0.15" | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| include!(concat!(env!("OUT_DIR"), "/client_hash.rs")); | ||||
|  | ||||
| // use aes_gcm::{aead::OsRng, Aes256Gcm, KeyInit}; | ||||
| use gpcommon::{server, SOCKET_PATH}; | ||||
| use env_logger::Env; | ||||
| use log::error; | ||||
| use std::fs::File; | ||||
| use tokio::signal; | ||||
|  | ||||
| // static mut HTTP_PORT: u16 = 0; | ||||
| @@ -96,10 +95,10 @@ use tokio::signal; | ||||
| //     println!("Shutting down http server"); | ||||
| // } | ||||
|  | ||||
| const LOG_FILE: &str = "/var/log/gpservice.log"; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); | ||||
|  | ||||
|     // println!("{GPCLIENT_HASH}"); | ||||
|  | ||||
|     // unsafe { | ||||
| @@ -110,6 +109,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // start_http_server().await; | ||||
|     // server::start().await | ||||
|  | ||||
|     let log_file = File::create(LOG_FILE)?; | ||||
|     fern::Dispatch::new() | ||||
|         .format(|out, message, record| { | ||||
|             out.finish(format_args!( | ||||
|                 "[{} {} {}] {}", | ||||
|                 humantime::format_rfc3339_millis(std::time::SystemTime::now()), | ||||
|                 record.level(), | ||||
|                 record.target(), | ||||
|                 message | ||||
|             )) | ||||
|         }) | ||||
|         .level(log::LevelFilter::Info) | ||||
|         .chain(std::io::stdout()) | ||||
|         .chain(log_file) | ||||
|         .apply()?; | ||||
|  | ||||
|     if let Err(err) = server::run(SOCKET_PATH, signal::ctrl_c()).await { | ||||
|         error!("Error running server: {}", err); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user