mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			204 Commits
		
	
	
		
			v1.2.9
			...
			3674a28dee
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3674a28dee | ||
|  | 3098d1170f | ||
|  | acf7ef28ab | ||
|  | 11a374765c | ||
|  | f6ceb5ac0a | ||
|  | 94a2cd2886 | ||
|  | 867ea71144 | ||
|  | c9484248a5 | ||
|  | a10539e9c3 | ||
|  | 601f422863 | ||
|  | bf96a88e21 | ||
|  | 963b7d5407 | ||
|  | f91f0bcd17 | ||
|  | 5603157679 | ||
|  | 4191e9a19f | ||
|  | 5bc3da99ac | ||
|  | e8db014336 | ||
|  | e5fd2ba280 | ||
|  | 15e798c1e7 | ||
|  | 1af21432d4 | ||
|  | c07e232ec2 | ||
|  | d975f981cc | ||
|  | c74ce52c2d | ||
|  | da3dc10569 | ||
|  | 75db9e162f | ||
|  | 89eb42ceac | ||
|  | 54d3bb8a92 | ||
|  | a1b49fde47 | ||
|  | f42f0d248e | ||
|  | ec0bff1e36 | ||
|  | 77fda0f527 | ||
|  | 8de183a53d | ||
|  | 462428f99a | ||
|  | cbd8b0f144 | ||
|  | 5a84c99cd9 | ||
|  | d5af0e58c2 | ||
|  | 16696e3840 | ||
|  | 6df9877895 | ||
|  | 19b9b757f4 | ||
|  | 7bef2ccc68 | ||
|  | bffc5d733b | ||
|  | 8ca2610550 | ||
|  | acf184134a | ||
|  | 4a3f74f1c3 | ||
|  | b39983a0f8 | ||
|  | d6fa32d95d | ||
|  | 7c299f6e68 | ||
|  | 25e8ccd07e | ||
|  | 092123b075 | ||
|  | feb2956cc1 | ||
|  | d356839859 | ||
|  | 2ff39fd14e | ||
|  | c3d300c807 | ||
|  | ef43d10a70 | ||
|  | bd73466e48 | ||
|  | cc2c0ae34e | ||
|  | 9207f7a798 | ||
|  | 2069b7fd8e | ||
|  | f552ef6204 | ||
|  | 2761f7521a | ||
|  | c3939a774b | ||
|  | 49e5242bf2 | ||
|  | 3181d37b20 | ||
|  | 6d788a5e91 | ||
|  | 74c7549444 | ||
|  | c52ccb87f1 | ||
|  | fab25848e1 | ||
|  | 75a24c89cd | ||
|  | 15a73b7dba | ||
|  | 0adeaf9c28 | ||
|  | fe64b2cd19 | ||
|  | 5788474d7e | ||
|  | 3559834762 | ||
|  | f9926b4026 | ||
|  | cb457c4b09 | ||
|  | 5ebfe9b0f4 | ||
|  | 35266dd8bf | ||
|  | bf03d375e0 | ||
|  | 6cf909e34f | ||
|  | 343a6d03c1 | ||
|  | fab8e7591e | ||
|  | 5a485197b7 | ||
|  | 7bc02a4208 | ||
|  | 3067e6e911 | ||
|  | 5db77e8404 | ||
|  | 5714063457 | ||
|  | 41f88ed2e0 | ||
|  | 4fada9bd14 | ||
|  | b57fb993ca | ||
|  | f6d06ed978 | ||
|  | cc67de3a2b | ||
|  | e2d28c83b2 | ||
|  | a489c5881b | ||
|  | 44fd2f1d3f | ||
|  | 9c9b42b87f | ||
|  | fb2b148b72 | ||
|  | 64bec9660a | ||
|  | 0619e91bf5 | ||
|  | 048aa4799f | ||
|  | db0e8b801d | ||
|  | d03bbc339e | ||
|  | 1312d54d08 | ||
|  | 39f99d9143 | ||
|  | 7a4eb0def3 | ||
|  | d9b2094edd | ||
|  | e6118af9f3 | ||
|  | 108b4be3ec | ||
|  | 65c59e47ec | ||
|  | 177da7f3a2 | ||
|  | d5cd90373b | ||
|  | ffa99d3783 | ||
|  | 4940830885 | ||
|  | ad178fe56c | ||
|  | 829298bb84 | ||
|  | 8fe717d844 | ||
|  | dffbc64ef5 | ||
|  | b99c5a8391 | ||
|  | c2f7576d10 | ||
|  | 4327235093 | ||
|  | 0699878b92 | ||
|  | e3aba11506 | ||
|  | ff58258d5c | ||
|  | 991cf25a7b | ||
|  | 02c70150ba | ||
|  | 28d8321958 | ||
|  | e1c9180cae | ||
|  | 57df34fd1e | ||
|  | 04d180e11a | ||
|  | 6d3b127569 | ||
|  | e72b25e415 | ||
|  | 37a511c24d | ||
|  | ad7db36c92 | ||
|  | 11dc5920ef | ||
|  | e6383916c7 | ||
|  | 1d9d928b26 | ||
|  | c02ad5d46d | ||
|  | 2319c7c49c | ||
|  | e0c2c14dc3 | ||
|  | 8f27c92e7b | ||
|  | 9d6ec84c14 | ||
|  | dd81ed9519 | ||
|  | 32bd713965 | ||
|  | ba92517141 | ||
|  | 0e4e082594 | ||
|  | 3e590cab7b | ||
|  | 3e0e4cff12 | ||
|  | 692df2f2c5 | ||
|  | f2b9ffddde | ||
|  | ca38925066 | ||
|  | 8591dd7e81 | ||
|  | b07880930e | ||
|  | fceb80e10e | ||
|  | d802c56d8f | ||
|  | 386f08d0e8 | ||
|  | 9e7fb17bd3 | ||
|  | 36d9753008 | ||
|  | e5b3df9cda | ||
|  | 0dd705d0c0 | ||
|  | ce2360be61 | ||
|  | b5b7033eee | ||
|  | 9e7db4eb86 | ||
|  | bc07e3d496 | ||
|  | 452fe2f189 | ||
|  | 8a65099ca7 | ||
|  | 5c97b2df7a | ||
|  | 0d4485d754 | ||
|  | 98e641e99d | ||
|  | 6fa77cdbd2 | ||
|  | 64e6487e7e | ||
|  | e8b2c1606f | ||
|  | 84f1480653 | ||
|  | 3175855122 | ||
|  | fa8b5c1528 | ||
|  | 7b9942c7e6 | ||
|  | 011a1a0dec | ||
|  | 4a53033023 | ||
|  | 9c6ea1c4b5 | ||
|  | 3369ad4c1d | ||
|  | 25c9f2291a | ||
|  | bba3bc7e4f | ||
|  | b12b692090 | ||
|  | 1300a0cc43 | ||
|  | 165080b476 | ||
|  | d6af8a1598 | ||
|  | eef92b1d31 | ||
|  | 946ead24a4 | ||
|  | 39e57c8598 | ||
|  | 4e2e423c27 | ||
|  | 732a62f1ee | ||
|  | 9f9444a72b | ||
|  | 6352e1fb2b | ||
|  | 42cae3ff26 | ||
|  | 53c8572cf6 | ||
|  | 3f6467321f | ||
|  | 563ec48c8c | ||
|  | 3787ae164c | ||
|  | 04a24c34e8 | ||
|  | fe68248b1f | ||
|  | 47013033ec | ||
|  | 05fb9a26bd | ||
|  | 96962f957c | ||
|  | b4f9cfae67 | ||
|  | c8942984a8 | ||
|  | 3907827d0e | 
							
								
								
									
										21
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | root = true | ||||||
|  |  | ||||||
|  | [*] | ||||||
|  | charset = utf-8 | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 2 | ||||||
|  | end_of_line = lf | ||||||
|  | insert_final_newline = true | ||||||
|  | trim_trailing_whitespace = true | ||||||
|  |  | ||||||
|  | [*.{rs,toml}] | ||||||
|  | indent_size = 4 | ||||||
|  |  | ||||||
|  | [*.{c,h}] | ||||||
|  | indent_size = 4 | ||||||
|  |  | ||||||
|  | [*.{js,jsx,ts,tsx}] | ||||||
|  | indent_size = 2 | ||||||
|  |  | ||||||
|  | [*.md] | ||||||
|  | trim_trailing_whitespace = false | ||||||
							
								
								
									
										30
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,30 +0,0 @@ | |||||||
| name: CI |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: [ master ] |  | ||||||
|   pull_request: |  | ||||||
|     branches: [ master ] |  | ||||||
|   workflow_dispatch: |  | ||||||
|  |  | ||||||
| # A workflow run is made up of one or more jobs that can run sequentially or in parallel |  | ||||||
| jobs: |  | ||||||
|   build: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|       - name: Install Qt |  | ||||||
|         uses: jurplel/install-qt-action@v2 |  | ||||||
|         with: |  | ||||||
|           version: '5.15.2' |  | ||||||
|           modules: 'qtwebengine qtwebsockets' |  | ||||||
|        |  | ||||||
|       # Checkout repository and submodules |  | ||||||
|       - uses: actions/checkout@v2 |  | ||||||
|         with: |  | ||||||
|           submodules: recursive |  | ||||||
|  |  | ||||||
|       - name: Build |  | ||||||
|         run: | |  | ||||||
|           qmake CONFIG+=release |  | ||||||
|           make |  | ||||||
							
								
								
									
										61
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,61 +0,0 @@ | |||||||
| name: Publish |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   workflow_dispatch: |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   build: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|      |  | ||||||
|     steps: |  | ||||||
|       - name: Install Qt |  | ||||||
|         uses: jurplel/install-qt-action@v2 |  | ||||||
|         with: |  | ||||||
|           version: 5.15.2 |  | ||||||
|           modules: 'qtwebengine qtwebsockets' |  | ||||||
|  |  | ||||||
|       # Checkout repository and submodules |  | ||||||
|       - uses: actions/checkout@v2 |  | ||||||
|         with: |  | ||||||
|           submodules: recursive |  | ||||||
|  |  | ||||||
|       - name: Build |  | ||||||
|         run: | |  | ||||||
|           qmake CONFIG+=release |  | ||||||
|           make |  | ||||||
|  |  | ||||||
|   aur-publish: |  | ||||||
|     needs: |  | ||||||
|       - build |  | ||||||
|    |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|       - uses: actions/checkout@v2 |  | ||||||
|         with: |  | ||||||
|           fetch-depth: 0 |  | ||||||
|        |  | ||||||
|       - name: Get latest version |  | ||||||
|         id: get-version |  | ||||||
|         run: | |  | ||||||
|           echo ::set-output name=VERSION::$(git tag --sort=-v:refname --list "v[0-9]*" | head -n 1 | cut -c 2-) |  | ||||||
|            |  | ||||||
|       - name: Get the sha256sum |  | ||||||
|         id: get-sha256sum |  | ||||||
|         run: | |  | ||||||
|           echo ::set-output name=SHA::$(curl -L https://github.com/yuezk/GlobalProtect-openconnect/archive/refs/tags/v${{ steps.get-version.outputs.VERSION }}.tar.gz | sha256sum | cut -f1 -d" ") |  | ||||||
|        |  | ||||||
|       - name: Generate PKGBUILD |  | ||||||
|         run: | |  | ||||||
|           sed "s/{PKG_VERSION}/${{ steps.get-version.outputs.VERSION }}/g;s/{SOURCE_SHA}/${{ steps.get-sha256sum.outputs.SHA }}/g" PKGBUILD.template > PKGBUILD |  | ||||||
|        |  | ||||||
|       - name: Publish AUR package |  | ||||||
|         uses: KSXGitHub/github-actions-deploy-aur@v2.2.4 |  | ||||||
|         with: |  | ||||||
|           pkgname: globalprotect-openconnect |  | ||||||
|           pkgbuild: ./PKGBUILD |  | ||||||
|           commit_username: ${{ secrets.AUR_USERNAME }} |  | ||||||
|           commit_email: ${{ secrets.AUR_EMAIL }} |  | ||||||
|           ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} |  | ||||||
|           commit_message: 'Release v${{ steps.get-version.outputs.VERSION }}' |  | ||||||
|           force_push: true |  | ||||||
							
								
								
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,60 +1,35 @@ | |||||||
| # Binaries | # Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode | ||||||
| gpclient | # Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode | ||||||
| gpservice |  | ||||||
|  |  | ||||||
| # Auto generated DBus files | ### Rust ### | ||||||
| *_adaptor.cpp | # Generated by Cargo | ||||||
| *_adaptor.h | # will have compiled files and executables | ||||||
|  | debug/ | ||||||
|  | target/ | ||||||
|  |  | ||||||
| # C++ objects and libs | # These are backup files generated by rustfmt | ||||||
| *.slo | **/*.rs.bk | ||||||
| *.lo |  | ||||||
| *.o |  | ||||||
| *.a |  | ||||||
| *.la |  | ||||||
| *.lai |  | ||||||
| *.so |  | ||||||
| *.so.* |  | ||||||
| *.dll |  | ||||||
| *.dylib |  | ||||||
|  |  | ||||||
| # Qt-es | # MSVC Windows builds of rustc generate these, which store debugging information | ||||||
| object_script.*.Release | *.pdb | ||||||
| object_script.*.Debug |  | ||||||
| *_plugin_import.cpp |  | ||||||
| /.qmake.cache |  | ||||||
| /.qmake.stash |  | ||||||
| *.pro.user |  | ||||||
| *.pro.user.* |  | ||||||
| *.qbs.user |  | ||||||
| *.qbs.user.* |  | ||||||
| *.moc |  | ||||||
| moc_*.cpp |  | ||||||
| moc_*.h |  | ||||||
| qrc_*.cpp |  | ||||||
| ui_*.h |  | ||||||
| *.qmlc |  | ||||||
| *.jsc |  | ||||||
| Makefile* |  | ||||||
| *build-* |  | ||||||
| *.qm |  | ||||||
| *.prl |  | ||||||
|  |  | ||||||
| # Qt unit tests | ### VisualStudioCode ### | ||||||
| target_wrapper.* | .vscode/* | ||||||
|  | !.vscode/settings.json | ||||||
|  | !.vscode/tasks.json | ||||||
|  | !.vscode/launch.json | ||||||
|  | !.vscode/extensions.json | ||||||
|  | !.vscode/*.code-snippets | ||||||
|  |  | ||||||
| # QtCreator | # Local History for Visual Studio Code | ||||||
| *.autosave | .history/ | ||||||
|  |  | ||||||
| # QtCreator Qml | # Built Visual Studio Code Extensions | ||||||
| *.qmlproject.user | *.vsix | ||||||
| *.qmlproject.user.* |  | ||||||
|  |  | ||||||
| # QtCreator CMake | ### VisualStudioCode Patch ### | ||||||
| CMakeLists.txt.user* | # Ignore all local history of files | ||||||
|  | .history | ||||||
|  | .ionide | ||||||
|  |  | ||||||
| # QtCreator 4.8< compilation database  | # End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode | ||||||
| compile_commands.json |  | ||||||
|  |  | ||||||
| # QtCreator local machine specific files for imported projects |  | ||||||
| *creator.user* |  | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +0,0 @@ | |||||||
| [submodule "singleapplication"] |  | ||||||
| 	path = singleapplication |  | ||||||
| 	url = https://github.com/itay-grudev/SingleApplication.git |  | ||||||
|  |  | ||||||
| [submodule "plog"] |  | ||||||
| 	path = plog |  | ||||||
| 	url = https://github.com/SergiusTheBest/plog.git |  | ||||||
							
								
								
									
										52
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | { | ||||||
|  |   "cSpell.words": [ | ||||||
|  |     "authcookie", | ||||||
|  |     "bindgen", | ||||||
|  |     "clickaway", | ||||||
|  |     "clientgpversion", | ||||||
|  |     "clientos", | ||||||
|  |     "configparser", | ||||||
|  |     "consts", | ||||||
|  |     "devicename", | ||||||
|  |     "distro", | ||||||
|  |     "gpcommon", | ||||||
|  |     "gpconf", | ||||||
|  |     "gpgui", | ||||||
|  |     "gpservice", | ||||||
|  |     "humantime", | ||||||
|  |     "Immer", | ||||||
|  |     "jnlp", | ||||||
|  |     "lexopt", | ||||||
|  |     "notistack", | ||||||
|  |     "oneshot", | ||||||
|  |     "openconnect", | ||||||
|  |     "pkexec", | ||||||
|  |     "prelogin", | ||||||
|  |     "prelogon", | ||||||
|  |     "prelogonuserauthcookie", | ||||||
|  |     "repr", | ||||||
|  |     "rustc", | ||||||
|  |     "servercert", | ||||||
|  |     "shlex", | ||||||
|  |     "tauri", | ||||||
|  |     "tempdir", | ||||||
|  |     "tempfile", | ||||||
|  |     "thiserror", | ||||||
|  |     "unlisten", | ||||||
|  |     "userauthcookie", | ||||||
|  |     "vpnc", | ||||||
|  |     "vpninfo" | ||||||
|  |   ], | ||||||
|  |   "files.associations": { | ||||||
|  |     "*.css": "css", | ||||||
|  |     "errno.h": "c", | ||||||
|  |     "stdarg.h": "c", | ||||||
|  |     "wrapper.h": "c", | ||||||
|  |     "cstdlib": "c", | ||||||
|  |     "stdio.h": "c", | ||||||
|  |     "openconnect.h": "c", | ||||||
|  |     "compare": "c", | ||||||
|  |     "stdlib.h": "c", | ||||||
|  |     "vpn.h": "c" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										4739
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4739
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										12
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | [workspace] | ||||||
|  |  | ||||||
|  | members = [ | ||||||
|  |     "gpcommon", | ||||||
|  |     "gpclient", | ||||||
|  |     "gpservice", | ||||||
|  |     "gpgui/src-tauri" | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [profile.release] | ||||||
|  | strip = true | ||||||
|  | opt-level = "z" | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| TARGET = gpclient |  | ||||||
|  |  | ||||||
| QT       += core gui network websockets dbus webenginewidgets |  | ||||||
|  |  | ||||||
| greaterThan(QT_MAJOR_VERSION, 4): QT += widgets |  | ||||||
|  |  | ||||||
| CONFIG += c++11 |  | ||||||
|  |  | ||||||
| include(../singleapplication/singleapplication.pri) |  | ||||||
| DEFINES += QAPPLICATION_CLASS=QApplication |  | ||||||
|  |  | ||||||
| # The following define makes your compiler emit warnings if you use |  | ||||||
| # any Qt feature that has been marked deprecated (the exact warnings |  | ||||||
| # depend on your compiler). Please consult the documentation of the |  | ||||||
| # deprecated API in order to know how to port your code away from it. |  | ||||||
| DEFINES += QT_DEPRECATED_WARNINGS |  | ||||||
|  |  | ||||||
| INCLUDEPATH += ../plog/include |  | ||||||
|  |  | ||||||
| # You can also make your code fail to compile if it uses deprecated APIs. |  | ||||||
| # In order to do so, uncomment the following line. |  | ||||||
| # You can also select to disable deprecated APIs only up to a certain version of Qt. |  | ||||||
| #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0 |  | ||||||
| SOURCES += \ |  | ||||||
|     cdpcommand.cpp \ |  | ||||||
|     cdpcommandmanager.cpp \ |  | ||||||
|     enhancedwebview.cpp \ |  | ||||||
|     gatewayauthenticator.cpp \ |  | ||||||
|     gpgateway.cpp \ |  | ||||||
|     gphelper.cpp \ |  | ||||||
|     loginparams.cpp \ |  | ||||||
|     main.cpp \ |  | ||||||
|     normalloginwindow.cpp \ |  | ||||||
|     portalauthenticator.cpp \ |  | ||||||
|     portalconfigresponse.cpp \ |  | ||||||
|     preloginresponse.cpp \ |  | ||||||
|     samlloginwindow.cpp \ |  | ||||||
|     gpclient.cpp |  | ||||||
|  |  | ||||||
| HEADERS += \ |  | ||||||
|     cdpcommand.h \ |  | ||||||
|     cdpcommandmanager.h \ |  | ||||||
|     enhancedwebview.h \ |  | ||||||
|     gatewayauthenticator.h \ |  | ||||||
|     gpgateway.h \ |  | ||||||
|     gphelper.h \ |  | ||||||
|     loginparams.h \ |  | ||||||
|     normalloginwindow.h \ |  | ||||||
|     portalauthenticator.h \ |  | ||||||
|     portalconfigresponse.h \ |  | ||||||
|     preloginresponse.h \ |  | ||||||
|     samlloginwindow.h \ |  | ||||||
|     gpclient.h |  | ||||||
|  |  | ||||||
| FORMS += \ |  | ||||||
|     gpclient.ui \ |  | ||||||
|     normalloginwindow.ui |  | ||||||
|  |  | ||||||
| DBUS_INTERFACES += ../GPService/gpservice.xml |  | ||||||
|  |  | ||||||
| # Default rules for deployment. |  | ||||||
| target.path = /usr/bin |  | ||||||
| INSTALLS += target |  | ||||||
|  |  | ||||||
| DISTFILES += \ |  | ||||||
|     com.yuezk.qt.GPClient.svg \ |  | ||||||
|     com.yuezk.qt.gpclient.desktop |  | ||||||
|  |  | ||||||
| desktop_entry.path = /usr/share/applications/ |  | ||||||
| desktop_entry.files = com.yuezk.qt.gpclient.desktop |  | ||||||
|  |  | ||||||
| desktop_icon.path = /usr/share/pixmaps/ |  | ||||||
| desktop_icon.files = com.yuezk.qt.GPClient.svg |  | ||||||
|  |  | ||||||
| INSTALLS += desktop_entry desktop_icon |  | ||||||
|  |  | ||||||
| RESOURCES += \ |  | ||||||
|     resources.qrc |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| #include "cdpcommand.h" |  | ||||||
|  |  | ||||||
| #include <QVariantMap> |  | ||||||
| #include <QJsonDocument> |  | ||||||
| #include <QJsonObject> |  | ||||||
|  |  | ||||||
| CDPCommand::CDPCommand(QObject *parent) : QObject(parent) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CDPCommand::CDPCommand(int id, QString cmd, QVariantMap& params) : |  | ||||||
|     QObject(nullptr), |  | ||||||
|     id(id), |  | ||||||
|     cmd(cmd), |  | ||||||
|     params(¶ms) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QByteArray CDPCommand::toJson() |  | ||||||
| { |  | ||||||
|     QVariantMap payloadMap; |  | ||||||
|     payloadMap["id"] = id; |  | ||||||
|     payloadMap["method"] = cmd; |  | ||||||
|     payloadMap["params"] = *params; |  | ||||||
|  |  | ||||||
|     QJsonObject payloadJsonObject = QJsonObject::fromVariantMap(payloadMap); |  | ||||||
|     QJsonDocument payloadJson(payloadJsonObject); |  | ||||||
|  |  | ||||||
|     return payloadJson.toJson(); |  | ||||||
| } |  | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| #ifndef CDPCOMMAND_H |  | ||||||
| #define CDPCOMMAND_H |  | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
|  |  | ||||||
| class CDPCommand : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit CDPCommand(QObject *parent = nullptr); |  | ||||||
|     CDPCommand(int id, QString cmd, QVariantMap& params); |  | ||||||
|  |  | ||||||
|     QByteArray toJson(); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void finished(); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     int id; |  | ||||||
|     QString cmd; |  | ||||||
|     QVariantMap *params; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // CDPCOMMAND_H |  | ||||||
| @@ -1,86 +0,0 @@ | |||||||
| #include "cdpcommandmanager.h" |  | ||||||
| #include <QVariantMap> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| CDPCommandManager::CDPCommandManager(QObject *parent) |  | ||||||
|     : QObject(parent) |  | ||||||
|     , networkManager(new QNetworkAccessManager) |  | ||||||
|     , socket(new QWebSocket) |  | ||||||
| { |  | ||||||
|     // WebSocket setup |  | ||||||
|     QObject::connect(socket, &QWebSocket::connected, this, &CDPCommandManager::ready); |  | ||||||
|     QObject::connect(socket, &QWebSocket::textMessageReceived, this, &CDPCommandManager::onTextMessageReceived); |  | ||||||
|     QObject::connect(socket, &QWebSocket::disconnected, this, &CDPCommandManager::onSocketDisconnected); |  | ||||||
|     QObject::connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &CDPCommandManager::onSocketError); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CDPCommandManager::~CDPCommandManager() |  | ||||||
| { |  | ||||||
|     delete networkManager; |  | ||||||
|     delete socket; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CDPCommandManager::initialize(QString endpoint) |  | ||||||
| { |  | ||||||
|     QNetworkReply *reply = networkManager->get(QNetworkRequest(endpoint)); |  | ||||||
|  |  | ||||||
|     QObject::connect( |  | ||||||
|         reply, &QNetworkReply::finished, |  | ||||||
|         [reply, this]() { |  | ||||||
|             if (reply->error()) { |  | ||||||
|                 PLOGE << "CDP request error"; |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); |  | ||||||
|             QJsonArray pages = doc.array(); |  | ||||||
|             QJsonObject page = pages.first().toObject(); |  | ||||||
|             QString wsUrl = page.value("webSocketDebuggerUrl").toString(); |  | ||||||
|  |  | ||||||
|             socket->open(wsUrl); |  | ||||||
|         } |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CDPCommand *CDPCommandManager::sendCommand(QString cmd) |  | ||||||
| { |  | ||||||
|     QVariantMap emptyParams; |  | ||||||
|     return sendCommend(cmd, emptyParams); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CDPCommand *CDPCommandManager::sendCommend(QString cmd, QVariantMap ¶ms) |  | ||||||
| { |  | ||||||
|     int id = ++commandId; |  | ||||||
|     CDPCommand *command = new CDPCommand(id, cmd, params); |  | ||||||
|     socket->sendTextMessage(command->toJson()); |  | ||||||
|     commandPool.insert(id, command); |  | ||||||
|  |  | ||||||
|     return command; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CDPCommandManager::onTextMessageReceived(QString message) |  | ||||||
| { |  | ||||||
|     QJsonDocument responseDoc = QJsonDocument::fromJson(message.toUtf8()); |  | ||||||
|     QJsonObject response = responseDoc.object(); |  | ||||||
|  |  | ||||||
|     // Response for method |  | ||||||
|     if (response.contains("id")) { |  | ||||||
|         int id = response.value("id").toInt(); |  | ||||||
|         if (commandPool.contains(id)) { |  | ||||||
|             CDPCommand *command = commandPool.take(id); |  | ||||||
|             command->finished(); |  | ||||||
|         } |  | ||||||
|     } else { // Response for event |  | ||||||
|         emit eventReceived(response.value("method").toString(), response.value("params").toObject()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CDPCommandManager::onSocketDisconnected() |  | ||||||
| { |  | ||||||
|     PLOGI << "WebSocket disconnected"; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CDPCommandManager::onSocketError(QAbstractSocket::SocketError error) |  | ||||||
| { |  | ||||||
|     PLOGE << "WebSocket error" << error; |  | ||||||
| } |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| #ifndef CDPCOMMANDMANAGER_H |  | ||||||
| #define CDPCOMMANDMANAGER_H |  | ||||||
|  |  | ||||||
| #include "cdpcommand.h" |  | ||||||
| #include <QObject> |  | ||||||
| #include <QHash> |  | ||||||
| #include <QtWebSockets> |  | ||||||
| #include <QNetworkAccessManager> |  | ||||||
|  |  | ||||||
| class CDPCommandManager : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit CDPCommandManager(QObject *parent = nullptr); |  | ||||||
|     ~CDPCommandManager(); |  | ||||||
|  |  | ||||||
|     void initialize(QString endpoint); |  | ||||||
|  |  | ||||||
|     CDPCommand *sendCommand(QString cmd); |  | ||||||
|     CDPCommand *sendCommend(QString cmd, QVariantMap& params); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void ready(); |  | ||||||
|     void eventReceived(QString eventName, QJsonObject params); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QNetworkAccessManager *networkManager; |  | ||||||
|     QWebSocket *socket; |  | ||||||
|  |  | ||||||
|     int commandId = 0; |  | ||||||
|     QHash<int, CDPCommand*> commandPool; |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onTextMessageReceived(QString message); |  | ||||||
|     void onSocketDisconnected(); |  | ||||||
|     void onSocketError(QAbstractSocket::SocketError error); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // CDPCOMMANDMANAGER_H |  | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> |  | ||||||
| <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> |  | ||||||
|  |  | ||||||
| <svg |  | ||||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" |  | ||||||
|    xmlns:cc="http://creativecommons.org/ns#" |  | ||||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |  | ||||||
|    xmlns:svg="http://www.w3.org/2000/svg" |  | ||||||
|    xmlns="http://www.w3.org/2000/svg" |  | ||||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |  | ||||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |  | ||||||
|    version="1.1" |  | ||||||
|    id="Layer_1" |  | ||||||
|    x="0px" |  | ||||||
|    y="0px" |  | ||||||
|    viewBox="0 0 96 96" |  | ||||||
|    style="enable-background:new 0 0 96 96;" |  | ||||||
|    xml:space="preserve" |  | ||||||
|    sodipodi:docname="com.yuezk.qt.GPClient.svg" |  | ||||||
|    inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata |  | ||||||
|    id="metadata14"><rdf:RDF><cc:Work |  | ||||||
|        rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type |  | ||||||
|          rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs |  | ||||||
|    id="defs12" /><sodipodi:namedview |  | ||||||
|    pagecolor="#ffffff" |  | ||||||
|    bordercolor="#666666" |  | ||||||
|    borderopacity="1" |  | ||||||
|    objecttolerance="10" |  | ||||||
|    gridtolerance="10" |  | ||||||
|    guidetolerance="10" |  | ||||||
|    inkscape:pageopacity="0" |  | ||||||
|    inkscape:pageshadow="2" |  | ||||||
|    inkscape:window-width="1920" |  | ||||||
|    inkscape:window-height="1006" |  | ||||||
|    id="namedview10" |  | ||||||
|    showgrid="false" |  | ||||||
|    inkscape:zoom="6.9532168" |  | ||||||
|    inkscape:cx="7.9545315" |  | ||||||
|    inkscape:cy="59.062386" |  | ||||||
|    inkscape:window-x="0" |  | ||||||
|    inkscape:window-y="0" |  | ||||||
|    inkscape:window-maximized="1" |  | ||||||
|    inkscape:current-layer="g8499" /> |  | ||||||
| <style |  | ||||||
|    type="text/css" |  | ||||||
|    id="style2"> |  | ||||||
| 	.st0{fill:#2980B9;} |  | ||||||
| 	.st1{fill:#3498DB;} |  | ||||||
| 	.st2{fill:#2ECC71;} |  | ||||||
| 	.st3{fill:#27AE60;} |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
| <g |  | ||||||
|    id="g8499" |  | ||||||
|    transform="matrix(1.3407388,0,0,1.3407388,-16.409202,-16.355463)"><g |  | ||||||
|      id="XMLID_1_"> |  | ||||||
| 	<circle |  | ||||||
|    r="32.5" |  | ||||||
|    cy="48" |  | ||||||
|    cx="48" |  | ||||||
|    class="st0" |  | ||||||
|    id="XMLID_3_" |  | ||||||
|    style="fill:#2980b9" /> |  | ||||||
| 	<path |  | ||||||
|    d="m 48,15.5 v 65 C 65.9,80.5 80.5,65.7 80.5,48 80.5,30 65.9,15.5 48,15.5 Z" |  | ||||||
|    class="st1" |  | ||||||
|    id="XMLID_4_" |  | ||||||
|    inkscape:connector-curvature="0" |  | ||||||
|    style="fill:#3498db" /> |  | ||||||
| 	<path |  | ||||||
|    d="m 48,15.5 v 0.6 l 1.2,-0.3 c 0.3,-0.3 0.4,-0.3 0.6,-0.3 h -1.1 z m 7.3,0.9 c -0.1,0 0.4,0.9 1.1,1.8 0.8,1.5 1.1,2.1 1.3,2.1 0.3,-0.3 1.9,-1.2 3,-2.1 -1.7,-0.9 -3.5,-1.5 -5.4,-1.8 z m 10.3,6.2 c -0.1,0 -0.4,0 -0.9,0.6 l -0.8,0.9 0.6,0.6 c 0.3,0.6 0.8,0.9 1,1.2 0.5,0.6 0.6,0.6 0.1,1.5 -0.2,0.6 -0.3,0.9 -0.3,0.9 0.1,0.3 0.3,0.3 1.4,0.3 h 1.6 c 0.1,0 0.3,-0.6 0.4,-1.2 l 0.1,-0.9 -1.1,-0.9 c -1,-0.9 -1,-0.9 -1.4,-1.8 -0.3,-0.6 -0.6,-1.2 -0.7,-1.2 z m -3,2.4 c -0.2,0 -1.3,2.1 -1.3,2.4 0,0 0.3,0.6 0.7,0.9 0.4,0.3 0.7,0.6 0.7,0.6 0.1,0 1.2,-1.2 1.4,-1.5 C 64.2,27.1 64,26.8 63.5,26.2 63.1,25.5 62.7,25 62.6,25 Z m 9.5,1.1 0.2,0.3 c 0,0.3 -0.7,0.9 -1.4,1.5 -1.2,0.9 -1.4,1.2 -2,1.2 -0.6,0 -0.9,0.3 -1.8,0.9 -0.6,0.6 -1.2,0.9 -1.2,1.2 0,0 0.2,0.3 0.6,0.9 0.7,0.6 0.7,0.9 0.2,1.8 l -0.4,0.3 h -1.1 c -0.6,0 -1.5,0 -1.8,-0.3 -0.9,0 -0.8,0 -0.1,2.1 1,3 1.1,3.2 1.3,3.2 0.1,0 1.3,-1.2 2.8,-2.4 1.5,-1.2 2.7,-2.4 2.8,-2.4 l 0.6,0.3 c 0.4,0.3 0.5,0 1.3,-0.6 l 0.8,-0.6 0.8,0.6 c 1.9,1.2 2.2,1.5 2.3,2.4 0.2,1.5 0.3,1.8 0.5,1.8 0.1,0 1.3,-1.5 1.6,-1.8 0.1,-0.3 -0.1,-0.6 -1.1,-2.1 -0.7,-0.9 -1.1,-1.8 -1.1,-2.1 0,0 0.1,0 0.3,-0.3 0.2,0 0.4,0.3 1,0.9 -1.6,-2.3 -3.2,-4.7 -5.1,-6.8 z m 2.8,10.7 c -0.2,0 -0.9,0.9 -0.8,1.2 l 0.5,0.3 H 75 c 0.2,0 0.3,0 0.2,-0.3 C 75.1,37.4 75,36.8 74.9,36.8 Z M 72.3,38 h -2.4 l -2.4,0.3 -4.5,3.5 -4.4,3.8 v 3.5 c 0,2.1 0,3.8 0.1,3.8 0.1,0 0.7,0.9 1.5,1.5 0.8,0.9 1.5,1.5 1.8,1.8 0.4,0.3 0.5,0.3 4,0.6 l 3.4,0.3 1.6,0.9 c 0.8,0.6 1.5,1.2 1.6,1.2 0.1,0 -0.3,0.3 -0.6,0.6 l -0.6,0.6 1,1.2 c 0.5,0.6 1.3,1.5 1.7,1.8 l 0.6,0.9 v 1.7 0.9 c 3.7,-5 5.9,-11.5 6.1,-18.3 0.1,-2.7 -0.3,-5.3 -0.8,-8 l -0.6,-0.3 c -0.1,0 -0.5,0.3 -1,0.6 -0.5,0.3 -1,0.9 -1.1,0.9 -0.1,0 -0.8,-0.3 -1.8,-0.6 l -1.8,-0.6 v -0.9 c 0,-0.6 0,-0.9 -0.6,-1.5 z M 48,63.7 V 64 h 0.2 z" |  | ||||||
|    class="st2" |  | ||||||
|    id="XMLID_13_" |  | ||||||
|    inkscape:connector-curvature="0" |  | ||||||
|    style="fill:#2ecc71" /> |  | ||||||
| 	<path |  | ||||||
|    d="m 48,15.5 c -3.1,0 -6.2,0.5 -9,1.3 0.3,0.4 0.3,0.4 0.6,0.9 1.5,2.5 1.7,2.8 2.1,2.9 0.3,0 0.9,0.1 1.6,0.1 h 1.2 l 0.9,-2 0.8,-1.9 1.8,-0.6 z m -16.9,4.7 c -2.8,1.7 -5.4,3.9 -7.6,6.4 -3.8,4.3 -6.3,9.6 -7.4,15.4 0.5,0 0.9,-0.1 1.8,-0.1 2.8,0.1 2.5,0 3.4,1.4 0.5,0.8 0.6,0.8 1.4,0.8 1,0.1 0.9,0 0.5,-1.6 -0.2,-0.6 -0.3,-1.2 -0.3,-1.4 0,-0.2 0.5,-0.7 1.7,-1.6 1.9,-1.5 1.8,-1.3 1.5,-2.9 -0.1,-0.3 0.1,-0.6 0.6,-1.2 0.7,-0.7 0.7,-0.6 1.4,-0.6 h 0.7 l 0.1,-1.2 c 0.1,-0.7 0.1,-1.3 0.2,-1.3 0,0 1.9,-1.1 4.1,-2.3 2.2,-1.2 4.1,-2.2 4.2,-2.3 0.2,-0.2 -0.3,-0.8 -2.7,-3.8 -1.5,-1.9 -2.8,-3.6 -2.9,-3.7 z m -5.8,23 c -0.1,0 -0.1,0.3 -0.1,0.6 0,0.6 0,0.7 0.6,1 0.8,0.4 0.9,0.5 0.8,0.2 -0.1,-0.4 -1.2,-1.9 -1.3,-1.8 z m -3.4,2.1 -0.5,1.8 c 0.1,0.1 0.9,0.3 1.8,0.5 1,0.2 1.6,0.4 1.8,0.3 l 0.5,-1.3 z m -3.8,1 -1.1,0.6 c -0.6,0.3 -1.2,0.6 -1.4,0.6 h -0.1 c 0,1.4 0.1,2.8 0.3,4.2 l 0.6,0.4 1,-0.1 h 1 l 0.6,1.4 c 0.3,0.7 0.7,1.4 0.8,1.5 0.1,0.1 1,0.1 1.8,0.1 h 1.5 L 23,56.2 c 0,1.2 0,1.3 -0.6,2.2 -0.4,0.5 -0.6,1.2 -0.6,1.4 0,0.2 0.7,2.1 1.6,4.3 l 1.5,4 1.6,0.8 c 1.2,0.6 1.5,0.8 1.5,1 0,0.1 -0.4,2.1 -0.6,3.1 3,2.5 6.4,4.5 10.2,5.8 3.5,-3.6 6.8,-7.1 7.3,-7.6 l 0.7,-0.7 0.2,-1.9 c 0.2,-1.1 0.4,-2.1 0.4,-2.2 0,-0.1 0.5,-0.6 1,-1.2 0.5,-0.5 0.8,-1 0.8,-1.1 v -0.2 c -0.1,-0.1 -1.4,-1.1 -3,-2.2 l -3.1,-2.1 -1.1,-0.1 c -0.8,0 -1.2,0 -1.3,-0.2 C 39.4,59.2 39.2,58.5 39.1,57.7 39,56.9 38.9,56.2 38.8,56.1 38.8,56 38,56 37.1,56 36.2,56 35.4,55.9 35.3,55.8 35.2,55.7 35.2,55.1 35.1,54.3 35,53.6 34.9,53 34.8,52.9 34.7,52.8 33.7,52.7 32.5,52.6 30.5,52.5 30.1,52.5 29.1,52 l -1.2,-0.6 -1.6,0.7 -1.7,0.9 -1.8,-0.1 c -2,0 -1.9,0.2 -2.1,-1.6 C 20.6,50.7 20.6,50.1 20.5,50.1 20.4,50 20,50 19.6,49.9 L 18.9,49.7 19,49.2 c 0,-0.3 0,-1 0.1,-1.4 L 19.2,47 18.7,46.5 Z m 9.1,1.1 C 27.1,47.5 27.1,47.8 27,48 l -0.1,0.5 2.9,1.2 c 2.9,1.1 3.4,1.2 3.9,0.7 0.2,-0.2 0.1,-0.2 -0.3,-0.4 -0.3,-0.1 -1.7,-0.9 -3.2,-1.6 -1.7,-0.7 -2.9,-1.1 -3,-1 z" |  | ||||||
|    class="st3" |  | ||||||
|    id="XMLID_20_" |  | ||||||
|    inkscape:connector-curvature="0" |  | ||||||
|    style="fill:#27ae60" /> |  | ||||||
| </g><g |  | ||||||
|      transform="matrix(1.458069,0,0,1.458069,-22.631538,-19.615144)" |  | ||||||
|      id="g7664"><path |  | ||||||
|        inkscape:connector-curvature="0" |  | ||||||
|        id="XMLID_6_" |  | ||||||
|        class="st3" |  | ||||||
|        d="m 38.8,56.1 c 0,1.2 1,2.2 2.2,2.2 h 15.2 c 1.2,0 2.2,-1 2.2,-2.2 V 45.3 c 0,-1.2 -1,-2.2 -2.2,-2.2 H 40.9 c -1.2,0 -2.2,1 -2.2,2.2 v 10.8 z" |  | ||||||
|        style="fill:#f1aa27;fill-opacity:1" /><path |  | ||||||
|        style="fill:#e6e6e6" |  | ||||||
|        inkscape:connector-curvature="0" |  | ||||||
|        id="XMLID_7_" |  | ||||||
|        class="st4" |  | ||||||
|        d="m 55.5,43.1 h -3.3 v -3.7 c 0,-2.1 -1.7,-3.8 -3.8,-3.8 -2.1,0 -3.8,1.7 -3.8,3.8 v 3.8 h -3.1 v -3.8 c 0,-3.9 3.2,-7 7,-7 3.9,0 7,3.2 7,7 z" /><path |  | ||||||
|        style="fill:#e6e6e6;fill-opacity:1" |  | ||||||
|        inkscape:connector-curvature="0" |  | ||||||
|        id="XMLID_8_" |  | ||||||
|        class="st5" |  | ||||||
|        d="m 50.35,48.2 c 0,-1 -0.8,-1.8 -1.8,-1.8 -1,0 -1.8,0.8 -1.8,1.8 0,0.7 0.4,1.3 1,1.6 l -1,5.2 h 3.6 l -1,-5.2 c 0.6,-0.3 1,-0.9 1,-1.6 z" /></g></g></svg> |  | ||||||
| Before Width: | Height: | Size: 6.7 KiB | 
| @@ -1,11 +0,0 @@ | |||||||
| [Desktop Entry] |  | ||||||
|  |  | ||||||
| Type=Application |  | ||||||
| Version=1.0.0 |  | ||||||
| Name=GlobalProtect VPN |  | ||||||
| Comment=GlobalProtect VPN client, supports SAML auth mode |  | ||||||
| Exec=/usr/bin/gpclient |  | ||||||
| Icon=com.yuezk.qt.GPClient |  | ||||||
| Categories=Network;VPN;Utility;Qt; |  | ||||||
| Keywords=GlobalProtect;Openconnect;SAML;connection;VPN; |  | ||||||
| StartupWMClass=gpclient |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 18 KiB | 
| @@ -1,36 +0,0 @@ | |||||||
| #include "enhancedwebview.h" |  | ||||||
| #include "cdpcommandmanager.h" |  | ||||||
|  |  | ||||||
| #include <QtWebEngineWidgets/QWebEngineView> |  | ||||||
| #include <QProcessEnvironment> |  | ||||||
|  |  | ||||||
| EnhancedWebView::EnhancedWebView(QWidget *parent) |  | ||||||
|     : QWebEngineView(parent) |  | ||||||
|     , cdp(new CDPCommandManager) |  | ||||||
| { |  | ||||||
|     QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady); |  | ||||||
|     QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| EnhancedWebView::~EnhancedWebView() |  | ||||||
| { |  | ||||||
|     delete cdp; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void EnhancedWebView::initialize() |  | ||||||
| { |  | ||||||
|     QString port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT); |  | ||||||
|     cdp->initialize("http://127.0.0.1:" + port + "/json"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void EnhancedWebView::onCDPReady() |  | ||||||
| { |  | ||||||
|     cdp->sendCommand("Network.enable"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void EnhancedWebView::onEventReceived(QString eventName, QJsonObject params) |  | ||||||
| { |  | ||||||
|     if (eventName == "Network.responseReceived") { |  | ||||||
|         emit responseReceived(params); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| #ifndef ENHANCEDWEBVIEW_H |  | ||||||
| #define ENHANCEDWEBVIEW_H |  | ||||||
|  |  | ||||||
| #include "cdpcommandmanager.h" |  | ||||||
| #include <QtWebEngineWidgets/QWebEngineView> |  | ||||||
|  |  | ||||||
| #define ENV_CDP_PORT "QTWEBENGINE_REMOTE_DEBUGGING" |  | ||||||
|  |  | ||||||
| class EnhancedWebView : public QWebEngineView |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit EnhancedWebView(QWidget *parent = nullptr); |  | ||||||
|     ~EnhancedWebView(); |  | ||||||
|  |  | ||||||
|     void initialize(); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void responseReceived(QJsonObject params); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onCDPReady(); |  | ||||||
|     void onEventReceived(QString eventName, QJsonObject params); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     CDPCommandManager *cdp; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // ENHANCEDWEBVIEW_H |  | ||||||
| @@ -1,176 +0,0 @@ | |||||||
| #include "gatewayauthenticator.h" |  | ||||||
| #include "gphelper.h" |  | ||||||
| #include "loginparams.h" |  | ||||||
| #include "preloginresponse.h" |  | ||||||
|  |  | ||||||
| #include <QNetworkReply> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; |  | ||||||
|  |  | ||||||
| GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig) |  | ||||||
|     : QObject() |  | ||||||
|     , preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") |  | ||||||
|     , loginUrl("https://" + gateway + "/ssl-vpn/login.esp") |  | ||||||
|     , portalConfig(portalConfig) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GatewayAuthenticator::~GatewayAuthenticator() |  | ||||||
| { |  | ||||||
|     delete normalLoginWindow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::authenticate() |  | ||||||
| { |  | ||||||
|     PLOGI << "Start gateway authentication..."; |  | ||||||
|  |  | ||||||
|     LoginParams params; |  | ||||||
|     params.setUser(portalConfig.username()); |  | ||||||
|     params.setPassword(portalConfig.password()); |  | ||||||
|     params.setUserAuthCookie(portalConfig.userAuthCookie()); |  | ||||||
|  |  | ||||||
|     login(params); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::login(const LoginParams ¶ms) |  | ||||||
| { |  | ||||||
|     PLOGI << "Trying to login the gateway at " << loginUrl << " with " << params.toUtf8(); |  | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(loginUrl, params.toUtf8()); |  | ||||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginFinished() |  | ||||||
| { |  | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |  | ||||||
|     QByteArray response; |  | ||||||
|  |  | ||||||
|     if (reply->error() || (response = reply->readAll()).contains("Authentication failure")) { |  | ||||||
|         PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl).arg(reply->errorString()); |  | ||||||
|  |  | ||||||
|         if (normalLoginWindow) { |  | ||||||
|             normalLoginWindow->setProcessing(false); |  | ||||||
|             openMessageBox("Gateway login failed.", "Please check your credentials and try again."); |  | ||||||
|         } else { |  | ||||||
|             doAuth(); |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (normalLoginWindow) { |  | ||||||
|         normalLoginWindow->close(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const QUrlQuery params = gpclient::helper::parseGatewayResponse(response); |  | ||||||
|     emit success(params.toString()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::doAuth() |  | ||||||
| { |  | ||||||
|     PLOGI << "Perform the gateway prelogin at " << preloginUrl; |  | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(preloginUrl); |  | ||||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onPreloginFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onPreloginFinished() |  | ||||||
| { |  | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |  | ||||||
|  |  | ||||||
|     if (reply->error()) { |  | ||||||
|         PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl).arg(reply->errorString()); |  | ||||||
|  |  | ||||||
|         emit fail("Error occurred on the gateway prelogin interface."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Gateway prelogin succeeded."; |  | ||||||
|  |  | ||||||
|     PreloginResponse response = PreloginResponse::parse(reply->readAll()); |  | ||||||
|  |  | ||||||
|     if (response.hasSamlAuthFields()) { |  | ||||||
|         samlAuth(response.samlMethod(), response.samlRequest(), reply->url().toString()); |  | ||||||
|     } else if (response.hasNormalAuthFields()) { |  | ||||||
|         normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage()); |  | ||||||
|     } else { |  | ||||||
|         PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl).arg(QString::fromUtf8(response.rawResponse())); |  | ||||||
|         emit fail("Unknown response for gateway prelogin interface."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     delete reply; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPassword, QString authMessage) |  | ||||||
| { |  | ||||||
|     PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername).arg(labelPassword); |  | ||||||
|  |  | ||||||
|     normalLoginWindow = new NormalLoginWindow; |  | ||||||
|     normalLoginWindow->setPortalAddress(gateway); |  | ||||||
|     normalLoginWindow->setAuthMessage(authMessage); |  | ||||||
|     normalLoginWindow->setUsernameLabel(labelUsername); |  | ||||||
|     normalLoginWindow->setPasswordLabel(labelPassword); |  | ||||||
|  |  | ||||||
|     // Do login |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformNormalLogin); |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished); |  | ||||||
|  |  | ||||||
|     normalLoginWindow->show(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start to perform normal login..."; |  | ||||||
|  |  | ||||||
|     normalLoginWindow->setProcessing(true); |  | ||||||
|     LoginParams params; |  | ||||||
|     params.setUser(username); |  | ||||||
|     params.setPassword(password); |  | ||||||
|     login(params); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginWindowRejected() |  | ||||||
| { |  | ||||||
|     emit fail(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginWindowFinished() |  | ||||||
| { |  | ||||||
|     delete normalLoginWindow; |  | ||||||
|     normalLoginWindow = nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl) |  | ||||||
| { |  | ||||||
|     PLOGI << "Trying to perform SAML login with saml-method " << samlMethod; |  | ||||||
|  |  | ||||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; |  | ||||||
|  |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &GatewayAuthenticator::onSAMLLoginSuccess); |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &GatewayAuthenticator::onSAMLLoginFail); |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); |  | ||||||
|  |  | ||||||
|     loginWindow->login(samlMethod, samlRequest, preloginUrl); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult) |  | ||||||
| { |  | ||||||
|     if (samlResult.contains("preloginCookie")) { |  | ||||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); |  | ||||||
|     } else { |  | ||||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     LoginParams params; |  | ||||||
|     params.setUser(samlResult.value("username")); |  | ||||||
|     params.setPreloginCookie(samlResult.value("preloginCookie")); |  | ||||||
|     params.setUserAuthCookie(samlResult.value("userAuthCookie")); |  | ||||||
|  |  | ||||||
|     login(params); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onSAMLLoginFail(const QString msg) |  | ||||||
| { |  | ||||||
|     emit fail(msg); |  | ||||||
| } |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| #ifndef GATEWAYAUTHENTICATOR_H |  | ||||||
| #define GATEWAYAUTHENTICATOR_H |  | ||||||
|  |  | ||||||
| #include "portalconfigresponse.h" |  | ||||||
| #include "normalloginwindow.h" |  | ||||||
| #include "loginparams.h" |  | ||||||
| #include <QObject> |  | ||||||
|  |  | ||||||
| class GatewayAuthenticator : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig); |  | ||||||
|     ~GatewayAuthenticator(); |  | ||||||
|  |  | ||||||
|     void authenticate(); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void success(const QString& authCookie); |  | ||||||
|     void fail(const QString& msg = ""); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onLoginFinished(); |  | ||||||
|     void onPreloginFinished(); |  | ||||||
|     void onPerformNormalLogin(const QString &username, const QString &password); |  | ||||||
|     void onLoginWindowRejected(); |  | ||||||
|     void onLoginWindowFinished(); |  | ||||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> &samlResult); |  | ||||||
|     void onSAMLLoginFail(const QString msg); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QString gateway; |  | ||||||
|     QString preloginUrl; |  | ||||||
|     QString loginUrl; |  | ||||||
|  |  | ||||||
|     const PortalConfigResponse& portalConfig; |  | ||||||
|  |  | ||||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; |  | ||||||
|  |  | ||||||
|     void login(const LoginParams& params); |  | ||||||
|     void doAuth(); |  | ||||||
|     void normalAuth(QString labelUsername, QString labelPassword, QString authMessage); |  | ||||||
|     void samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl = ""); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // GATEWAYAUTHENTICATOR_H |  | ||||||
| @@ -1,441 +0,0 @@ | |||||||
| #include "gpclient.h" |  | ||||||
| #include "gphelper.h" |  | ||||||
| #include "ui_gpclient.h" |  | ||||||
| #include "portalauthenticator.h" |  | ||||||
| #include "gatewayauthenticator.h" |  | ||||||
|  |  | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <QIcon> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; |  | ||||||
|  |  | ||||||
| GPClient::GPClient(QWidget *parent) |  | ||||||
|     : QMainWindow(parent) |  | ||||||
|     , ui(new Ui::GPClient) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |  | ||||||
|     setWindowTitle("GlobalProtect"); |  | ||||||
|     setFixedSize(width(), height()); |  | ||||||
|     gpclient::helper::moveCenter(this); |  | ||||||
|  |  | ||||||
|     // Restore portal from the previous settings |  | ||||||
|     ui->portalInput->setText(settings::get("portal", "").toString()); |  | ||||||
|  |  | ||||||
|     // DBus service setup |  | ||||||
|     vpn = new com::yuezk::qt::GPService("com.yuezk.qt.GPService", "/", QDBusConnection::systemBus(), this); |  | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::connected, this, &GPClient::onVPNConnected); |  | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::disconnected, this, &GPClient::onVPNDisconnected); |  | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::logAvailable, this, &GPClient::onVPNLogAvailable); |  | ||||||
|  |  | ||||||
|     // Initiallize the context menu of system tray. |  | ||||||
|     initSystemTrayIcon(); |  | ||||||
|     initVpnStatus(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GPClient::~GPClient() |  | ||||||
| { |  | ||||||
|     delete ui; |  | ||||||
|     delete vpn; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::on_connectButton_clicked() |  | ||||||
| { |  | ||||||
|     doConnect(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::on_portalInput_returnPressed() |  | ||||||
| { |  | ||||||
|     doConnect(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::on_portalInput_editingFinished() |  | ||||||
| { |  | ||||||
|     populateGatewayMenu(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::initSystemTrayIcon() |  | ||||||
| { |  | ||||||
|     systemTrayIcon = new QSystemTrayIcon(this); |  | ||||||
|     contextMenu = new QMenu("GlobalProtect", this); |  | ||||||
|  |  | ||||||
|     gatewaySwitchMenu = new QMenu("Switch Gateway", this); |  | ||||||
|     gatewaySwitchMenu->setIcon(QIcon::fromTheme("network-workgroup")); |  | ||||||
|     populateGatewayMenu(); |  | ||||||
|  |  | ||||||
|     systemTrayIcon->setIcon(QIcon(":/images/not_connected.png")); |  | ||||||
|     systemTrayIcon->setToolTip("GlobalProtect"); |  | ||||||
|     systemTrayIcon->setContextMenu(contextMenu); |  | ||||||
|  |  | ||||||
|     connect(systemTrayIcon, &QSystemTrayIcon::activated, this, &GPClient::onSystemTrayActivated); |  | ||||||
|     connect(gatewaySwitchMenu, &QMenu::triggered, this, &GPClient::onGatewayChanged); |  | ||||||
|  |  | ||||||
|     openAction = contextMenu->addAction(QIcon::fromTheme("window-new"), "Open", this, &GPClient::activate); |  | ||||||
|     connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect); |  | ||||||
|     contextMenu->addMenu(gatewaySwitchMenu); |  | ||||||
|     contextMenu->addSeparator(); |  | ||||||
|     clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset Settings", this, &GPClient::clearSettings); |  | ||||||
|     quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit); |  | ||||||
|  |  | ||||||
|     systemTrayIcon->show(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::initVpnStatus() { |  | ||||||
|     int status = vpn->status(); |  | ||||||
|  |  | ||||||
|     if (status == 1) { |  | ||||||
|         ui->statusLabel->setText("Connecting..."); |  | ||||||
|         updateConnectionStatus(VpnStatus::pending); |  | ||||||
|     } else if (status == 2) { |  | ||||||
|         updateConnectionStatus(VpnStatus::connected); |  | ||||||
|     } else if (status == 3) { |  | ||||||
|         ui->statusLabel->setText("Disconnecting..."); |  | ||||||
|         updateConnectionStatus(VpnStatus::pending); |  | ||||||
|     } else { |  | ||||||
|         updateConnectionStatus(VpnStatus::disconnected); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::populateGatewayMenu() |  | ||||||
| { |  | ||||||
|     PLOGI << "Populating the Switch Gateway menu..."; |  | ||||||
|  |  | ||||||
|     const QList<GPGateway> gateways = allGateways(); |  | ||||||
|     gatewaySwitchMenu->clear(); |  | ||||||
|  |  | ||||||
|     if (gateways.isEmpty()) { |  | ||||||
|         gatewaySwitchMenu->addAction("<None>")->setData(-1); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const QString currentGatewayName = currentGateway().name(); |  | ||||||
|     for (int i = 0; i < gateways.length(); i++) { |  | ||||||
|         const GPGateway g = gateways.at(i); |  | ||||||
|         QString iconImage = ":/images/radio_unselected.png"; |  | ||||||
|         if (g.name() == currentGatewayName) { |  | ||||||
|             iconImage = ":/images/radio_selected.png"; |  | ||||||
|         } |  | ||||||
|         gatewaySwitchMenu->addAction(QIcon(iconImage), g.name())->setData(i); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::updateConnectionStatus(const GPClient::VpnStatus &status) |  | ||||||
| { |  | ||||||
|     switch (status) { |  | ||||||
|         case VpnStatus::disconnected: |  | ||||||
|             ui->statusLabel->setText("Not Connected"); |  | ||||||
|             ui->statusImage->setStyleSheet("image: url(:/images/not_connected.png); padding: 15;"); |  | ||||||
|             ui->connectButton->setText("Connect"); |  | ||||||
|             ui->connectButton->setDisabled(false); |  | ||||||
|             ui->portalInput->setReadOnly(false); |  | ||||||
|  |  | ||||||
|             systemTrayIcon->setIcon(QIcon{ ":/images/not_connected.png" }); |  | ||||||
|             connectAction->setEnabled(true); |  | ||||||
|             connectAction->setText("Connect"); |  | ||||||
|             gatewaySwitchMenu->setEnabled(true); |  | ||||||
|             clearAction->setEnabled(true); |  | ||||||
|             break; |  | ||||||
|         case VpnStatus::pending: |  | ||||||
|             ui->statusImage->setStyleSheet("image: url(:/images/pending.png); padding: 15;"); |  | ||||||
|             ui->connectButton->setDisabled(true); |  | ||||||
|             ui->portalInput->setReadOnly(true); |  | ||||||
|  |  | ||||||
|             systemTrayIcon->setIcon(QIcon{ ":/images/pending.png" }); |  | ||||||
|             connectAction->setEnabled(false); |  | ||||||
|             gatewaySwitchMenu->setEnabled(false); |  | ||||||
|             clearAction->setEnabled(false); |  | ||||||
|             break; |  | ||||||
|         case VpnStatus::connected: |  | ||||||
|             ui->statusLabel->setText("Connected"); |  | ||||||
|             ui->statusImage->setStyleSheet("image: url(:/images/connected.png); padding: 15;"); |  | ||||||
|             ui->connectButton->setText("Disconnect"); |  | ||||||
|             ui->connectButton->setDisabled(false); |  | ||||||
|             ui->portalInput->setReadOnly(true); |  | ||||||
|  |  | ||||||
|             systemTrayIcon->setIcon(QIcon{ ":/images/connected.png" }); |  | ||||||
|             connectAction->setEnabled(true); |  | ||||||
|             connectAction->setText("Disconnect"); |  | ||||||
|             gatewaySwitchMenu->setEnabled(true); |  | ||||||
|             clearAction->setEnabled(false); |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason) |  | ||||||
| { |  | ||||||
|     switch (reason) { |  | ||||||
|         case QSystemTrayIcon::Trigger: |  | ||||||
|         case QSystemTrayIcon::DoubleClick: |  | ||||||
|             this->activate(); |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onGatewayChanged(QAction *action) |  | ||||||
| { |  | ||||||
|     const int index = action->data().toInt(); |  | ||||||
|  |  | ||||||
|     if (index == -1) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const GPGateway g = allGateways().at(index); |  | ||||||
|  |  | ||||||
|     // If the selected gateway is the same as the current gateway |  | ||||||
|     if (g.name() == currentGateway().name()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     setCurrentGateway(g); |  | ||||||
|  |  | ||||||
|     if (connected()) { |  | ||||||
|         ui->statusLabel->setText("Switching Gateway..."); |  | ||||||
|         ui->connectButton->setEnabled(false); |  | ||||||
|  |  | ||||||
|         vpn->disconnect(); |  | ||||||
|         isSwitchingGateway = true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::doConnect() |  | ||||||
| { |  | ||||||
|     PLOGI << "Start connecting..."; |  | ||||||
|  |  | ||||||
|     const QString btnText = ui->connectButton->text(); |  | ||||||
|     const QString portal = this->portal(); |  | ||||||
|  |  | ||||||
|     // Display the main window if portal is empty |  | ||||||
|     if (portal.isEmpty()) { |  | ||||||
|         activate(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (btnText.endsWith("Connect")) { |  | ||||||
|         settings::save("portal", portal); |  | ||||||
|  |  | ||||||
|         // Login to the previously saved gateway |  | ||||||
|         if (!currentGateway().name().isEmpty()) { |  | ||||||
|             PLOGI << "Start gateway login using the previously saved gateway..."; |  | ||||||
|             isQuickConnect = true; |  | ||||||
|             gatewayLogin(); |  | ||||||
|         } else { |  | ||||||
|             // Perform the portal login |  | ||||||
|             PLOGI << "Start portal login..."; |  | ||||||
|             portalLogin(); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         PLOGI << "Start disconnecting the VPN..."; |  | ||||||
|  |  | ||||||
|         ui->statusLabel->setText("Disconnecting..."); |  | ||||||
|         updateConnectionStatus(VpnStatus::pending); |  | ||||||
|         vpn->disconnect(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Login to the portal interface to get the portal config and preferred gateway |  | ||||||
| void GPClient::portalLogin() |  | ||||||
| { |  | ||||||
|     PortalAuthenticator *portalAuth = new PortalAuthenticator(portal()); |  | ||||||
|  |  | ||||||
|     connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess); |  | ||||||
|     // Prelogin failed on the portal interface, try to treat the portal as a gateway interface |  | ||||||
|     connect(portalAuth, &PortalAuthenticator::preloginFailed, this, &GPClient::onPortalPreloginFail); |  | ||||||
|     connect(portalAuth, &PortalAuthenticator::portalConfigFailed, this, &GPClient::onPortalConfigFail); |  | ||||||
|     // Portal login failed |  | ||||||
|     connect(portalAuth, &PortalAuthenticator::fail, this, &GPClient::onPortalFail); |  | ||||||
|  |  | ||||||
|     ui->statusLabel->setText("Authenticating..."); |  | ||||||
|     updateConnectionStatus(VpnStatus::pending); |  | ||||||
|     portalAuth->authenticate(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region) |  | ||||||
| { |  | ||||||
|     PLOGI << "Portal authentication succeeded."; |  | ||||||
|  |  | ||||||
|     // No gateway found in protal configuration |  | ||||||
|     if (portalConfig.allGateways().size() == 0) { |  | ||||||
|         PLOGI << "No gateway found in portal configuration, treat the portal address as a gateway."; |  | ||||||
|         tryGatewayLogin(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     GPGateway gateway = filterPreferredGateway(portalConfig.allGateways(), region); |  | ||||||
|     setAllGateways(portalConfig.allGateways()); |  | ||||||
|     setCurrentGateway(gateway); |  | ||||||
|     this->portalConfig = portalConfig; |  | ||||||
|  |  | ||||||
|     gatewayLogin(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onPortalPreloginFail(const QString msg) |  | ||||||
| { |  | ||||||
|     PLOGI << "Portal prelogin failed: " << msg; |  | ||||||
|     tryGatewayLogin(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onPortalConfigFail(const QString msg) |  | ||||||
| { |  | ||||||
|     PLOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway."; |  | ||||||
|     tryGatewayLogin(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onPortalFail(const QString &msg) |  | ||||||
| { |  | ||||||
|     if (!msg.isEmpty()) { |  | ||||||
|         openMessageBox("Portal authentication failed.", msg); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     updateConnectionStatus(VpnStatus::disconnected); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::tryGatewayLogin() |  | ||||||
| { |  | ||||||
|     PLOGI << "Try to preform login on the the gateway interface..."; |  | ||||||
|  |  | ||||||
|     // Treat the portal input as the gateway address |  | ||||||
|     GPGateway g; |  | ||||||
|     g.setName(portal()); |  | ||||||
|     g.setAddress(portal()); |  | ||||||
|  |  | ||||||
|     QList<GPGateway> gateways; |  | ||||||
|     gateways.append(g); |  | ||||||
|  |  | ||||||
|     setAllGateways(gateways); |  | ||||||
|     setCurrentGateway(g); |  | ||||||
|  |  | ||||||
|     gatewayLogin(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Login to the gateway |  | ||||||
| void GPClient::gatewayLogin() |  | ||||||
| { |  | ||||||
|     PLOGI << "Performing gateway login..."; |  | ||||||
|  |  | ||||||
|     GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), portalConfig); |  | ||||||
|  |  | ||||||
|     connect(gatewayAuth, &GatewayAuthenticator::success, this, &GPClient::onGatewaySuccess); |  | ||||||
|     connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail); |  | ||||||
|  |  | ||||||
|     ui->statusLabel->setText("Authenticating..."); |  | ||||||
|     updateConnectionStatus(VpnStatus::pending); |  | ||||||
|     gatewayAuth->authenticate(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onGatewaySuccess(const QString &authCookie) |  | ||||||
| { |  | ||||||
|     PLOGI << "Gateway login succeeded, got the cookie " << authCookie; |  | ||||||
|  |  | ||||||
|     isQuickConnect = false; |  | ||||||
|     vpn->connect(currentGateway().address(), portalConfig.username(), authCookie); |  | ||||||
|     ui->statusLabel->setText("Connecting..."); |  | ||||||
|     updateConnectionStatus(VpnStatus::pending); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onGatewayFail(const QString &msg) |  | ||||||
| { |  | ||||||
|     // If the quick connect on gateway failed, perform the portal login |  | ||||||
|     if (isQuickConnect && !msg.isEmpty()) { |  | ||||||
|         isQuickConnect = false; |  | ||||||
|         portalLogin(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!msg.isEmpty()) { |  | ||||||
|         openMessageBox("Gateway authentication failed.", msg); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     updateConnectionStatus(VpnStatus::disconnected); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::activate() |  | ||||||
| { |  | ||||||
|     activateWindow(); |  | ||||||
|     showNormal(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPClient::portal() const |  | ||||||
| { |  | ||||||
|     const QString input = ui->portalInput->text().trimmed(); |  | ||||||
|  |  | ||||||
|     if (input.startsWith("http")) { |  | ||||||
|         return QUrl(input).authority(); |  | ||||||
|     } |  | ||||||
|     return input; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool GPClient::connected() const |  | ||||||
| { |  | ||||||
|     const QString statusText = ui->statusLabel->text(); |  | ||||||
|     return statusText.contains("Connected") && !statusText.contains("Not"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QList<GPGateway> GPClient::allGateways() const |  | ||||||
| { |  | ||||||
|     const QString gatewaysJson = settings::get(portal() + "_gateways").toString(); |  | ||||||
|     return GPGateway::fromJson(gatewaysJson); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::setAllGateways(QList<GPGateway> gateways) |  | ||||||
| { |  | ||||||
|     PLOGI << "Updating all the gateways..."; |  | ||||||
|  |  | ||||||
|     settings::save(portal() + "_gateways", GPGateway::serialize(gateways)); |  | ||||||
|     populateGatewayMenu(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GPGateway GPClient::currentGateway() const |  | ||||||
| { |  | ||||||
|     const QString selectedGateway = settings::get(portal() + "_selectedGateway").toString(); |  | ||||||
|  |  | ||||||
|     for (auto g : allGateways()) { |  | ||||||
|         if (g.name() == selectedGateway) { |  | ||||||
|             return g; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return GPGateway{}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::setCurrentGateway(const GPGateway gateway) |  | ||||||
| { |  | ||||||
|     PLOGI << "Updating the current gateway to " << gateway.name(); |  | ||||||
|  |  | ||||||
|     settings::save(portal() + "_selectedGateway", gateway.name()); |  | ||||||
|     populateGatewayMenu(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::clearSettings() |  | ||||||
| { |  | ||||||
|     settings::clear(); |  | ||||||
|     populateGatewayMenu(); |  | ||||||
|     ui->portalInput->clear(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::quit() |  | ||||||
| { |  | ||||||
|     vpn->disconnect(); |  | ||||||
|     QApplication::quit(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onVPNConnected() |  | ||||||
| { |  | ||||||
|     updateConnectionStatus(VpnStatus::connected); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onVPNDisconnected() |  | ||||||
| { |  | ||||||
|     updateConnectionStatus(VpnStatus::disconnected); |  | ||||||
|  |  | ||||||
|     if (isSwitchingGateway) { |  | ||||||
|         gatewayLogin(); |  | ||||||
|         isSwitchingGateway = false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPClient::onVPNLogAvailable(QString log) |  | ||||||
| { |  | ||||||
|     PLOGI << log; |  | ||||||
| } |  | ||||||
| @@ -1,91 +0,0 @@ | |||||||
| #ifndef GPCLIENT_H |  | ||||||
| #define GPCLIENT_H |  | ||||||
|  |  | ||||||
| #include "gpservice_interface.h" |  | ||||||
| #include "portalconfigresponse.h" |  | ||||||
|  |  | ||||||
| #include <QMainWindow> |  | ||||||
| #include <QSystemTrayIcon> |  | ||||||
| #include <QMenu> |  | ||||||
|  |  | ||||||
| QT_BEGIN_NAMESPACE |  | ||||||
| namespace Ui { class GPClient; } |  | ||||||
| QT_END_NAMESPACE |  | ||||||
|  |  | ||||||
| class GPClient : public QMainWindow |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
|  |  | ||||||
| public: |  | ||||||
|     GPClient(QWidget *parent = nullptr); |  | ||||||
|     ~GPClient(); |  | ||||||
|  |  | ||||||
|     void activate(); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void on_connectButton_clicked(); |  | ||||||
|     void on_portalInput_returnPressed(); |  | ||||||
|     void on_portalInput_editingFinished(); |  | ||||||
|  |  | ||||||
|     void onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason); |  | ||||||
|     void onGatewayChanged(QAction *action); |  | ||||||
|  |  | ||||||
|     void onPortalSuccess(const PortalConfigResponse portalConfig, const QString region); |  | ||||||
|     void onPortalPreloginFail(const QString msg); |  | ||||||
|     void onPortalConfigFail(const QString msg); |  | ||||||
|     void onPortalFail(const QString &msg); |  | ||||||
|  |  | ||||||
|     void onGatewaySuccess(const QString &authCookie); |  | ||||||
|     void onGatewayFail(const QString &msg); |  | ||||||
|  |  | ||||||
|     void onVPNConnected(); |  | ||||||
|     void onVPNDisconnected(); |  | ||||||
|     void onVPNLogAvailable(QString log); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     enum class VpnStatus |  | ||||||
|     { |  | ||||||
|         disconnected, |  | ||||||
|         pending, |  | ||||||
|         connected |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     Ui::GPClient *ui; |  | ||||||
|     com::yuezk::qt::GPService *vpn; |  | ||||||
|  |  | ||||||
|     QSystemTrayIcon *systemTrayIcon; |  | ||||||
|     QMenu *contextMenu; |  | ||||||
|     QAction *openAction; |  | ||||||
|     QAction *connectAction; |  | ||||||
|  |  | ||||||
|     QMenu *gatewaySwitchMenu; |  | ||||||
|     QAction *clearAction; |  | ||||||
|     QAction *quitAction; |  | ||||||
|  |  | ||||||
|     bool isQuickConnect { false }; |  | ||||||
|     bool isSwitchingGateway { false }; |  | ||||||
|     PortalConfigResponse portalConfig; |  | ||||||
|  |  | ||||||
|     void initSystemTrayIcon(); |  | ||||||
|     void initVpnStatus(); |  | ||||||
|     void populateGatewayMenu(); |  | ||||||
|     void updateConnectionStatus(const VpnStatus &status); |  | ||||||
|  |  | ||||||
|     void doConnect(); |  | ||||||
|     void portalLogin(); |  | ||||||
|     void tryGatewayLogin(); |  | ||||||
|     void gatewayLogin(); |  | ||||||
|  |  | ||||||
|     QString portal() const; |  | ||||||
|     bool connected() const; |  | ||||||
|  |  | ||||||
|     QList<GPGateway> allGateways() const; |  | ||||||
|     void setAllGateways(QList<GPGateway> gateways); |  | ||||||
|  |  | ||||||
|     GPGateway currentGateway() const; |  | ||||||
|     void setCurrentGateway(const GPGateway gateway); |  | ||||||
|  |  | ||||||
|     void clearSettings(); |  | ||||||
|     void quit(); |  | ||||||
| }; |  | ||||||
| #endif // GPCLIENT_H |  | ||||||
| @@ -1,133 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <ui version="4.0"> |  | ||||||
|  <class>GPClient</class> |  | ||||||
|  <widget class="QMainWindow" name="GPClient"> |  | ||||||
|   <property name="geometry"> |  | ||||||
|    <rect> |  | ||||||
|     <x>0</x> |  | ||||||
|     <y>0</y> |  | ||||||
|     <width>260</width> |  | ||||||
|     <height>338</height> |  | ||||||
|    </rect> |  | ||||||
|   </property> |  | ||||||
|   <property name="windowTitle"> |  | ||||||
|    <string>GlobalProtect OpenConnect</string> |  | ||||||
|   </property> |  | ||||||
|   <property name="windowIcon"> |  | ||||||
|    <iconset resource="resources.qrc"> |  | ||||||
|     <normaloff>:/images/logo.svg</normaloff>:/images/logo.svg</iconset> |  | ||||||
|   </property> |  | ||||||
|   <property name="styleSheet"> |  | ||||||
|    <string notr="true"/> |  | ||||||
|   </property> |  | ||||||
|   <property name="iconSize"> |  | ||||||
|    <size> |  | ||||||
|     <width>22</width> |  | ||||||
|     <height>22</height> |  | ||||||
|    </size> |  | ||||||
|   </property> |  | ||||||
|   <widget class="QWidget" name="centralwidget"> |  | ||||||
|    <property name="sizePolicy"> |  | ||||||
|     <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |  | ||||||
|      <horstretch>0</horstretch> |  | ||||||
|      <verstretch>0</verstretch> |  | ||||||
|     </sizepolicy> |  | ||||||
|    </property> |  | ||||||
|    <property name="layoutDirection"> |  | ||||||
|     <enum>Qt::LeftToRight</enum> |  | ||||||
|    </property> |  | ||||||
|    <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0"> |  | ||||||
|     <property name="leftMargin"> |  | ||||||
|      <number>15</number> |  | ||||||
|     </property> |  | ||||||
|     <property name="topMargin"> |  | ||||||
|      <number>15</number> |  | ||||||
|     </property> |  | ||||||
|     <property name="rightMargin"> |  | ||||||
|      <number>15</number> |  | ||||||
|     </property> |  | ||||||
|     <property name="bottomMargin"> |  | ||||||
|      <number>15</number> |  | ||||||
|     </property> |  | ||||||
|     <item> |  | ||||||
|      <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0"> |  | ||||||
|       <property name="bottomMargin"> |  | ||||||
|        <number>15</number> |  | ||||||
|       </property> |  | ||||||
|       <item> |  | ||||||
|        <widget class="QLabel" name="statusImage"> |  | ||||||
|         <property name="styleSheet"> |  | ||||||
|          <string notr="true">#statusImage { |  | ||||||
| 	image: url(:/images/not_connected.png); |  | ||||||
| 	padding: 15 |  | ||||||
| }</string> |  | ||||||
|         </property> |  | ||||||
|         <property name="text"> |  | ||||||
|          <string/> |  | ||||||
|         </property> |  | ||||||
|        </widget> |  | ||||||
|       </item> |  | ||||||
|       <item> |  | ||||||
|        <widget class="QLabel" name="statusLabel"> |  | ||||||
|         <property name="font"> |  | ||||||
|          <font> |  | ||||||
|           <pointsize>14</pointsize> |  | ||||||
|           <weight>50</weight> |  | ||||||
|           <bold>false</bold> |  | ||||||
|           <underline>false</underline> |  | ||||||
|          </font> |  | ||||||
|         </property> |  | ||||||
|         <property name="text"> |  | ||||||
|          <string>Not Connected</string> |  | ||||||
|         </property> |  | ||||||
|         <property name="alignment"> |  | ||||||
|          <set>Qt::AlignCenter</set> |  | ||||||
|         </property> |  | ||||||
|        </widget> |  | ||||||
|       </item> |  | ||||||
|      </layout> |  | ||||||
|     </item> |  | ||||||
|     <item> |  | ||||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> |  | ||||||
|       <property name="bottomMargin"> |  | ||||||
|        <number>0</number> |  | ||||||
|       </property> |  | ||||||
|       <item> |  | ||||||
|        <widget class="QLineEdit" name="portalInput"> |  | ||||||
|         <property name="text"> |  | ||||||
|          <string/> |  | ||||||
|         </property> |  | ||||||
|         <property name="placeholderText"> |  | ||||||
|          <string>Please enter your portal address</string> |  | ||||||
|         </property> |  | ||||||
|        </widget> |  | ||||||
|       </item> |  | ||||||
|       <item> |  | ||||||
|        <widget class="QPushButton" name="connectButton"> |  | ||||||
|         <property name="sizePolicy"> |  | ||||||
|          <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |  | ||||||
|           <horstretch>0</horstretch> |  | ||||||
|           <verstretch>0</verstretch> |  | ||||||
|          </sizepolicy> |  | ||||||
|         </property> |  | ||||||
|         <property name="text"> |  | ||||||
|          <string>Connect</string> |  | ||||||
|         </property> |  | ||||||
|         <property name="autoDefault"> |  | ||||||
|          <bool>true</bool> |  | ||||||
|         </property> |  | ||||||
|         <property name="default"> |  | ||||||
|          <bool>false</bool> |  | ||||||
|         </property> |  | ||||||
|        </widget> |  | ||||||
|       </item> |  | ||||||
|      </layout> |  | ||||||
|     </item> |  | ||||||
|    </layout> |  | ||||||
|   </widget> |  | ||||||
|  </widget> |  | ||||||
|  <resources> |  | ||||||
|   <include location="resources.qrc"/> |  | ||||||
|  </resources> |  | ||||||
|  <connections/> |  | ||||||
| </ui> |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| #include "gpgateway.h" |  | ||||||
|  |  | ||||||
| #include <QJsonObject> |  | ||||||
| #include <QJsonDocument> |  | ||||||
| #include <QJsonArray> |  | ||||||
|  |  | ||||||
| GPGateway::GPGateway() |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPGateway::name() const |  | ||||||
| { |  | ||||||
|     return _name; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPGateway::address() const |  | ||||||
| { |  | ||||||
|     return _address; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPGateway::setName(const QString &name) |  | ||||||
| { |  | ||||||
|     _name = name; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPGateway::setAddress(const QString &address) |  | ||||||
| { |  | ||||||
|     _address = address; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPGateway::setPriorityRules(const QMap<QString, int> &priorityRules) |  | ||||||
| { |  | ||||||
|     _priorityRules = priorityRules; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int GPGateway::priorityOf(QString ruleName) const |  | ||||||
| { |  | ||||||
|     if (_priorityRules.contains(ruleName)) { |  | ||||||
|         return _priorityRules.value(ruleName); |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QJsonObject GPGateway::toJsonObject() const |  | ||||||
| { |  | ||||||
|     QJsonObject obj; |  | ||||||
|     obj.insert("name", name()); |  | ||||||
|     obj.insert("address", address()); |  | ||||||
|  |  | ||||||
|     return obj; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPGateway::toString() const |  | ||||||
| { |  | ||||||
|     QJsonDocument jsonDoc{ toJsonObject() }; |  | ||||||
|     return QString::fromUtf8(jsonDoc.toJson()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPGateway::serialize(QList<GPGateway> &gateways) |  | ||||||
| { |  | ||||||
|     QJsonArray arr; |  | ||||||
|  |  | ||||||
|     for (auto g : gateways) { |  | ||||||
|         arr.append(g.toJsonObject()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QJsonDocument jsonDoc{ arr }; |  | ||||||
|     return QString::fromUtf8(jsonDoc.toJson()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QList<GPGateway> GPGateway::fromJson(const QString &jsonString) |  | ||||||
| { |  | ||||||
|     QList<GPGateway> gateways; |  | ||||||
|  |  | ||||||
|     if (jsonString.isEmpty()) { |  | ||||||
|         return gateways; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8()); |  | ||||||
|  |  | ||||||
|     for (auto item : jsonDoc.array()) { |  | ||||||
|         GPGateway g = GPGateway::fromJsonObject(item.toObject()); |  | ||||||
|         gateways.append(g); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return gateways; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GPGateway GPGateway::fromJsonObject(const QJsonObject &jsonObj) |  | ||||||
| { |  | ||||||
|     GPGateway g; |  | ||||||
|  |  | ||||||
|     g.setName(jsonObj.value("name").toString()); |  | ||||||
|     g.setAddress(jsonObj.value("address").toString()); |  | ||||||
|  |  | ||||||
|     return g; |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| #ifndef GPGATEWAY_H |  | ||||||
| #define GPGATEWAY_H |  | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QMap> |  | ||||||
| #include <QJsonObject> |  | ||||||
|  |  | ||||||
| class GPGateway |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     GPGateway(); |  | ||||||
|  |  | ||||||
|     QString name() const; |  | ||||||
|     QString address() const; |  | ||||||
|  |  | ||||||
|     void setName(const QString &name); |  | ||||||
|     void setAddress(const QString &address); |  | ||||||
|     void setPriorityRules(const QMap<QString, int> &priorityRules); |  | ||||||
|     int priorityOf(QString ruleName) const; |  | ||||||
|     QJsonObject toJsonObject() const; |  | ||||||
|     QString toString() const; |  | ||||||
|  |  | ||||||
|     static QString serialize(QList<GPGateway> &gateways); |  | ||||||
|     static QList<GPGateway> fromJson(const QString &jsonString); |  | ||||||
|     static GPGateway fromJsonObject(const QJsonObject &jsonObj); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QString _name; |  | ||||||
|     QString _address; |  | ||||||
|     QMap<QString, int> _priorityRules; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // GPGATEWAY_H |  | ||||||
| @@ -1,122 +0,0 @@ | |||||||
| #include "gphelper.h" |  | ||||||
| #include <QNetworkRequest> |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
| #include <QMessageBox> |  | ||||||
| #include <QDesktopWidget> |  | ||||||
| #include <QApplication> |  | ||||||
| #include <QWidget> |  | ||||||
| #include <QSslConfiguration> |  | ||||||
| #include <QSslSocket> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager; |  | ||||||
|  |  | ||||||
| QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) |  | ||||||
| { |  | ||||||
|     QNetworkRequest request(url); |  | ||||||
|  |  | ||||||
|     // Skip the ssl verifying |  | ||||||
|     QSslConfiguration conf = request.sslConfiguration(); |  | ||||||
|     conf.setPeerVerifyMode(QSslSocket::VerifyNone); |  | ||||||
|     request.setSslConfiguration(conf); |  | ||||||
|  |  | ||||||
|     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); |  | ||||||
|     request.setHeader(QNetworkRequest::UserAgentHeader, UA); |  | ||||||
|  |  | ||||||
|     if (params == nullptr) { |  | ||||||
|         return networkManager->post(request, QByteArray(nullptr)); |  | ||||||
|     } |  | ||||||
|     return networkManager->post(request, params); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName) |  | ||||||
| { |  | ||||||
|     PLOGI << gateways.size() << " gateway(s) avaiable, filter the gateways with rule: " << ruleName; |  | ||||||
|  |  | ||||||
|     GPGateway gateway = gateways.first(); |  | ||||||
|  |  | ||||||
|     for (GPGateway g : gateways) { |  | ||||||
|         if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) { |  | ||||||
|             PLOGI << "Find a preferred gateway: " << g.name(); |  | ||||||
|             gateway = g; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return gateway; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the gateway response..."; |  | ||||||
|     PLOGI << "The gateway response is: " << xml; |  | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader{xml}; |  | ||||||
|     QList<QString> args; |  | ||||||
|  |  | ||||||
|     while (!xmlReader.atEnd()) { |  | ||||||
|         xmlReader.readNextStartElement(); |  | ||||||
|         if (xmlReader.name() == "argument") { |  | ||||||
|             args.append(QUrl::toPercentEncoding(xmlReader.readElementText())); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QUrlQuery params{}; |  | ||||||
|     params.addQueryItem("authcookie", args.at(1)); |  | ||||||
|     params.addQueryItem("portal", args.at(3)); |  | ||||||
|     params.addQueryItem("user", args.at(4)); |  | ||||||
|     params.addQueryItem("domain", args.at(7)); |  | ||||||
|     params.addQueryItem("preferred-ip", args.at(15)); |  | ||||||
|     params.addQueryItem("computer", QUrl::toPercentEncoding(QSysInfo::machineHostName())); |  | ||||||
|  |  | ||||||
|     return params; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void gpclient::helper::openMessageBox(const QString &message, const QString& informativeText) |  | ||||||
| { |  | ||||||
|     QMessageBox msgBox; |  | ||||||
|     msgBox.setWindowTitle("Notice"); |  | ||||||
|     msgBox.setText(message); |  | ||||||
|     msgBox.setFixedWidth(500); |  | ||||||
|     msgBox.setStyleSheet("QLabel{min-width: 250px}"); |  | ||||||
|     msgBox.setInformativeText(informativeText); |  | ||||||
|     msgBox.exec(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void gpclient::helper::moveCenter(QWidget *widget) |  | ||||||
| { |  | ||||||
|     QDesktopWidget *desktop = QApplication::desktop(); |  | ||||||
|  |  | ||||||
|     int screenWidth, width; |  | ||||||
|     int screenHeight, height; |  | ||||||
|     int x, y; |  | ||||||
|     QSize windowSize; |  | ||||||
|  |  | ||||||
|     screenWidth = desktop->width(); |  | ||||||
|     screenHeight = desktop->height(); |  | ||||||
|  |  | ||||||
|     windowSize = widget->size(); |  | ||||||
|     width = windowSize.width(); |  | ||||||
|     height = windowSize.height(); |  | ||||||
|  |  | ||||||
|     x = (screenWidth - width) / 2; |  | ||||||
|     y = (screenHeight - height) / 2; |  | ||||||
|     y -= 50; |  | ||||||
|     widget->move(x, y); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QSettings *gpclient::helper::settings::_settings = new QSettings("com.yuezk.qt", "GPClient"); |  | ||||||
|  |  | ||||||
| QVariant gpclient::helper::settings::get(const QString &key, const QVariant &defaultValue) |  | ||||||
| { |  | ||||||
|     return _settings->value(key, defaultValue); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void gpclient::helper::settings::save(const QString &key, const QVariant &value) |  | ||||||
| { |  | ||||||
|     _settings->setValue(key, value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void gpclient::helper::settings::clear() |  | ||||||
| { |  | ||||||
|     _settings->clear(); |  | ||||||
| } |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| #ifndef GPHELPER_H |  | ||||||
| #define GPHELPER_H |  | ||||||
|  |  | ||||||
| #include "samlloginwindow.h" |  | ||||||
| #include "gpgateway.h" |  | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <QNetworkAccessManager> |  | ||||||
| #include <QNetworkRequest> |  | ||||||
| #include <QNetworkReply> |  | ||||||
| #include <QUrlQuery> |  | ||||||
| #include <QSettings> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const QString UA = "PAN GlobalProtect"; |  | ||||||
|  |  | ||||||
| namespace gpclient { |  | ||||||
|     namespace helper { |  | ||||||
|         extern QNetworkAccessManager *networkManager; |  | ||||||
|  |  | ||||||
|         QNetworkReply* createRequest(QString url, QByteArray params = nullptr); |  | ||||||
|  |  | ||||||
|         GPGateway filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName); |  | ||||||
|  |  | ||||||
|         QUrlQuery parseGatewayResponse(const QByteArray& xml); |  | ||||||
|  |  | ||||||
|         void openMessageBox(const QString& message, const QString& informativeText = ""); |  | ||||||
|  |  | ||||||
|         void moveCenter(QWidget *widget); |  | ||||||
|  |  | ||||||
|         namespace settings { |  | ||||||
|  |  | ||||||
|             extern QSettings *_settings; |  | ||||||
|  |  | ||||||
|             QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); |  | ||||||
|             void save(const QString &key, const QVariant &value); |  | ||||||
|             void clear(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // GPHELPER_H |  | ||||||
| @@ -1,70 +0,0 @@ | |||||||
| #include "loginparams.h" |  | ||||||
|  |  | ||||||
| #include <QUrlQuery> |  | ||||||
|  |  | ||||||
| LoginParams::LoginParams() |  | ||||||
| { |  | ||||||
|     params.addQueryItem("prot", QUrl::toPercentEncoding("https:")); |  | ||||||
|     params.addQueryItem("server", ""); |  | ||||||
|     params.addQueryItem("inputSrc", ""); |  | ||||||
|     params.addQueryItem("jnlpReady", "jnlpReady"); |  | ||||||
|     params.addQueryItem("user", ""); |  | ||||||
|     params.addQueryItem("passwd", ""); |  | ||||||
|     params.addQueryItem("computer", QUrl::toPercentEncoding(QSysInfo::machineHostName())); |  | ||||||
|     params.addQueryItem("ok", "Login"); |  | ||||||
|     params.addQueryItem("direct", "yes"); |  | ||||||
|     params.addQueryItem("clientVer", "4100"); |  | ||||||
|     params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName())); |  | ||||||
|     params.addQueryItem("clientos", "Linux"); |  | ||||||
|     params.addQueryItem("portal-userauthcookie", ""); |  | ||||||
|     params.addQueryItem("portal-prelogonuserauthcookie", ""); |  | ||||||
|     params.addQueryItem("prelogin-cookie", ""); |  | ||||||
|     params.addQueryItem("ipv6-support", "yes"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| LoginParams::~LoginParams() |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setUser(const QString user) |  | ||||||
| { |  | ||||||
|     updateQueryItem("user", user); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setServer(const QString server) |  | ||||||
| { |  | ||||||
|     updateQueryItem("server", server); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setPassword(const QString password) |  | ||||||
| { |  | ||||||
|     updateQueryItem("passwd", password); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setUserAuthCookie(const QString cookie) |  | ||||||
| { |  | ||||||
|     updateQueryItem("portal-userauthcookie", cookie); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setPrelogonAuthCookie(const QString cookie) |  | ||||||
| { |  | ||||||
|     updateQueryItem("portal-prelogonuserauthcookie", cookie); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::setPreloginCookie(const QString cookie) |  | ||||||
| { |  | ||||||
|     updateQueryItem("prelogin-cookie", cookie); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QByteArray LoginParams::toUtf8() const |  | ||||||
| { |  | ||||||
|     return params.toString().toUtf8(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void LoginParams::updateQueryItem(const QString key, const QString value) |  | ||||||
| { |  | ||||||
|     if (params.hasQueryItem(key)) { |  | ||||||
|         params.removeQueryItem(key); |  | ||||||
|     } |  | ||||||
|     params.addQueryItem(key, QUrl::toPercentEncoding(value)); |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| #ifndef LOGINPARAMS_H |  | ||||||
| #define LOGINPARAMS_H |  | ||||||
|  |  | ||||||
| #include <QUrlQuery> |  | ||||||
|  |  | ||||||
| class LoginParams |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     LoginParams(); |  | ||||||
|     ~LoginParams(); |  | ||||||
|  |  | ||||||
|     void setUser(const QString user); |  | ||||||
|     void setServer(const QString server); |  | ||||||
|     void setPassword(const QString password); |  | ||||||
|     void setUserAuthCookie(const QString cookie); |  | ||||||
|     void setPrelogonAuthCookie(const QString cookie); |  | ||||||
|     void setPreloginCookie(const QString cookie); |  | ||||||
|  |  | ||||||
|     QByteArray toUtf8() const; |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QUrlQuery params; |  | ||||||
|  |  | ||||||
|     void updateQueryItem(const QString key, const QString value); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // LOGINPARAMS_H |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| #include "singleapplication.h" |  | ||||||
| #include "gpclient.h" |  | ||||||
| #include "enhancedwebview.h" |  | ||||||
|  |  | ||||||
| #include <QStandardPaths> |  | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <plog/Appenders/ColorConsoleAppender.h> |  | ||||||
|  |  | ||||||
| static const QString version = "v1.2.9"; |  | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) |  | ||||||
| { |  | ||||||
|     const QDir path = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/GlobalProtect-openconnect"; |  | ||||||
|     const QString logFile = path.path() + "/gpclient.log"; |  | ||||||
|     if (!path.exists()) { |  | ||||||
|         path.mkpath("."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender; |  | ||||||
|     plog::init(plog::debug, logFile.toUtf8()).addAppender(&consoleAppender); |  | ||||||
|  |  | ||||||
|     PLOGI << "GlobalProtect started, version: " << version; |  | ||||||
|  |  | ||||||
|     QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); |  | ||||||
|  |  | ||||||
|     if (port == "") { |  | ||||||
|         qputenv(ENV_CDP_PORT, "12315"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SingleApplication app(argc, argv); |  | ||||||
|     app.setQuitOnLastWindowClosed(false); |  | ||||||
|  |  | ||||||
|     GPClient w; |  | ||||||
|     w.show(); |  | ||||||
|  |  | ||||||
|     QObject::connect(&app, &SingleApplication::instanceStarted, &w, &GPClient::activate); |  | ||||||
|  |  | ||||||
|     return app.exec(); |  | ||||||
| } |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| #include "normalloginwindow.h" |  | ||||||
| #include "ui_normalloginwindow.h" |  | ||||||
|  |  | ||||||
| #include <QCloseEvent> |  | ||||||
|  |  | ||||||
| NormalLoginWindow::NormalLoginWindow(QWidget *parent) : |  | ||||||
|     QDialog(parent), |  | ||||||
|     ui(new Ui::NormalLoginWindow) |  | ||||||
| { |  | ||||||
|     ui->setupUi(this); |  | ||||||
|     setWindowTitle("GlobalProtect Login"); |  | ||||||
|     setFixedSize(width(), height()); |  | ||||||
|     setModal(true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| NormalLoginWindow::~NormalLoginWindow() |  | ||||||
| { |  | ||||||
|     delete ui; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::setAuthMessage(QString message) |  | ||||||
| { |  | ||||||
|     ui->authMessage->setText(message); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::setUsernameLabel(QString label) |  | ||||||
| { |  | ||||||
|     ui->username->setPlaceholderText(label); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::setPasswordLabel(QString label) |  | ||||||
| { |  | ||||||
|     ui->password->setPlaceholderText(label); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::setPortalAddress(QString portal) |  | ||||||
| { |  | ||||||
|     ui->portalAddress->setText(portal); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::setProcessing(bool isProcessing) |  | ||||||
| { |  | ||||||
|     ui->username->setReadOnly(isProcessing); |  | ||||||
|     ui->password->setReadOnly(isProcessing); |  | ||||||
|     ui->loginButton->setDisabled(isProcessing); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::on_loginButton_clicked() |  | ||||||
| { |  | ||||||
|     const QString username = ui->username->text().trimmed(); |  | ||||||
|     const QString password = ui->password->text().trimmed(); |  | ||||||
|  |  | ||||||
|     if (username.isEmpty() || password.isEmpty()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     emit performLogin(username, password); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void NormalLoginWindow::closeEvent(QCloseEvent *event) |  | ||||||
| { |  | ||||||
|     event->accept(); |  | ||||||
|     reject(); |  | ||||||
| } |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| #ifndef PORTALAUTHWINDOW_H |  | ||||||
| #define PORTALAUTHWINDOW_H |  | ||||||
|  |  | ||||||
| #include <QDialog> |  | ||||||
|  |  | ||||||
| namespace Ui { |  | ||||||
| class NormalLoginWindow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class NormalLoginWindow : public QDialog |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
|  |  | ||||||
| public: |  | ||||||
|     explicit NormalLoginWindow(QWidget *parent = nullptr); |  | ||||||
|     ~NormalLoginWindow(); |  | ||||||
|  |  | ||||||
|     void setAuthMessage(QString); |  | ||||||
|     void setUsernameLabel(QString); |  | ||||||
|     void setPasswordLabel(QString); |  | ||||||
|     void setPortalAddress(QString); |  | ||||||
|  |  | ||||||
|     void setProcessing(bool isProcessing); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void on_loginButton_clicked(); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void performLogin(QString username, QString password); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     Ui::NormalLoginWindow *ui; |  | ||||||
|  |  | ||||||
|     void closeEvent(QCloseEvent *event); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // PORTALAUTHWINDOW_H |  | ||||||
| @@ -1,148 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <ui version="4.0"> |  | ||||||
|  <class>NormalLoginWindow</class> |  | ||||||
|  <widget class="QDialog" name="NormalLoginWindow"> |  | ||||||
|   <property name="geometry"> |  | ||||||
|    <rect> |  | ||||||
|     <x>0</x> |  | ||||||
|     <y>0</y> |  | ||||||
|     <width>255</width> |  | ||||||
|     <height>269</height> |  | ||||||
|    </rect> |  | ||||||
|   </property> |  | ||||||
|   <property name="sizePolicy"> |  | ||||||
|    <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> |  | ||||||
|     <horstretch>0</horstretch> |  | ||||||
|     <verstretch>0</verstretch> |  | ||||||
|    </sizepolicy> |  | ||||||
|   </property> |  | ||||||
|   <property name="cursor"> |  | ||||||
|    <cursorShape>ArrowCursor</cursorShape> |  | ||||||
|   </property> |  | ||||||
|   <property name="windowTitle"> |  | ||||||
|    <string>Login</string> |  | ||||||
|   </property> |  | ||||||
|   <property name="modal"> |  | ||||||
|    <bool>true</bool> |  | ||||||
|   </property> |  | ||||||
|   <layout class="QVBoxLayout" name="verticalLayout_5"> |  | ||||||
|    <item> |  | ||||||
|     <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,0,0"> |  | ||||||
|      <item> |  | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout"> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLabel" name="label"> |  | ||||||
|          <property name="font"> |  | ||||||
|           <font> |  | ||||||
|            <pointsize>20</pointsize> |  | ||||||
|           </font> |  | ||||||
|          </property> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string>Login</string> |  | ||||||
|          </property> |  | ||||||
|          <property name="alignment"> |  | ||||||
|           <set>Qt::AlignCenter</set> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLabel" name="authMessage"> |  | ||||||
|          <property name="enabled"> |  | ||||||
|           <bool>true</bool> |  | ||||||
|          </property> |  | ||||||
|          <property name="sizePolicy"> |  | ||||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |  | ||||||
|            <horstretch>0</horstretch> |  | ||||||
|            <verstretch>2</verstretch> |  | ||||||
|           </sizepolicy> |  | ||||||
|          </property> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string>Please enter the login credentials</string> |  | ||||||
|          </property> |  | ||||||
|          <property name="alignment"> |  | ||||||
|           <set>Qt::AlignCenter</set> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|       </layout> |  | ||||||
|      </item> |  | ||||||
|      <item> |  | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_2"> |  | ||||||
|        <property name="spacing"> |  | ||||||
|         <number>0</number> |  | ||||||
|        </property> |  | ||||||
|        <property name="leftMargin"> |  | ||||||
|         <number>6</number> |  | ||||||
|        </property> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLabel" name="portalLabel"> |  | ||||||
|          <property name="sizePolicy"> |  | ||||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |  | ||||||
|            <horstretch>0</horstretch> |  | ||||||
|            <verstretch>0</verstretch> |  | ||||||
|           </sizepolicy> |  | ||||||
|          </property> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string>Portal:</string> |  | ||||||
|          </property> |  | ||||||
|          <property name="margin"> |  | ||||||
|           <number>0</number> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLabel" name="portalAddress"> |  | ||||||
|          <property name="sizePolicy"> |  | ||||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |  | ||||||
|            <horstretch>0</horstretch> |  | ||||||
|            <verstretch>0</verstretch> |  | ||||||
|           </sizepolicy> |  | ||||||
|          </property> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string>vpn.example.com</string> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|       </layout> |  | ||||||
|      </item> |  | ||||||
|      <item> |  | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_3"> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLineEdit" name="username"> |  | ||||||
|          <property name="placeholderText"> |  | ||||||
|           <string>Username</string> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QLineEdit" name="password"> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string/> |  | ||||||
|          </property> |  | ||||||
|          <property name="echoMode"> |  | ||||||
|           <enum>QLineEdit::Password</enum> |  | ||||||
|          </property> |  | ||||||
|          <property name="placeholderText"> |  | ||||||
|           <string>Password</string> |  | ||||||
|          </property> |  | ||||||
|          <property name="clearButtonEnabled"> |  | ||||||
|           <bool>false</bool> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|        <item> |  | ||||||
|         <widget class="QPushButton" name="loginButton"> |  | ||||||
|          <property name="text"> |  | ||||||
|           <string>Login</string> |  | ||||||
|          </property> |  | ||||||
|         </widget> |  | ||||||
|        </item> |  | ||||||
|       </layout> |  | ||||||
|      </item> |  | ||||||
|     </layout> |  | ||||||
|    </item> |  | ||||||
|   </layout> |  | ||||||
|  </widget> |  | ||||||
|  <resources/> |  | ||||||
|  <connections/> |  | ||||||
| </ui> |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 16 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 16 KiB | 
| @@ -1,206 +0,0 @@ | |||||||
| #include "portalauthenticator.h" |  | ||||||
| #include "gphelper.h" |  | ||||||
| #include "normalloginwindow.h" |  | ||||||
| #include "samlloginwindow.h" |  | ||||||
| #include "loginparams.h" |  | ||||||
| #include "preloginresponse.h" |  | ||||||
| #include "portalconfigresponse.h" |  | ||||||
| #include "gpgateway.h" |  | ||||||
|  |  | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <QNetworkReply> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; |  | ||||||
|  |  | ||||||
| PortalAuthenticator::PortalAuthenticator(const QString& portal) : QObject() |  | ||||||
|   , portal(portal) |  | ||||||
|   , preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") |  | ||||||
|   , configUrl("https://" + portal + "/global-protect/getconfig.esp") |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PortalAuthenticator::~PortalAuthenticator() |  | ||||||
| { |  | ||||||
|     delete normalLoginWindow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::authenticate() |  | ||||||
| { |  | ||||||
|     PLOGI << "Preform portal prelogin at " << preloginUrl; |  | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(preloginUrl); |  | ||||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onPreloginFinished() |  | ||||||
| { |  | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |  | ||||||
|  |  | ||||||
|     if (reply->error()) { |  | ||||||
|         PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl).arg(reply->errorString()); |  | ||||||
|         emit preloginFailed("Error occurred on the portal prelogin interface."); |  | ||||||
|         delete reply; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Portal prelogin succeeded."; |  | ||||||
|  |  | ||||||
|     preloginResponse = PreloginResponse::parse(reply->readAll()); |  | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region(); |  | ||||||
|  |  | ||||||
|     if (preloginResponse.hasSamlAuthFields()) { |  | ||||||
|         // Do SAML authentication |  | ||||||
|         samlAuth(); |  | ||||||
|     } else if (preloginResponse.hasNormalAuthFields()) { |  | ||||||
|         // Do normal username/password authentication |  | ||||||
|         tryAutoLogin(); |  | ||||||
|     } else { |  | ||||||
|         PLOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse())); |  | ||||||
|         emit preloginFailed("Unknown response for portal prelogin interface."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     delete reply; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::tryAutoLogin() |  | ||||||
| { |  | ||||||
|     const QString username = settings::get("username").toString(); |  | ||||||
|     const QString password = settings::get("password").toString(); |  | ||||||
|  |  | ||||||
|     if (!username.isEmpty() && !password.isEmpty()) { |  | ||||||
|         PLOGI << "Trying auto login using the saved credentials"; |  | ||||||
|         isAutoLogin = true; |  | ||||||
|         fetchConfig(settings::get("username").toString(), settings::get("password").toString()); |  | ||||||
|     } else { |  | ||||||
|         normalAuth(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::normalAuth() |  | ||||||
| { |  | ||||||
|     PLOGI << "Trying to launch the normal login window..."; |  | ||||||
|  |  | ||||||
|     normalLoginWindow = new NormalLoginWindow; |  | ||||||
|     normalLoginWindow->setPortalAddress(portal); |  | ||||||
|     normalLoginWindow->setAuthMessage(preloginResponse.authMessage()); |  | ||||||
|     normalLoginWindow->setUsernameLabel(preloginResponse.labelUsername()); |  | ||||||
|     normalLoginWindow->setPasswordLabel(preloginResponse.labelPassword()); |  | ||||||
|  |  | ||||||
|     // Do login |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); |  | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); |  | ||||||
|  |  | ||||||
|     normalLoginWindow->show(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) |  | ||||||
| { |  | ||||||
|     normalLoginWindow->setProcessing(true); |  | ||||||
|     fetchConfig(username, password); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onLoginWindowRejected() |  | ||||||
| { |  | ||||||
|     emitFail(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onLoginWindowFinished() |  | ||||||
| { |  | ||||||
|     delete normalLoginWindow; |  | ||||||
|     normalLoginWindow = nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::samlAuth() |  | ||||||
| { |  | ||||||
|     PLOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod(); |  | ||||||
|  |  | ||||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; |  | ||||||
|  |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &PortalAuthenticator::onSAMLLoginSuccess); |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &PortalAuthenticator::onSAMLLoginFail); |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); |  | ||||||
|  |  | ||||||
|     loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult) |  | ||||||
| { |  | ||||||
|     if (samlResult.contains("preloginCookie")) { |  | ||||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); |  | ||||||
|     } else { |  | ||||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie")); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onSAMLLoginFail(const QString msg) |  | ||||||
| { |  | ||||||
|     emitFail(msg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie) |  | ||||||
| { |  | ||||||
|     LoginParams params; |  | ||||||
|     params.setServer(portal); |  | ||||||
|     params.setUser(username); |  | ||||||
|     params.setPassword(password); |  | ||||||
|     params.setPreloginCookie(preloginCookie); |  | ||||||
|     params.setUserAuthCookie(userAuthCookie); |  | ||||||
|  |  | ||||||
|     // Save the username and password for future use. |  | ||||||
|     this->username = username; |  | ||||||
|     this->password = password; |  | ||||||
|  |  | ||||||
|     PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username; |  | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(configUrl, params.toUtf8()); |  | ||||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::onFetchConfigFinished() |  | ||||||
| { |  | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |  | ||||||
|  |  | ||||||
|     if (reply->error()) { |  | ||||||
|         PLOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString()); |  | ||||||
|  |  | ||||||
|         // Login failed, enable the fields of the normal login window |  | ||||||
|         if (normalLoginWindow) { |  | ||||||
|             normalLoginWindow->setProcessing(false); |  | ||||||
|             openMessageBox("Portal login failed.", "Please check your credentials and try again."); |  | ||||||
|         } else if (isAutoLogin) { |  | ||||||
|             isAutoLogin = false; |  | ||||||
|             normalAuth(); |  | ||||||
|         } else { |  | ||||||
|             emit portalConfigFailed("Failed to fetch the portal config."); |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Fetch the portal config succeeded."; |  | ||||||
|     PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll()); |  | ||||||
|  |  | ||||||
|     // Add the username & password to the response object |  | ||||||
|     response.setUsername(username); |  | ||||||
|     response.setPassword(password); |  | ||||||
|  |  | ||||||
|     // Close the login window |  | ||||||
|     if (normalLoginWindow) { |  | ||||||
|         PLOGI << "Closing the NormalLoginWindow..."; |  | ||||||
|  |  | ||||||
|         // Save the credentials for reuse |  | ||||||
|         settings::save("username", username); |  | ||||||
|         settings::save("password", password); |  | ||||||
|         normalLoginWindow->close(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     emit success(response, preloginResponse.region()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalAuthenticator::emitFail(const QString& msg) |  | ||||||
| { |  | ||||||
|     emit fail(msg); |  | ||||||
| } |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| #ifndef PORTALAUTHENTICATOR_H |  | ||||||
| #define PORTALAUTHENTICATOR_H |  | ||||||
|  |  | ||||||
| #include "portalconfigresponse.h" |  | ||||||
| #include "normalloginwindow.h" |  | ||||||
| #include "samlloginwindow.h" |  | ||||||
| #include "preloginresponse.h" |  | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
|  |  | ||||||
| class PortalAuthenticator : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit PortalAuthenticator(const QString& portal); |  | ||||||
|     ~PortalAuthenticator(); |  | ||||||
|  |  | ||||||
|     void authenticate(); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void success(const PortalConfigResponse response, const QString region); |  | ||||||
|     void fail(const QString& msg); |  | ||||||
|     void preloginFailed(const QString& msg); |  | ||||||
|     void portalConfigFailed(const QString msg); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onPreloginFinished(); |  | ||||||
|     void onPerformNormalLogin(const QString &username, const QString &password); |  | ||||||
|     void onLoginWindowRejected(); |  | ||||||
|     void onLoginWindowFinished(); |  | ||||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> samlResult); |  | ||||||
|     void onSAMLLoginFail(const QString msg); |  | ||||||
|     void onFetchConfigFinished(); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QString portal; |  | ||||||
|     QString preloginUrl; |  | ||||||
|     QString configUrl; |  | ||||||
|     QString username; |  | ||||||
|     QString password; |  | ||||||
|  |  | ||||||
|     PreloginResponse preloginResponse; |  | ||||||
|  |  | ||||||
|     bool isAutoLogin { false }; |  | ||||||
|  |  | ||||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; |  | ||||||
|  |  | ||||||
|     void tryAutoLogin(); |  | ||||||
|     void normalAuth(); |  | ||||||
|     void samlAuth(); |  | ||||||
|     void fetchConfig(QString username, QString password, QString preloginCookie = "", QString userAuthCookie = ""); |  | ||||||
|     void emitFail(const QString& msg = ""); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // PORTALAUTHENTICATOR_H |  | ||||||
| @@ -1,178 +0,0 @@ | |||||||
| #include "portalconfigresponse.h" |  | ||||||
|  |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::xmlUserAuthCookie = "portal-userauthcookie"; |  | ||||||
| QString PortalConfigResponse::xmlPrelogonUserAuthCookie = "portal-prelogonuserauthcookie"; |  | ||||||
| QString PortalConfigResponse::xmlGateways = "gateways"; |  | ||||||
|  |  | ||||||
| PortalConfigResponse::PortalConfigResponse() |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PortalConfigResponse::~PortalConfigResponse() |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the portal configuration..."; |  | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader(xml); |  | ||||||
|     PortalConfigResponse response; |  | ||||||
|     response.setRawResponse(xml); |  | ||||||
|  |  | ||||||
|     while (!xmlReader.atEnd()) { |  | ||||||
|         xmlReader.readNextStartElement(); |  | ||||||
|  |  | ||||||
|         QString name = xmlReader.name().toString(); |  | ||||||
|  |  | ||||||
|         if (name == xmlUserAuthCookie) { |  | ||||||
|             PLOGI << "Start reading " << name; |  | ||||||
|             response.setUserAuthCookie(xmlReader.readElementText()); |  | ||||||
|         } else if (name == xmlPrelogonUserAuthCookie) { |  | ||||||
|             PLOGI << "Start reading " << name; |  | ||||||
|             response.setPrelogonUserAuthCookie(xmlReader.readElementText()); |  | ||||||
|         } else if (name == xmlGateways) { |  | ||||||
|             response.setAllGateways(parseGateways(xmlReader)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing portal configuration."; |  | ||||||
|  |  | ||||||
|     return response; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const QByteArray PortalConfigResponse::rawResponse() const |  | ||||||
| { |  | ||||||
|     return _rawResponse; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::username() const |  | ||||||
| { |  | ||||||
|     return _username; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::password() const |  | ||||||
| { |  | ||||||
|     return _password; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the gateways from portal configuration..."; |  | ||||||
|  |  | ||||||
|     QList<GPGateway> gateways; |  | ||||||
|  |  | ||||||
|     while (xmlReader.name() != "external"){ |  | ||||||
|         xmlReader.readNext(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (xmlReader.name() != "list"){ |  | ||||||
|         xmlReader.readNext(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (xmlReader.name() != xmlGateways || !xmlReader.isEndElement()) { |  | ||||||
|         xmlReader.readNext(); |  | ||||||
|         // Parse the gateways -> external -> list -> entry |  | ||||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { |  | ||||||
|             GPGateway g; |  | ||||||
|             QString address = xmlReader.attributes().value("name").toString(); |  | ||||||
|             g.setAddress(address); |  | ||||||
|             g.setPriorityRules(parsePriorityRules(xmlReader)); |  | ||||||
|             g.setName(parseGatewayName(xmlReader)); |  | ||||||
|             gateways.append(g); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the gateways."; |  | ||||||
|  |  | ||||||
|     return gateways; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QMap<QString, int> PortalConfigResponse::parsePriorityRules(QXmlStreamReader &xmlReader) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the priority rules..."; |  | ||||||
|  |  | ||||||
|     QMap<QString, int> priorityRules; |  | ||||||
|  |  | ||||||
|     while ((xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) && !xmlReader.hasError()) { |  | ||||||
|         xmlReader.readNext(); |  | ||||||
|  |  | ||||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { |  | ||||||
|             QString ruleName = xmlReader.attributes().value("name").toString(); |  | ||||||
|             // Read the priority tag |  | ||||||
|             while (xmlReader.name() != "priority"){ |  | ||||||
|                 xmlReader.readNext(); |  | ||||||
|             } |  | ||||||
|             int ruleValue = xmlReader.readElementText().toUInt(); |  | ||||||
|             priorityRules.insert(ruleName, ruleValue); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the priority rules."; |  | ||||||
|  |  | ||||||
|     return priorityRules; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the gateway name..."; |  | ||||||
|  |  | ||||||
|     while (xmlReader.name() != "description" || !xmlReader.isEndElement()) { |  | ||||||
|         xmlReader.readNext(); |  | ||||||
|         if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) { |  | ||||||
|             PLOGI << "Finished parsing the gateway name"; |  | ||||||
|             return xmlReader.readElementText(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PLOGE << "Error: <description> tag not found"; |  | ||||||
|     return ""; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::userAuthCookie() const |  | ||||||
| { |  | ||||||
|     return _userAuthCookie; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::prelogonUserAuthCookie() const |  | ||||||
| { |  | ||||||
|     return _prelogonAuthCookie; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QList<GPGateway> PortalConfigResponse::allGateways() const |  | ||||||
| { |  | ||||||
|     return _gateways; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setAllGateways(QList<GPGateway> gateways) |  | ||||||
| { |  | ||||||
|     _gateways = gateways; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setRawResponse(const QByteArray response) |  | ||||||
| { |  | ||||||
|     _rawResponse = response; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setUsername(const QString username) |  | ||||||
| { |  | ||||||
|     _username = username; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setPassword(const QString password) |  | ||||||
| { |  | ||||||
|     _password = password; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setUserAuthCookie(const QString cookie) |  | ||||||
| { |  | ||||||
|     _userAuthCookie = cookie; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie) |  | ||||||
| { |  | ||||||
|     _prelogonAuthCookie = cookie; |  | ||||||
| } |  | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| #ifndef PORTALCONFIGRESPONSE_H |  | ||||||
| #define PORTALCONFIGRESPONSE_H |  | ||||||
|  |  | ||||||
| #include "gpgateway.h" |  | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QList> |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
|  |  | ||||||
| class PortalConfigResponse |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     PortalConfigResponse(); |  | ||||||
|     ~PortalConfigResponse(); |  | ||||||
|  |  | ||||||
|     static PortalConfigResponse parse(const QByteArray xml); |  | ||||||
|  |  | ||||||
|     const QByteArray rawResponse() const; |  | ||||||
|     QString username() const; |  | ||||||
|     QString password() const; |  | ||||||
|     QString userAuthCookie() const; |  | ||||||
|     QString prelogonUserAuthCookie() const; |  | ||||||
|     QList<GPGateway> allGateways() const; |  | ||||||
|     void setAllGateways(QList<GPGateway> gateways); |  | ||||||
|  |  | ||||||
|     void setUsername(const QString username); |  | ||||||
|     void setPassword(const QString password); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     static QString xmlUserAuthCookie; |  | ||||||
|     static QString xmlPrelogonUserAuthCookie; |  | ||||||
|     static QString xmlGateways; |  | ||||||
|  |  | ||||||
|     QByteArray _rawResponse; |  | ||||||
|     QString _username; |  | ||||||
|     QString _password; |  | ||||||
|     QString _userAuthCookie; |  | ||||||
|     QString _prelogonAuthCookie; |  | ||||||
|  |  | ||||||
|     QList<GPGateway> _gateways; |  | ||||||
|  |  | ||||||
|     void setRawResponse(const QByteArray response); |  | ||||||
|     void setUserAuthCookie(const QString cookie); |  | ||||||
|     void setPrelogonUserAuthCookie(const QString cookie); |  | ||||||
|  |  | ||||||
|     static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader); |  | ||||||
|     static QMap<QString, int> parsePriorityRules(QXmlStreamReader &xmlReader); |  | ||||||
|     static QString parseGatewayName(QXmlStreamReader &xmlReader); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // PORTALCONFIGRESPONSE_H |  | ||||||
| @@ -1,100 +0,0 @@ | |||||||
| #include "preloginresponse.h" |  | ||||||
|  |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
| #include <QMap> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| QString PreloginResponse::xmlAuthMessage = "authentication-message"; |  | ||||||
| QString PreloginResponse::xmlLabelUsername = "username-label"; |  | ||||||
| QString PreloginResponse::xmlLabelPassword = "password-label"; |  | ||||||
| QString PreloginResponse::xmlSamlMethod = "saml-auth-method"; |  | ||||||
| QString PreloginResponse::xmlSamlRequest = "saml-request"; |  | ||||||
| QString PreloginResponse::xmlRegion = "region"; |  | ||||||
|  |  | ||||||
| PreloginResponse::PreloginResponse() |  | ||||||
| { |  | ||||||
|     add(xmlAuthMessage, ""); |  | ||||||
|     add(xmlLabelUsername, ""); |  | ||||||
|     add(xmlLabelPassword, ""); |  | ||||||
|     add(xmlSamlMethod, ""); |  | ||||||
|     add(xmlSamlRequest, ""); |  | ||||||
|     add(xmlRegion, ""); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PreloginResponse PreloginResponse::parse(const QByteArray& xml) |  | ||||||
| { |  | ||||||
|     PLOGI << "Start parsing the prelogin response..."; |  | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader(xml); |  | ||||||
|     PreloginResponse response; |  | ||||||
|     response.setRawResponse(xml); |  | ||||||
|  |  | ||||||
|     while (!xmlReader.atEnd()) { |  | ||||||
|         xmlReader.readNextStartElement(); |  | ||||||
|         QString name = xmlReader.name().toString(); |  | ||||||
|         if (response.has(name)) { |  | ||||||
|             response.add(name, xmlReader.readElementText()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return response; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const QByteArray& PreloginResponse::rawResponse() const |  | ||||||
| { |  | ||||||
|     return _rawResponse; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::authMessage() const |  | ||||||
| { |  | ||||||
|     return resultMap.value(xmlAuthMessage); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::labelUsername() const |  | ||||||
| { |  | ||||||
|     return resultMap.value(xmlLabelUsername); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::labelPassword() const |  | ||||||
| { |  | ||||||
|     return resultMap.value(xmlLabelPassword); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::samlMethod() const |  | ||||||
| { |  | ||||||
|     return resultMap.value(xmlSamlMethod); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::samlRequest() const |  | ||||||
| { |  | ||||||
|     return QByteArray::fromBase64(resultMap.value(xmlSamlRequest).toUtf8()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PreloginResponse::region() const |  | ||||||
| { |  | ||||||
|     return resultMap.value(xmlRegion); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool PreloginResponse::hasSamlAuthFields() const |  | ||||||
| { |  | ||||||
|     return !samlMethod().isEmpty() && !samlRequest().isEmpty(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool PreloginResponse::hasNormalAuthFields() const |  | ||||||
| { |  | ||||||
|     return !labelUsername().isEmpty() && !labelPassword().isEmpty(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PreloginResponse::setRawResponse(const QByteArray response) |  | ||||||
| { |  | ||||||
|     _rawResponse = response; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool PreloginResponse::has(const QString name) const |  | ||||||
| { |  | ||||||
|     return resultMap.contains(name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PreloginResponse::add(const QString name, const QString value) |  | ||||||
| { |  | ||||||
|     resultMap.insert(name, value); |  | ||||||
| } |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| #ifndef PRELOGINRESPONSE_H |  | ||||||
| #define PRELOGINRESPONSE_H |  | ||||||
|  |  | ||||||
| #include <QString> |  | ||||||
| #include <QMap> |  | ||||||
|  |  | ||||||
| class PreloginResponse |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     PreloginResponse(); |  | ||||||
|  |  | ||||||
|     static PreloginResponse parse(const QByteArray& xml); |  | ||||||
|  |  | ||||||
|     const QByteArray& rawResponse() const; |  | ||||||
|     QString authMessage() const; |  | ||||||
|     QString labelUsername() const; |  | ||||||
|     QString labelPassword() const; |  | ||||||
|     QString samlMethod() const; |  | ||||||
|     QString samlRequest() const; |  | ||||||
|     QString region() const; |  | ||||||
|  |  | ||||||
|     bool hasSamlAuthFields() const; |  | ||||||
|     bool hasNormalAuthFields() const; |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     static QString xmlAuthMessage; |  | ||||||
|     static QString xmlLabelUsername; |  | ||||||
|     static QString xmlLabelPassword; |  | ||||||
|     static QString xmlSamlMethod; |  | ||||||
|     static QString xmlSamlRequest; |  | ||||||
|     static QString xmlRegion; |  | ||||||
|  |  | ||||||
|     QMap<QString, QString> resultMap; |  | ||||||
|     QByteArray _rawResponse; |  | ||||||
|  |  | ||||||
|     void setRawResponse(const QByteArray response); |  | ||||||
|     void add(const QString name, const QString value); |  | ||||||
|     bool has(const QString name) const; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // PRELOGINRESPONSE_H |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 1.2 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 993 B | 
| @@ -1,10 +0,0 @@ | |||||||
| <RCC> |  | ||||||
|     <qresource prefix="/images"> |  | ||||||
|         <file alias="logo.svg">com.yuezk.qt.GPClient.svg</file> |  | ||||||
|         <file>connected.png</file> |  | ||||||
|         <file>pending.png</file> |  | ||||||
|         <file>not_connected.png</file> |  | ||||||
|         <file>radio_unselected.png</file> |  | ||||||
|         <file>radio_selected.png</file> |  | ||||||
|     </qresource> |  | ||||||
| </RCC> |  | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| #include "samlloginwindow.h" |  | ||||||
|  |  | ||||||
| #include <QVBoxLayout> |  | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <QWebEngineProfile> |  | ||||||
| #include <QWebEngineView> |  | ||||||
|  |  | ||||||
| SAMLLoginWindow::SAMLLoginWindow(QWidget *parent) |  | ||||||
|     : QDialog(parent) |  | ||||||
|     , webView(new EnhancedWebView(this)) |  | ||||||
| { |  | ||||||
|     setWindowTitle("GlobalProtect SAML Login"); |  | ||||||
|     setModal(true); |  | ||||||
|     resize(700, 550); |  | ||||||
|  |  | ||||||
|     QVBoxLayout *verticalLayout = new QVBoxLayout(this); |  | ||||||
|     webView->setUrl(QUrl("about:blank")); |  | ||||||
|     // webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); |  | ||||||
|     verticalLayout->addWidget(webView); |  | ||||||
|  |  | ||||||
|     webView->initialize(); |  | ||||||
|     connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived); |  | ||||||
|     connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| SAMLLoginWindow::~SAMLLoginWindow() |  | ||||||
| { |  | ||||||
|     delete webView; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SAMLLoginWindow::closeEvent(QCloseEvent *event) |  | ||||||
| { |  | ||||||
|     event->accept(); |  | ||||||
|     reject(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloingUrl) |  | ||||||
| { |  | ||||||
|     if (samlMethod == "POST") { |  | ||||||
|         webView->setHtml(samlRequest, preloingUrl); |  | ||||||
|     } else if (samlMethod == "REDIRECT") { |  | ||||||
|         webView->load(samlRequest); |  | ||||||
|     } else { |  | ||||||
|         PLOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; |  | ||||||
|         emit fail("Unknown saml-auth-method, got " + samlMethod); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SAMLLoginWindow::onResponseReceived(QJsonObject params) |  | ||||||
| { |  | ||||||
|     QString type = params.value("type").toString(); |  | ||||||
|     // Skip non-document response |  | ||||||
|     if (type != "Document") { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QJsonObject response = params.value("response").toObject(); |  | ||||||
|     QJsonObject headers = response.value("headers").toObject(); |  | ||||||
|  |  | ||||||
|     const QString username = headers.value("saml-username").toString(); |  | ||||||
|     const QString preloginCookie = headers.value("prelogin-cookie").toString(); |  | ||||||
|     const QString userAuthCookie = headers.value("portal-userauthcookie").toString(); |  | ||||||
|  |  | ||||||
|     LOGI << "Response received from " << response.value("url").toString(); |  | ||||||
|  |  | ||||||
|     if (!username.isEmpty()) { |  | ||||||
|         LOGI << "Got username from SAML response headers " << username; |  | ||||||
|         samlResult.insert("username", username); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!preloginCookie.isEmpty()) { |  | ||||||
|         LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie; |  | ||||||
|         samlResult.insert("preloginCookie", preloginCookie); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!userAuthCookie.isEmpty()) { |  | ||||||
|         LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie; |  | ||||||
|         samlResult.insert("userAuthCookie", userAuthCookie); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Check the SAML result |  | ||||||
|     if (samlResult.contains("username") |  | ||||||
|             && (samlResult.contains("preloginCookie") || samlResult.contains("userAuthCookie"))) { |  | ||||||
|         LOGI << "Got the SAML authentication information successfully. " |  | ||||||
|              << "username: " << samlResult.value("username") |  | ||||||
|              << ", preloginCookie: " << samlResult.value("preloginCookie") |  | ||||||
|              << ", userAuthCookie: " << samlResult.value("userAuthCookie"); |  | ||||||
|  |  | ||||||
|         emit success(samlResult); |  | ||||||
|         accept(); |  | ||||||
|     } else { |  | ||||||
|         this->show(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SAMLLoginWindow::onLoadFinished() |  | ||||||
| { |  | ||||||
|      LOGI << "Load finished " << this->webView->page()->url().toString(); |  | ||||||
| } |  | ||||||
| @@ -1,35 +0,0 @@ | |||||||
| #ifndef SAMLLOGINWINDOW_H |  | ||||||
| #define SAMLLOGINWINDOW_H |  | ||||||
|  |  | ||||||
| #include "enhancedwebview.h" |  | ||||||
|  |  | ||||||
| #include <QDialog> |  | ||||||
| #include <QMap> |  | ||||||
| #include <QCloseEvent> |  | ||||||
|  |  | ||||||
| class SAMLLoginWindow : public QDialog |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
|  |  | ||||||
| public: |  | ||||||
|     explicit SAMLLoginWindow(QWidget *parent = nullptr); |  | ||||||
|     ~SAMLLoginWindow(); |  | ||||||
|  |  | ||||||
|     void login(const QString samlMethod, const QString samlRequest, const QString preloingUrl); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void success(QMap<QString, QString> samlResult); |  | ||||||
|     void fail(const QString msg); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onResponseReceived(QJsonObject params); |  | ||||||
|     void onLoadFinished(); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     EnhancedWebView *webView; |  | ||||||
|     QMap<QString, QString> samlResult; |  | ||||||
|  |  | ||||||
|     void closeEvent(QCloseEvent *event); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // SAMLLOGINWINDOW_H |  | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| TARGET = gpservice |  | ||||||
|  |  | ||||||
| QT += dbus |  | ||||||
| QT -= gui |  | ||||||
|  |  | ||||||
| CONFIG += c++11 console |  | ||||||
| CONFIG -= app_bundle |  | ||||||
|  |  | ||||||
| include(../singleapplication/singleapplication.pri) |  | ||||||
| DEFINES += QAPPLICATION_CLASS=QCoreApplication |  | ||||||
|  |  | ||||||
| # The following define makes your compiler emit warnings if you use |  | ||||||
| # any Qt feature that has been marked deprecated (the exact warnings |  | ||||||
| # depend on your compiler). Please consult the documentation of the |  | ||||||
| # deprecated API in order to know how to port your code away from it. |  | ||||||
| DEFINES += QT_DEPRECATED_WARNINGS |  | ||||||
|  |  | ||||||
| # You can also make your code fail to compile if it uses deprecated APIs. |  | ||||||
| # In order to do so, uncomment the following line. |  | ||||||
| # You can also select to disable deprecated APIs only up to a certain version of Qt. |  | ||||||
| #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0 |  | ||||||
|  |  | ||||||
| HEADERS += \ |  | ||||||
|     gpservice.h \ |  | ||||||
|     sigwatch.h |  | ||||||
|  |  | ||||||
| SOURCES += \ |  | ||||||
|         gpservice.cpp \ |  | ||||||
|         main.cpp \ |  | ||||||
|         sigwatch.cpp |  | ||||||
|  |  | ||||||
| DBUS_ADAPTORS += gpservice.xml |  | ||||||
|  |  | ||||||
| # Default rules for deployment. |  | ||||||
| target.path = /usr/bin |  | ||||||
| INSTALLS += target |  | ||||||
|  |  | ||||||
| DISTFILES += \ |  | ||||||
|     dbus/com.yuezk.qt.GPService.conf \ |  | ||||||
|     dbus/com.yuezk.qt.GPService.service \ |  | ||||||
|     systemd/gpservice.service |  | ||||||
|  |  | ||||||
| dbus_config.path = /usr/share/dbus-1/system.d/ |  | ||||||
| dbus_config.files = dbus/com.yuezk.qt.GPService.conf |  | ||||||
|  |  | ||||||
| dbus_service.path = /usr/share/dbus-1/system-services/ |  | ||||||
| dbus_service.files = dbus/com.yuezk.qt.GPService.service |  | ||||||
|  |  | ||||||
| systemd_service.path = /etc/systemd/system/ |  | ||||||
| systemd_service.files = systemd/gpservice.service |  | ||||||
|  |  | ||||||
| INSTALLS += dbus_config dbus_service systemd_service |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <!DOCTYPE busconfig PUBLIC |  | ||||||
| "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" |  | ||||||
| "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> |  | ||||||
| <busconfig> |  | ||||||
|         <policy user="root"> |  | ||||||
|                 <allow own="com.yuezk.qt.GPService"/> |  | ||||||
|         </policy> |  | ||||||
|  |  | ||||||
|         <policy context="default"> |  | ||||||
|                 <allow send_destination="com.yuezk.qt.GPService" |  | ||||||
|                         send_interface="com.yuezk.qt.GPService" |  | ||||||
|                         /> |  | ||||||
|                 <allow send_destination="com.yuezk.qt.GPService" |  | ||||||
|                         send_interface="org.freedesktop.DBus.Introspectable" |  | ||||||
|                         /> |  | ||||||
|         </policy> |  | ||||||
| </busconfig> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| [D-BUS Service] |  | ||||||
| Name=com.yuezk.qt.GPService |  | ||||||
| Exec=/usr/bin/gpservice |  | ||||||
| User=root |  | ||||||
| SystemdService=gpservice.service |  | ||||||
| @@ -1,131 +0,0 @@ | |||||||
| #include "gpservice.h" |  | ||||||
| #include "gpservice_adaptor.h" |  | ||||||
|  |  | ||||||
| #include <QFileInfo> |  | ||||||
| #include <QtDBus> |  | ||||||
| #include <QDateTime> |  | ||||||
| #include <QVariant> |  | ||||||
|  |  | ||||||
| GPService::GPService(QObject *parent) |  | ||||||
|     : QObject(parent) |  | ||||||
|     , openconnect(new QProcess) |  | ||||||
| { |  | ||||||
|     // Register the DBus service |  | ||||||
|     new GPServiceAdaptor(this); |  | ||||||
|     QDBusConnection dbus = QDBusConnection::systemBus(); |  | ||||||
|     dbus.registerObject("/", this); |  | ||||||
|     dbus.registerService("com.yuezk.qt.GPService"); |  | ||||||
|  |  | ||||||
|     // Setup the openconnect process |  | ||||||
|     QObject::connect(openconnect, &QProcess::started, this, &GPService::onProcessStarted); |  | ||||||
|     QObject::connect(openconnect, &QProcess::errorOccurred, this, &GPService::onProcessError); |  | ||||||
|     QObject::connect(openconnect, &QProcess::readyReadStandardOutput, this, &GPService::onProcessStdout); |  | ||||||
|     QObject::connect(openconnect, &QProcess::readyReadStandardError, this, &GPService::onProcessStderr); |  | ||||||
|     QObject::connect(openconnect, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &GPService::onProcessFinished); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GPService::~GPService() |  | ||||||
| { |  | ||||||
|     delete openconnect; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString GPService::findBinary() |  | ||||||
| { |  | ||||||
|     for (int i = 0; i < binaryPaths->length(); i++) { |  | ||||||
|         if (QFileInfo::exists(binaryPaths[i])) { |  | ||||||
|             return binaryPaths[i]; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::quit() |  | ||||||
| { |  | ||||||
|     if (openconnect->state() == QProcess::NotRunning) { |  | ||||||
|         exit(0); |  | ||||||
|     } else { |  | ||||||
|         aboutToQuit = true; |  | ||||||
|         openconnect->terminate(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::connect(QString server, QString username, QString passwd) |  | ||||||
| { |  | ||||||
|     if (vpnStatus != GPService::VpnNotConnected) { |  | ||||||
|         log("VPN status is: " + QVariant::fromValue(vpnStatus).toString()); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QString bin = findBinary(); |  | ||||||
|     if (bin == nullptr) { |  | ||||||
|         log("Could not found openconnect binary, make sure openconnect is installed, exiting."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QStringList args; |  | ||||||
|     args << QCoreApplication::arguments().mid(1) |  | ||||||
|      << "--protocol=gp" |  | ||||||
|      << "-u" << username |  | ||||||
|      << "-C" << passwd |  | ||||||
|      << server; |  | ||||||
|  |  | ||||||
|     openconnect->start(bin, args); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::disconnect() |  | ||||||
| { |  | ||||||
|     if (openconnect->state() != QProcess::NotRunning) { |  | ||||||
|         vpnStatus = GPService::VpnDisconnecting; |  | ||||||
|         openconnect->terminate(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int GPService::status() |  | ||||||
| { |  | ||||||
|     return vpnStatus; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::onProcessStarted() |  | ||||||
| { |  | ||||||
|     log("Openconnect started successfully, PID=" + QString::number(openconnect->processId())); |  | ||||||
|     vpnStatus = GPService::VpnConnecting; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::onProcessError(QProcess::ProcessError error) |  | ||||||
| { |  | ||||||
|     log("Error occurred: " + QVariant::fromValue(error).toString()); |  | ||||||
|     vpnStatus = GPService::VpnNotConnected; |  | ||||||
|     emit disconnected(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::onProcessStdout() |  | ||||||
| { |  | ||||||
|     QString output = openconnect->readAllStandardOutput(); |  | ||||||
|  |  | ||||||
|     log(output); |  | ||||||
|     if (output.indexOf("Connected as") >= 0) { |  | ||||||
|         vpnStatus = GPService::VpnConnected; |  | ||||||
|         emit connected(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::onProcessStderr() |  | ||||||
| { |  | ||||||
|     log(openconnect->readAllStandardError()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) |  | ||||||
| { |  | ||||||
|     log("Openconnect process exited with code " + QString::number(exitCode) + " and exit status " + QVariant::fromValue(exitStatus).toString()); |  | ||||||
|     vpnStatus = GPService::VpnNotConnected; |  | ||||||
|     emit disconnected(); |  | ||||||
|  |  | ||||||
|     if (aboutToQuit) { |  | ||||||
|         exit(0); |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void GPService::log(QString msg) |  | ||||||
| { |  | ||||||
|     emit logAvailable(msg); |  | ||||||
| } |  | ||||||
| @@ -1,58 +0,0 @@ | |||||||
| #ifndef GLOBALPROTECTSERVICE_H |  | ||||||
| #define GLOBALPROTECTSERVICE_H |  | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <QProcess> |  | ||||||
|  |  | ||||||
| static const QString binaryPaths[] { |  | ||||||
|     "/usr/local/bin/openconnect", |  | ||||||
|     "/usr/local/sbin/openconnect", |  | ||||||
|     "/usr/bin/openconnect", |  | ||||||
|     "/usr/sbin/openconnect", |  | ||||||
|     "/opt/bin/openconnect", |  | ||||||
|     "/opt/sbin/openconnect" |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class GPService : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
|     Q_CLASSINFO("D-Bus Interface", "com.yuezk.qt.GPService") |  | ||||||
| public: |  | ||||||
|     explicit GPService(QObject *parent = nullptr); |  | ||||||
|     ~GPService(); |  | ||||||
|  |  | ||||||
|     enum VpnStatus { |  | ||||||
|         VpnNotConnected, |  | ||||||
|         VpnConnecting, |  | ||||||
|         VpnConnected, |  | ||||||
|         VpnDisconnecting, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void connected(); |  | ||||||
|     void disconnected(); |  | ||||||
|     void logAvailable(QString log); |  | ||||||
|  |  | ||||||
| public slots: |  | ||||||
|     void connect(QString server, QString username, QString passwd); |  | ||||||
|     void disconnect(); |  | ||||||
|     int status(); |  | ||||||
|     void quit(); |  | ||||||
|  |  | ||||||
| private slots: |  | ||||||
|     void onProcessStarted(); |  | ||||||
|     void onProcessError(QProcess::ProcessError error); |  | ||||||
|     void onProcessStdout(); |  | ||||||
|     void onProcessStderr(); |  | ||||||
|     void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     QProcess *openconnect; |  | ||||||
|     bool aboutToQuit = false; |  | ||||||
|     int vpnStatus = GPService::VpnNotConnected; |  | ||||||
|  |  | ||||||
|     void log(QString msg); |  | ||||||
|     static QString findBinary(); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // GLOBALPROTECTSERVICE_H |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> |  | ||||||
| <node> |  | ||||||
|   <interface name="com.yuezk.qt.GPService"> |  | ||||||
|     <signal name="connected"> |  | ||||||
|     </signal> |  | ||||||
|     <signal name="disconnected"> |  | ||||||
|     </signal> |  | ||||||
|     <signal name="logAvailable"> |  | ||||||
|       <arg name="log" type="s" /> |  | ||||||
|     </signal> |  | ||||||
|     <method name="connect"> |  | ||||||
|       <arg name="server" type="s" direction="in"/> |  | ||||||
|       <arg name="username" type="s" direction="in"/> |  | ||||||
|       <arg name="passwd" type="s" direction="in"/> |  | ||||||
|     </method> |  | ||||||
|     <method name="disconnect"> |  | ||||||
|     </method> |  | ||||||
|     <method name="status"> |  | ||||||
|       <arg type="i" direction="out"/> |  | ||||||
|     </method> |  | ||||||
|   </interface> |  | ||||||
| </node> |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| #include <QtDBus> |  | ||||||
| #include "gpservice.h" |  | ||||||
| #include "singleapplication.h" |  | ||||||
| #include "sigwatch.h" |  | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) |  | ||||||
| { |  | ||||||
|     SingleApplication app(argc, argv); |  | ||||||
|  |  | ||||||
|     if (!QDBusConnection::systemBus().isConnected()) { |  | ||||||
|         qWarning("Cannot connect to the D-Bus session bus.\n" |  | ||||||
|                  "Please check your system settings and try again.\n"); |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     GPService service; |  | ||||||
|  |  | ||||||
|     UnixSignalWatcher sigwatch; |  | ||||||
|     sigwatch.watchForSignal(SIGINT); |  | ||||||
|     sigwatch.watchForSignal(SIGTERM); |  | ||||||
|     sigwatch.watchForSignal(SIGQUIT); |  | ||||||
|     sigwatch.watchForSignal(SIGHUP); |  | ||||||
|     QObject::connect(&sigwatch, &UnixSignalWatcher::unixSignal, &service, &GPService::quit); |  | ||||||
|  |  | ||||||
|     return app.exec(); |  | ||||||
| } |  | ||||||
| @@ -1,176 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Unix signal watcher for Qt. |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2014 Simon Knopp |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
|  * SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <QMap> |  | ||||||
| #include <QSocketNotifier> |  | ||||||
| #include <QDebug> |  | ||||||
| #include "sigwatch.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * \brief The UnixSignalWatcherPrivate class implements the back-end signal |  | ||||||
|  * handling for the UnixSignalWatcher. |  | ||||||
|  * |  | ||||||
|  * \see http://qt-project.org/doc/qt-5.0/qtdoc/unix-signals.html |  | ||||||
|  */ |  | ||||||
| class UnixSignalWatcherPrivate : public QObject |  | ||||||
| { |  | ||||||
|     UnixSignalWatcher * const q_ptr; |  | ||||||
|     Q_DECLARE_PUBLIC(UnixSignalWatcher) |  | ||||||
|  |  | ||||||
| public: |  | ||||||
|     UnixSignalWatcherPrivate(UnixSignalWatcher *q); |  | ||||||
|     ~UnixSignalWatcherPrivate(); |  | ||||||
|  |  | ||||||
|     void watchForSignal(int signal); |  | ||||||
|     static void signalHandler(int signal); |  | ||||||
|  |  | ||||||
|     void _q_onNotify(int sockfd); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     static int sockpair[2]; |  | ||||||
|     QSocketNotifier *notifier; |  | ||||||
|     QList<int> watchedSignals; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| int UnixSignalWatcherPrivate::sockpair[2]; |  | ||||||
|  |  | ||||||
| UnixSignalWatcherPrivate::UnixSignalWatcherPrivate(UnixSignalWatcher *q) : |  | ||||||
|     q_ptr(q) |  | ||||||
| { |  | ||||||
|     // Create socket pair |  | ||||||
|     if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair)) { |  | ||||||
|         qDebug() << "UnixSignalWatcher: socketpair: " << ::strerror(errno); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Create a notifier for the read end of the pair |  | ||||||
|     notifier = new QSocketNotifier(sockpair[1], QSocketNotifier::Read); |  | ||||||
|     QObject::connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_onNotify(int))); |  | ||||||
|     notifier->setEnabled(true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| UnixSignalWatcherPrivate::~UnixSignalWatcherPrivate() |  | ||||||
| { |  | ||||||
|     delete notifier; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Registers a handler for the given Unix \a signal. The handler will write to |  | ||||||
|  * a socket pair, the other end of which is connected to a QSocketNotifier. |  | ||||||
|  * This provides a way to break out of the asynchronous context from which the |  | ||||||
|  * signal handler is called and back into the Qt event loop. |  | ||||||
|  */ |  | ||||||
| void UnixSignalWatcherPrivate::watchForSignal(int signal) |  | ||||||
| { |  | ||||||
|     if (watchedSignals.contains(signal)) { |  | ||||||
|         qDebug() << "Already watching for signal" << signal; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Register a sigaction which will write to the socket pair |  | ||||||
|     struct sigaction sigact; |  | ||||||
|     sigact.sa_handler = UnixSignalWatcherPrivate::signalHandler; |  | ||||||
|     sigact.sa_flags = 0; |  | ||||||
|     ::sigemptyset(&sigact.sa_mask); |  | ||||||
|     sigact.sa_flags |= SA_RESTART; |  | ||||||
|     if (::sigaction(signal, &sigact, NULL)) { |  | ||||||
|         qDebug() << "UnixSignalWatcher: sigaction: " << ::strerror(errno); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     watchedSignals.append(signal); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Called when a Unix \a signal is received. Write to the socket to wake up the |  | ||||||
|  * QSocketNotifier. |  | ||||||
|  */ |  | ||||||
| void UnixSignalWatcherPrivate::signalHandler(int signal) |  | ||||||
| { |  | ||||||
|     ssize_t nBytes = ::write(sockpair[0], &signal, sizeof(signal)); |  | ||||||
|     Q_UNUSED(nBytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Called when the signal handler has written to the socket pair. Emits the Unix |  | ||||||
|  * signal as a Qt signal. |  | ||||||
|  */ |  | ||||||
| void UnixSignalWatcherPrivate::_q_onNotify(int sockfd) |  | ||||||
| { |  | ||||||
|     Q_Q(UnixSignalWatcher); |  | ||||||
|  |  | ||||||
|     int signal; |  | ||||||
|     ssize_t nBytes = ::read(sockfd, &signal, sizeof(signal)); |  | ||||||
|     Q_UNUSED(nBytes); |  | ||||||
|     qDebug() << "Caught signal:" << ::strsignal(signal); |  | ||||||
|     emit q->unixSignal(signal); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Create a new UnixSignalWatcher as a child of the given \a parent. |  | ||||||
|  */ |  | ||||||
| UnixSignalWatcher::UnixSignalWatcher(QObject *parent) : |  | ||||||
|     QObject(parent), |  | ||||||
|     d_ptr(new UnixSignalWatcherPrivate(this)) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Destroy this UnixSignalWatcher. |  | ||||||
|  */ |  | ||||||
| UnixSignalWatcher::~UnixSignalWatcher() |  | ||||||
| { |  | ||||||
|     delete d_ptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * Register a signal handler for the given \a signal. |  | ||||||
|  * |  | ||||||
|  * After calling this method you can \c connect() to the unixSignal() Qt signal |  | ||||||
|  * to be notified when the Unix signal is received. |  | ||||||
|  */ |  | ||||||
| void UnixSignalWatcher::watchForSignal(int signal) |  | ||||||
| { |  | ||||||
|     Q_D(UnixSignalWatcher); |  | ||||||
|     d->watchForSignal(signal); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * \fn void UnixSignalWatcher::unixSignal(int signal) |  | ||||||
|  * Emitted when the given Unix \a signal is received. |  | ||||||
|  * |  | ||||||
|  * watchForSignal() must be called for each Unix signal that you want to receive |  | ||||||
|  * via the unixSignal() Qt signal. If a watcher is watching multiple signals, |  | ||||||
|  * unixSignal() will be emitted whenever *any* of the watched Unix signals are |  | ||||||
|  * received, and the \a signal argument can be inspected to find out which one |  | ||||||
|  * was actually received. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "moc_sigwatch.cpp" |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Unix signal watcher for Qt. |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2014 Simon Knopp |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
|  * SOFTWARE. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef SIGWATCH_H |  | ||||||
| #define SIGWATCH_H |  | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <signal.h> |  | ||||||
|  |  | ||||||
| class UnixSignalWatcherPrivate; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  * \brief The UnixSignalWatcher class converts Unix signals to Qt signals. |  | ||||||
|  * |  | ||||||
|  * To watch for a given signal, e.g. \c SIGINT, call \c watchForSignal(SIGINT) |  | ||||||
|  * and \c connect() your handler to unixSignal(). |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| class UnixSignalWatcher : public QObject |  | ||||||
| { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit UnixSignalWatcher(QObject *parent = 0); |  | ||||||
|     ~UnixSignalWatcher(); |  | ||||||
|  |  | ||||||
|     void watchForSignal(int signal); |  | ||||||
|  |  | ||||||
| signals: |  | ||||||
|     void unixSignal(int signal); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     UnixSignalWatcherPrivate * const d_ptr; |  | ||||||
|     Q_DECLARE_PRIVATE(UnixSignalWatcher) |  | ||||||
|     Q_PRIVATE_SLOT(d_func(), void _q_onNotify(int)) |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // SIGWATCH_H |  | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| [Unit] |  | ||||||
| Description=GlobalProtect openconnect DBus service |  | ||||||
|  |  | ||||||
| [Service] |  | ||||||
| Environment="LANG=en_US.utf8" |  | ||||||
| Type=dbus |  | ||||||
| BusName=com.yuezk.qt.GPService |  | ||||||
| ExecStart=/usr/bin/gpservice |  | ||||||
|  |  | ||||||
| [Install] |  | ||||||
| WantedBy=multi-user.target |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| TEMPLATE = subdirs |  | ||||||
|  |  | ||||||
| SUBDIRS += \ |  | ||||||
|     GPClient \ |  | ||||||
|     GPService |  | ||||||
							
								
								
									
										674
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										674
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,674 +0,0 @@ | |||||||
|                     GNU GENERAL PUBLIC LICENSE |  | ||||||
|                        Version 3, 29 June 2007 |  | ||||||
|  |  | ||||||
|  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |  | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  | ||||||
|  of this license document, but changing it is not allowed. |  | ||||||
|  |  | ||||||
|                             Preamble |  | ||||||
|  |  | ||||||
|   The GNU General Public License is a free, copyleft license for |  | ||||||
| software and other kinds of works. |  | ||||||
|  |  | ||||||
|   The licenses for most software and other practical works are designed |  | ||||||
| to take away your freedom to share and change the works.  By contrast, |  | ||||||
| the GNU General Public License is intended to guarantee your freedom to |  | ||||||
| share and change all versions of a program--to make sure it remains free |  | ||||||
| software for all its users.  We, the Free Software Foundation, use the |  | ||||||
| GNU General Public License for most of our software; it applies also to |  | ||||||
| any other work released this way by its authors.  You can apply it to |  | ||||||
| your programs, too. |  | ||||||
|  |  | ||||||
|   When we speak of free software, we are referring to freedom, not |  | ||||||
| price.  Our General Public Licenses are designed to make sure that you |  | ||||||
| have the freedom to distribute copies of free software (and charge for |  | ||||||
| them if you wish), that you receive source code or can get it if you |  | ||||||
| want it, that you can change the software or use pieces of it in new |  | ||||||
| free programs, and that you know you can do these things. |  | ||||||
|  |  | ||||||
|   To protect your rights, we need to prevent others from denying you |  | ||||||
| these rights or asking you to surrender the rights.  Therefore, you have |  | ||||||
| certain responsibilities if you distribute copies of the software, or if |  | ||||||
| you modify it: responsibilities to respect the freedom of others. |  | ||||||
|  |  | ||||||
|   For example, if you distribute copies of such a program, whether |  | ||||||
| gratis or for a fee, you must pass on to the recipients the same |  | ||||||
| freedoms that you received.  You must make sure that they, too, receive |  | ||||||
| or can get the source code.  And you must show them these terms so they |  | ||||||
| know their rights. |  | ||||||
|  |  | ||||||
|   Developers that use the GNU GPL protect your rights with two steps: |  | ||||||
| (1) assert copyright on the software, and (2) offer you this License |  | ||||||
| giving you legal permission to copy, distribute and/or modify it. |  | ||||||
|  |  | ||||||
|   For the developers' and authors' protection, the GPL clearly explains |  | ||||||
| that there is no warranty for this free software.  For both users' and |  | ||||||
| authors' sake, the GPL requires that modified versions be marked as |  | ||||||
| changed, so that their problems will not be attributed erroneously to |  | ||||||
| authors of previous versions. |  | ||||||
|  |  | ||||||
|   Some devices are designed to deny users access to install or run |  | ||||||
| modified versions of the software inside them, although the manufacturer |  | ||||||
| can do so.  This is fundamentally incompatible with the aim of |  | ||||||
| protecting users' freedom to change the software.  The systematic |  | ||||||
| pattern of such abuse occurs in the area of products for individuals to |  | ||||||
| use, which is precisely where it is most unacceptable.  Therefore, we |  | ||||||
| have designed this version of the GPL to prohibit the practice for those |  | ||||||
| products.  If such problems arise substantially in other domains, we |  | ||||||
| stand ready to extend this provision to those domains in future versions |  | ||||||
| of the GPL, as needed to protect the freedom of users. |  | ||||||
|  |  | ||||||
|   Finally, every program is threatened constantly by software patents. |  | ||||||
| States should not allow patents to restrict development and use of |  | ||||||
| software on general-purpose computers, but in those that do, we wish to |  | ||||||
| avoid the special danger that patents applied to a free program could |  | ||||||
| make it effectively proprietary.  To prevent this, the GPL assures that |  | ||||||
| patents cannot be used to render the program non-free. |  | ||||||
|  |  | ||||||
|   The precise terms and conditions for copying, distribution and |  | ||||||
| modification follow. |  | ||||||
|  |  | ||||||
|                        TERMS AND CONDITIONS |  | ||||||
|  |  | ||||||
|   0. Definitions. |  | ||||||
|  |  | ||||||
|   "This License" refers to version 3 of the GNU General Public License. |  | ||||||
|  |  | ||||||
|   "Copyright" also means copyright-like laws that apply to other kinds of |  | ||||||
| works, such as semiconductor masks. |  | ||||||
|  |  | ||||||
|   "The Program" refers to any copyrightable work licensed under this |  | ||||||
| License.  Each licensee is addressed as "you".  "Licensees" and |  | ||||||
| "recipients" may be individuals or organizations. |  | ||||||
|  |  | ||||||
|   To "modify" a work means to copy from or adapt all or part of the work |  | ||||||
| in a fashion requiring copyright permission, other than the making of an |  | ||||||
| exact copy.  The resulting work is called a "modified version" of the |  | ||||||
| earlier work or a work "based on" the earlier work. |  | ||||||
|  |  | ||||||
|   A "covered work" means either the unmodified Program or a work based |  | ||||||
| on the Program. |  | ||||||
|  |  | ||||||
|   To "propagate" a work means to do anything with it that, without |  | ||||||
| permission, would make you directly or secondarily liable for |  | ||||||
| infringement under applicable copyright law, except executing it on a |  | ||||||
| computer or modifying a private copy.  Propagation includes copying, |  | ||||||
| distribution (with or without modification), making available to the |  | ||||||
| public, and in some countries other activities as well. |  | ||||||
|  |  | ||||||
|   To "convey" a work means any kind of propagation that enables other |  | ||||||
| parties to make or receive copies.  Mere interaction with a user through |  | ||||||
| a computer network, with no transfer of a copy, is not conveying. |  | ||||||
|  |  | ||||||
|   An interactive user interface displays "Appropriate Legal Notices" |  | ||||||
| to the extent that it includes a convenient and prominently visible |  | ||||||
| feature that (1) displays an appropriate copyright notice, and (2) |  | ||||||
| tells the user that there is no warranty for the work (except to the |  | ||||||
| extent that warranties are provided), that licensees may convey the |  | ||||||
| work under this License, and how to view a copy of this License.  If |  | ||||||
| the interface presents a list of user commands or options, such as a |  | ||||||
| menu, a prominent item in the list meets this criterion. |  | ||||||
|  |  | ||||||
|   1. Source Code. |  | ||||||
|  |  | ||||||
|   The "source code" for a work means the preferred form of the work |  | ||||||
| for making modifications to it.  "Object code" means any non-source |  | ||||||
| form of a work. |  | ||||||
|  |  | ||||||
|   A "Standard Interface" means an interface that either is an official |  | ||||||
| standard defined by a recognized standards body, or, in the case of |  | ||||||
| interfaces specified for a particular programming language, one that |  | ||||||
| is widely used among developers working in that language. |  | ||||||
|  |  | ||||||
|   The "System Libraries" of an executable work include anything, other |  | ||||||
| than the work as a whole, that (a) is included in the normal form of |  | ||||||
| packaging a Major Component, but which is not part of that Major |  | ||||||
| Component, and (b) serves only to enable use of the work with that |  | ||||||
| Major Component, or to implement a Standard Interface for which an |  | ||||||
| implementation is available to the public in source code form.  A |  | ||||||
| "Major Component", in this context, means a major essential component |  | ||||||
| (kernel, window system, and so on) of the specific operating system |  | ||||||
| (if any) on which the executable work runs, or a compiler used to |  | ||||||
| produce the work, or an object code interpreter used to run it. |  | ||||||
|  |  | ||||||
|   The "Corresponding Source" for a work in object code form means all |  | ||||||
| the source code needed to generate, install, and (for an executable |  | ||||||
| work) run the object code and to modify the work, including scripts to |  | ||||||
| control those activities.  However, it does not include the work's |  | ||||||
| System Libraries, or general-purpose tools or generally available free |  | ||||||
| programs which are used unmodified in performing those activities but |  | ||||||
| which are not part of the work.  For example, Corresponding Source |  | ||||||
| includes interface definition files associated with source files for |  | ||||||
| the work, and the source code for shared libraries and dynamically |  | ||||||
| linked subprograms that the work is specifically designed to require, |  | ||||||
| such as by intimate data communication or control flow between those |  | ||||||
| subprograms and other parts of the work. |  | ||||||
|  |  | ||||||
|   The Corresponding Source need not include anything that users |  | ||||||
| can regenerate automatically from other parts of the Corresponding |  | ||||||
| Source. |  | ||||||
|  |  | ||||||
|   The Corresponding Source for a work in source code form is that |  | ||||||
| same work. |  | ||||||
|  |  | ||||||
|   2. Basic Permissions. |  | ||||||
|  |  | ||||||
|   All rights granted under this License are granted for the term of |  | ||||||
| copyright on the Program, and are irrevocable provided the stated |  | ||||||
| conditions are met.  This License explicitly affirms your unlimited |  | ||||||
| permission to run the unmodified Program.  The output from running a |  | ||||||
| covered work is covered by this License only if the output, given its |  | ||||||
| content, constitutes a covered work.  This License acknowledges your |  | ||||||
| rights of fair use or other equivalent, as provided by copyright law. |  | ||||||
|  |  | ||||||
|   You may make, run and propagate covered works that you do not |  | ||||||
| convey, without conditions so long as your license otherwise remains |  | ||||||
| in force.  You may convey covered works to others for the sole purpose |  | ||||||
| of having them make modifications exclusively for you, or provide you |  | ||||||
| with facilities for running those works, provided that you comply with |  | ||||||
| the terms of this License in conveying all material for which you do |  | ||||||
| not control copyright.  Those thus making or running the covered works |  | ||||||
| for you must do so exclusively on your behalf, under your direction |  | ||||||
| and control, on terms that prohibit them from making any copies of |  | ||||||
| your copyrighted material outside their relationship with you. |  | ||||||
|  |  | ||||||
|   Conveying under any other circumstances is permitted solely under |  | ||||||
| the conditions stated below.  Sublicensing is not allowed; section 10 |  | ||||||
| makes it unnecessary. |  | ||||||
|  |  | ||||||
|   3. Protecting Users' Legal Rights From Anti-Circumvention Law. |  | ||||||
|  |  | ||||||
|   No covered work shall be deemed part of an effective technological |  | ||||||
| measure under any applicable law fulfilling obligations under article |  | ||||||
| 11 of the WIPO copyright treaty adopted on 20 December 1996, or |  | ||||||
| similar laws prohibiting or restricting circumvention of such |  | ||||||
| measures. |  | ||||||
|  |  | ||||||
|   When you convey a covered work, you waive any legal power to forbid |  | ||||||
| circumvention of technological measures to the extent such circumvention |  | ||||||
| is effected by exercising rights under this License with respect to |  | ||||||
| the covered work, and you disclaim any intention to limit operation or |  | ||||||
| modification of the work as a means of enforcing, against the work's |  | ||||||
| users, your or third parties' legal rights to forbid circumvention of |  | ||||||
| technological measures. |  | ||||||
|  |  | ||||||
|   4. Conveying Verbatim Copies. |  | ||||||
|  |  | ||||||
|   You may convey verbatim copies of the Program's source code as you |  | ||||||
| receive it, in any medium, provided that you conspicuously and |  | ||||||
| appropriately publish on each copy an appropriate copyright notice; |  | ||||||
| keep intact all notices stating that this License and any |  | ||||||
| non-permissive terms added in accord with section 7 apply to the code; |  | ||||||
| keep intact all notices of the absence of any warranty; and give all |  | ||||||
| recipients a copy of this License along with the Program. |  | ||||||
|  |  | ||||||
|   You may charge any price or no price for each copy that you convey, |  | ||||||
| and you may offer support or warranty protection for a fee. |  | ||||||
|  |  | ||||||
|   5. Conveying Modified Source Versions. |  | ||||||
|  |  | ||||||
|   You may convey a work based on the Program, or the modifications to |  | ||||||
| produce it from the Program, in the form of source code under the |  | ||||||
| terms of section 4, provided that you also meet all of these conditions: |  | ||||||
|  |  | ||||||
|     a) The work must carry prominent notices stating that you modified |  | ||||||
|     it, and giving a relevant date. |  | ||||||
|  |  | ||||||
|     b) The work must carry prominent notices stating that it is |  | ||||||
|     released under this License and any conditions added under section |  | ||||||
|     7.  This requirement modifies the requirement in section 4 to |  | ||||||
|     "keep intact all notices". |  | ||||||
|  |  | ||||||
|     c) You must license the entire work, as a whole, under this |  | ||||||
|     License to anyone who comes into possession of a copy.  This |  | ||||||
|     License will therefore apply, along with any applicable section 7 |  | ||||||
|     additional terms, to the whole of the work, and all its parts, |  | ||||||
|     regardless of how they are packaged.  This License gives no |  | ||||||
|     permission to license the work in any other way, but it does not |  | ||||||
|     invalidate such permission if you have separately received it. |  | ||||||
|  |  | ||||||
|     d) If the work has interactive user interfaces, each must display |  | ||||||
|     Appropriate Legal Notices; however, if the Program has interactive |  | ||||||
|     interfaces that do not display Appropriate Legal Notices, your |  | ||||||
|     work need not make them do so. |  | ||||||
|  |  | ||||||
|   A compilation of a covered work with other separate and independent |  | ||||||
| works, which are not by their nature extensions of the covered work, |  | ||||||
| and which are not combined with it such as to form a larger program, |  | ||||||
| in or on a volume of a storage or distribution medium, is called an |  | ||||||
| "aggregate" if the compilation and its resulting copyright are not |  | ||||||
| used to limit the access or legal rights of the compilation's users |  | ||||||
| beyond what the individual works permit.  Inclusion of a covered work |  | ||||||
| in an aggregate does not cause this License to apply to the other |  | ||||||
| parts of the aggregate. |  | ||||||
|  |  | ||||||
|   6. Conveying Non-Source Forms. |  | ||||||
|  |  | ||||||
|   You may convey a covered work in object code form under the terms |  | ||||||
| of sections 4 and 5, provided that you also convey the |  | ||||||
| machine-readable Corresponding Source under the terms of this License, |  | ||||||
| in one of these ways: |  | ||||||
|  |  | ||||||
|     a) Convey the object code in, or embodied in, a physical product |  | ||||||
|     (including a physical distribution medium), accompanied by the |  | ||||||
|     Corresponding Source fixed on a durable physical medium |  | ||||||
|     customarily used for software interchange. |  | ||||||
|  |  | ||||||
|     b) Convey the object code in, or embodied in, a physical product |  | ||||||
|     (including a physical distribution medium), accompanied by a |  | ||||||
|     written offer, valid for at least three years and valid for as |  | ||||||
|     long as you offer spare parts or customer support for that product |  | ||||||
|     model, to give anyone who possesses the object code either (1) a |  | ||||||
|     copy of the Corresponding Source for all the software in the |  | ||||||
|     product that is covered by this License, on a durable physical |  | ||||||
|     medium customarily used for software interchange, for a price no |  | ||||||
|     more than your reasonable cost of physically performing this |  | ||||||
|     conveying of source, or (2) access to copy the |  | ||||||
|     Corresponding Source from a network server at no charge. |  | ||||||
|  |  | ||||||
|     c) Convey individual copies of the object code with a copy of the |  | ||||||
|     written offer to provide the Corresponding Source.  This |  | ||||||
|     alternative is allowed only occasionally and noncommercially, and |  | ||||||
|     only if you received the object code with such an offer, in accord |  | ||||||
|     with subsection 6b. |  | ||||||
|  |  | ||||||
|     d) Convey the object code by offering access from a designated |  | ||||||
|     place (gratis or for a charge), and offer equivalent access to the |  | ||||||
|     Corresponding Source in the same way through the same place at no |  | ||||||
|     further charge.  You need not require recipients to copy the |  | ||||||
|     Corresponding Source along with the object code.  If the place to |  | ||||||
|     copy the object code is a network server, the Corresponding Source |  | ||||||
|     may be on a different server (operated by you or a third party) |  | ||||||
|     that supports equivalent copying facilities, provided you maintain |  | ||||||
|     clear directions next to the object code saying where to find the |  | ||||||
|     Corresponding Source.  Regardless of what server hosts the |  | ||||||
|     Corresponding Source, you remain obligated to ensure that it is |  | ||||||
|     available for as long as needed to satisfy these requirements. |  | ||||||
|  |  | ||||||
|     e) Convey the object code using peer-to-peer transmission, provided |  | ||||||
|     you inform other peers where the object code and Corresponding |  | ||||||
|     Source of the work are being offered to the general public at no |  | ||||||
|     charge under subsection 6d. |  | ||||||
|  |  | ||||||
|   A separable portion of the object code, whose source code is excluded |  | ||||||
| from the Corresponding Source as a System Library, need not be |  | ||||||
| included in conveying the object code work. |  | ||||||
|  |  | ||||||
|   A "User Product" is either (1) a "consumer product", which means any |  | ||||||
| tangible personal property which is normally used for personal, family, |  | ||||||
| or household purposes, or (2) anything designed or sold for incorporation |  | ||||||
| into a dwelling.  In determining whether a product is a consumer product, |  | ||||||
| doubtful cases shall be resolved in favor of coverage.  For a particular |  | ||||||
| product received by a particular user, "normally used" refers to a |  | ||||||
| typical or common use of that class of product, regardless of the status |  | ||||||
| of the particular user or of the way in which the particular user |  | ||||||
| actually uses, or expects or is expected to use, the product.  A product |  | ||||||
| is a consumer product regardless of whether the product has substantial |  | ||||||
| commercial, industrial or non-consumer uses, unless such uses represent |  | ||||||
| the only significant mode of use of the product. |  | ||||||
|  |  | ||||||
|   "Installation Information" for a User Product means any methods, |  | ||||||
| procedures, authorization keys, or other information required to install |  | ||||||
| and execute modified versions of a covered work in that User Product from |  | ||||||
| a modified version of its Corresponding Source.  The information must |  | ||||||
| suffice to ensure that the continued functioning of the modified object |  | ||||||
| code is in no case prevented or interfered with solely because |  | ||||||
| modification has been made. |  | ||||||
|  |  | ||||||
|   If you convey an object code work under this section in, or with, or |  | ||||||
| specifically for use in, a User Product, and the conveying occurs as |  | ||||||
| part of a transaction in which the right of possession and use of the |  | ||||||
| User Product is transferred to the recipient in perpetuity or for a |  | ||||||
| fixed term (regardless of how the transaction is characterized), the |  | ||||||
| Corresponding Source conveyed under this section must be accompanied |  | ||||||
| by the Installation Information.  But this requirement does not apply |  | ||||||
| if neither you nor any third party retains the ability to install |  | ||||||
| modified object code on the User Product (for example, the work has |  | ||||||
| been installed in ROM). |  | ||||||
|  |  | ||||||
|   The requirement to provide Installation Information does not include a |  | ||||||
| requirement to continue to provide support service, warranty, or updates |  | ||||||
| for a work that has been modified or installed by the recipient, or for |  | ||||||
| the User Product in which it has been modified or installed.  Access to a |  | ||||||
| network may be denied when the modification itself materially and |  | ||||||
| adversely affects the operation of the network or violates the rules and |  | ||||||
| protocols for communication across the network. |  | ||||||
|  |  | ||||||
|   Corresponding Source conveyed, and Installation Information provided, |  | ||||||
| in accord with this section must be in a format that is publicly |  | ||||||
| documented (and with an implementation available to the public in |  | ||||||
| source code form), and must require no special password or key for |  | ||||||
| unpacking, reading or copying. |  | ||||||
|  |  | ||||||
|   7. Additional Terms. |  | ||||||
|  |  | ||||||
|   "Additional permissions" are terms that supplement the terms of this |  | ||||||
| License by making exceptions from one or more of its conditions. |  | ||||||
| Additional permissions that are applicable to the entire Program shall |  | ||||||
| be treated as though they were included in this License, to the extent |  | ||||||
| that they are valid under applicable law.  If additional permissions |  | ||||||
| apply only to part of the Program, that part may be used separately |  | ||||||
| under those permissions, but the entire Program remains governed by |  | ||||||
| this License without regard to the additional permissions. |  | ||||||
|  |  | ||||||
|   When you convey a copy of a covered work, you may at your option |  | ||||||
| remove any additional permissions from that copy, or from any part of |  | ||||||
| it.  (Additional permissions may be written to require their own |  | ||||||
| removal in certain cases when you modify the work.)  You may place |  | ||||||
| additional permissions on material, added by you to a covered work, |  | ||||||
| for which you have or can give appropriate copyright permission. |  | ||||||
|  |  | ||||||
|   Notwithstanding any other provision of this License, for material you |  | ||||||
| add to a covered work, you may (if authorized by the copyright holders of |  | ||||||
| that material) supplement the terms of this License with terms: |  | ||||||
|  |  | ||||||
|     a) Disclaiming warranty or limiting liability differently from the |  | ||||||
|     terms of sections 15 and 16 of this License; or |  | ||||||
|  |  | ||||||
|     b) Requiring preservation of specified reasonable legal notices or |  | ||||||
|     author attributions in that material or in the Appropriate Legal |  | ||||||
|     Notices displayed by works containing it; or |  | ||||||
|  |  | ||||||
|     c) Prohibiting misrepresentation of the origin of that material, or |  | ||||||
|     requiring that modified versions of such material be marked in |  | ||||||
|     reasonable ways as different from the original version; or |  | ||||||
|  |  | ||||||
|     d) Limiting the use for publicity purposes of names of licensors or |  | ||||||
|     authors of the material; or |  | ||||||
|  |  | ||||||
|     e) Declining to grant rights under trademark law for use of some |  | ||||||
|     trade names, trademarks, or service marks; or |  | ||||||
|  |  | ||||||
|     f) Requiring indemnification of licensors and authors of that |  | ||||||
|     material by anyone who conveys the material (or modified versions of |  | ||||||
|     it) with contractual assumptions of liability to the recipient, for |  | ||||||
|     any liability that these contractual assumptions directly impose on |  | ||||||
|     those licensors and authors. |  | ||||||
|  |  | ||||||
|   All other non-permissive additional terms are considered "further |  | ||||||
| restrictions" within the meaning of section 10.  If the Program as you |  | ||||||
| received it, or any part of it, contains a notice stating that it is |  | ||||||
| governed by this License along with a term that is a further |  | ||||||
| restriction, you may remove that term.  If a license document contains |  | ||||||
| a further restriction but permits relicensing or conveying under this |  | ||||||
| License, you may add to a covered work material governed by the terms |  | ||||||
| of that license document, provided that the further restriction does |  | ||||||
| not survive such relicensing or conveying. |  | ||||||
|  |  | ||||||
|   If you add terms to a covered work in accord with this section, you |  | ||||||
| must place, in the relevant source files, a statement of the |  | ||||||
| additional terms that apply to those files, or a notice indicating |  | ||||||
| where to find the applicable terms. |  | ||||||
|  |  | ||||||
|   Additional terms, permissive or non-permissive, may be stated in the |  | ||||||
| form of a separately written license, or stated as exceptions; |  | ||||||
| the above requirements apply either way. |  | ||||||
|  |  | ||||||
|   8. Termination. |  | ||||||
|  |  | ||||||
|   You may not propagate or modify a covered work except as expressly |  | ||||||
| provided under this License.  Any attempt otherwise to propagate or |  | ||||||
| modify it is void, and will automatically terminate your rights under |  | ||||||
| this License (including any patent licenses granted under the third |  | ||||||
| paragraph of section 11). |  | ||||||
|  |  | ||||||
|   However, if you cease all violation of this License, then your |  | ||||||
| license from a particular copyright holder is reinstated (a) |  | ||||||
| provisionally, unless and until the copyright holder explicitly and |  | ||||||
| finally terminates your license, and (b) permanently, if the copyright |  | ||||||
| holder fails to notify you of the violation by some reasonable means |  | ||||||
| prior to 60 days after the cessation. |  | ||||||
|  |  | ||||||
|   Moreover, your license from a particular copyright holder is |  | ||||||
| reinstated permanently if the copyright holder notifies you of the |  | ||||||
| violation by some reasonable means, this is the first time you have |  | ||||||
| received notice of violation of this License (for any work) from that |  | ||||||
| copyright holder, and you cure the violation prior to 30 days after |  | ||||||
| your receipt of the notice. |  | ||||||
|  |  | ||||||
|   Termination of your rights under this section does not terminate the |  | ||||||
| licenses of parties who have received copies or rights from you under |  | ||||||
| this License.  If your rights have been terminated and not permanently |  | ||||||
| reinstated, you do not qualify to receive new licenses for the same |  | ||||||
| material under section 10. |  | ||||||
|  |  | ||||||
|   9. Acceptance Not Required for Having Copies. |  | ||||||
|  |  | ||||||
|   You are not required to accept this License in order to receive or |  | ||||||
| run a copy of the Program.  Ancillary propagation of a covered work |  | ||||||
| occurring solely as a consequence of using peer-to-peer transmission |  | ||||||
| to receive a copy likewise does not require acceptance.  However, |  | ||||||
| nothing other than this License grants you permission to propagate or |  | ||||||
| modify any covered work.  These actions infringe copyright if you do |  | ||||||
| not accept this License.  Therefore, by modifying or propagating a |  | ||||||
| covered work, you indicate your acceptance of this License to do so. |  | ||||||
|  |  | ||||||
|   10. Automatic Licensing of Downstream Recipients. |  | ||||||
|  |  | ||||||
|   Each time you convey a covered work, the recipient automatically |  | ||||||
| receives a license from the original licensors, to run, modify and |  | ||||||
| propagate that work, subject to this License.  You are not responsible |  | ||||||
| for enforcing compliance by third parties with this License. |  | ||||||
|  |  | ||||||
|   An "entity transaction" is a transaction transferring control of an |  | ||||||
| organization, or substantially all assets of one, or subdividing an |  | ||||||
| organization, or merging organizations.  If propagation of a covered |  | ||||||
| work results from an entity transaction, each party to that |  | ||||||
| transaction who receives a copy of the work also receives whatever |  | ||||||
| licenses to the work the party's predecessor in interest had or could |  | ||||||
| give under the previous paragraph, plus a right to possession of the |  | ||||||
| Corresponding Source of the work from the predecessor in interest, if |  | ||||||
| the predecessor has it or can get it with reasonable efforts. |  | ||||||
|  |  | ||||||
|   You may not impose any further restrictions on the exercise of the |  | ||||||
| rights granted or affirmed under this License.  For example, you may |  | ||||||
| not impose a license fee, royalty, or other charge for exercise of |  | ||||||
| rights granted under this License, and you may not initiate litigation |  | ||||||
| (including a cross-claim or counterclaim in a lawsuit) alleging that |  | ||||||
| any patent claim is infringed by making, using, selling, offering for |  | ||||||
| sale, or importing the Program or any portion of it. |  | ||||||
|  |  | ||||||
|   11. Patents. |  | ||||||
|  |  | ||||||
|   A "contributor" is a copyright holder who authorizes use under this |  | ||||||
| License of the Program or a work on which the Program is based.  The |  | ||||||
| work thus licensed is called the contributor's "contributor version". |  | ||||||
|  |  | ||||||
|   A contributor's "essential patent claims" are all patent claims |  | ||||||
| owned or controlled by the contributor, whether already acquired or |  | ||||||
| hereafter acquired, that would be infringed by some manner, permitted |  | ||||||
| by this License, of making, using, or selling its contributor version, |  | ||||||
| but do not include claims that would be infringed only as a |  | ||||||
| consequence of further modification of the contributor version.  For |  | ||||||
| purposes of this definition, "control" includes the right to grant |  | ||||||
| patent sublicenses in a manner consistent with the requirements of |  | ||||||
| this License. |  | ||||||
|  |  | ||||||
|   Each contributor grants you a non-exclusive, worldwide, royalty-free |  | ||||||
| patent license under the contributor's essential patent claims, to |  | ||||||
| make, use, sell, offer for sale, import and otherwise run, modify and |  | ||||||
| propagate the contents of its contributor version. |  | ||||||
|  |  | ||||||
|   In the following three paragraphs, a "patent license" is any express |  | ||||||
| agreement or commitment, however denominated, not to enforce a patent |  | ||||||
| (such as an express permission to practice a patent or covenant not to |  | ||||||
| sue for patent infringement).  To "grant" such a patent license to a |  | ||||||
| party means to make such an agreement or commitment not to enforce a |  | ||||||
| patent against the party. |  | ||||||
|  |  | ||||||
|   If you convey a covered work, knowingly relying on a patent license, |  | ||||||
| and the Corresponding Source of the work is not available for anyone |  | ||||||
| to copy, free of charge and under the terms of this License, through a |  | ||||||
| publicly available network server or other readily accessible means, |  | ||||||
| then you must either (1) cause the Corresponding Source to be so |  | ||||||
| available, or (2) arrange to deprive yourself of the benefit of the |  | ||||||
| patent license for this particular work, or (3) arrange, in a manner |  | ||||||
| consistent with the requirements of this License, to extend the patent |  | ||||||
| license to downstream recipients.  "Knowingly relying" means you have |  | ||||||
| actual knowledge that, but for the patent license, your conveying the |  | ||||||
| covered work in a country, or your recipient's use of the covered work |  | ||||||
| in a country, would infringe one or more identifiable patents in that |  | ||||||
| country that you have reason to believe are valid. |  | ||||||
|  |  | ||||||
|   If, pursuant to or in connection with a single transaction or |  | ||||||
| arrangement, you convey, or propagate by procuring conveyance of, a |  | ||||||
| covered work, and grant a patent license to some of the parties |  | ||||||
| receiving the covered work authorizing them to use, propagate, modify |  | ||||||
| or convey a specific copy of the covered work, then the patent license |  | ||||||
| you grant is automatically extended to all recipients of the covered |  | ||||||
| work and works based on it. |  | ||||||
|  |  | ||||||
|   A patent license is "discriminatory" if it does not include within |  | ||||||
| the scope of its coverage, prohibits the exercise of, or is |  | ||||||
| conditioned on the non-exercise of one or more of the rights that are |  | ||||||
| specifically granted under this License.  You may not convey a covered |  | ||||||
| work if you are a party to an arrangement with a third party that is |  | ||||||
| in the business of distributing software, under which you make payment |  | ||||||
| to the third party based on the extent of your activity of conveying |  | ||||||
| the work, and under which the third party grants, to any of the |  | ||||||
| parties who would receive the covered work from you, a discriminatory |  | ||||||
| patent license (a) in connection with copies of the covered work |  | ||||||
| conveyed by you (or copies made from those copies), or (b) primarily |  | ||||||
| for and in connection with specific products or compilations that |  | ||||||
| contain the covered work, unless you entered into that arrangement, |  | ||||||
| or that patent license was granted, prior to 28 March 2007. |  | ||||||
|  |  | ||||||
|   Nothing in this License shall be construed as excluding or limiting |  | ||||||
| any implied license or other defenses to infringement that may |  | ||||||
| otherwise be available to you under applicable patent law. |  | ||||||
|  |  | ||||||
|   12. No Surrender of Others' Freedom. |  | ||||||
|  |  | ||||||
|   If conditions are imposed on you (whether by court order, agreement or |  | ||||||
| otherwise) that contradict the conditions of this License, they do not |  | ||||||
| excuse you from the conditions of this License.  If you cannot convey a |  | ||||||
| covered work so as to satisfy simultaneously your obligations under this |  | ||||||
| License and any other pertinent obligations, then as a consequence you may |  | ||||||
| not convey it at all.  For example, if you agree to terms that obligate you |  | ||||||
| to collect a royalty for further conveying from those to whom you convey |  | ||||||
| the Program, the only way you could satisfy both those terms and this |  | ||||||
| License would be to refrain entirely from conveying the Program. |  | ||||||
|  |  | ||||||
|   13. Use with the GNU Affero General Public License. |  | ||||||
|  |  | ||||||
|   Notwithstanding any other provision of this License, you have |  | ||||||
| permission to link or combine any covered work with a work licensed |  | ||||||
| under version 3 of the GNU Affero General Public License into a single |  | ||||||
| combined work, and to convey the resulting work.  The terms of this |  | ||||||
| License will continue to apply to the part which is the covered work, |  | ||||||
| but the special requirements of the GNU Affero General Public License, |  | ||||||
| section 13, concerning interaction through a network will apply to the |  | ||||||
| combination as such. |  | ||||||
|  |  | ||||||
|   14. Revised Versions of this License. |  | ||||||
|  |  | ||||||
|   The Free Software Foundation may publish revised and/or new versions of |  | ||||||
| the GNU General Public License from time to time.  Such new versions will |  | ||||||
| be similar in spirit to the present version, but may differ in detail to |  | ||||||
| address new problems or concerns. |  | ||||||
|  |  | ||||||
|   Each version is given a distinguishing version number.  If the |  | ||||||
| Program specifies that a certain numbered version of the GNU General |  | ||||||
| Public License "or any later version" applies to it, you have the |  | ||||||
| option of following the terms and conditions either of that numbered |  | ||||||
| version or of any later version published by the Free Software |  | ||||||
| Foundation.  If the Program does not specify a version number of the |  | ||||||
| GNU General Public License, you may choose any version ever published |  | ||||||
| by the Free Software Foundation. |  | ||||||
|  |  | ||||||
|   If the Program specifies that a proxy can decide which future |  | ||||||
| versions of the GNU General Public License can be used, that proxy's |  | ||||||
| public statement of acceptance of a version permanently authorizes you |  | ||||||
| to choose that version for the Program. |  | ||||||
|  |  | ||||||
|   Later license versions may give you additional or different |  | ||||||
| permissions.  However, no additional obligations are imposed on any |  | ||||||
| author or copyright holder as a result of your choosing to follow a |  | ||||||
| later version. |  | ||||||
|  |  | ||||||
|   15. Disclaimer of Warranty. |  | ||||||
|  |  | ||||||
|   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |  | ||||||
| APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |  | ||||||
| HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |  | ||||||
| OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |  | ||||||
| THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |  | ||||||
| PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |  | ||||||
| IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |  | ||||||
| ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |  | ||||||
|  |  | ||||||
|   16. Limitation of Liability. |  | ||||||
|  |  | ||||||
|   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |  | ||||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |  | ||||||
| THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |  | ||||||
| GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |  | ||||||
| USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |  | ||||||
| DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |  | ||||||
| PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |  | ||||||
| EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |  | ||||||
| SUCH DAMAGES. |  | ||||||
|  |  | ||||||
|   17. Interpretation of Sections 15 and 16. |  | ||||||
|  |  | ||||||
|   If the disclaimer of warranty and limitation of liability provided |  | ||||||
| above cannot be given local legal effect according to their terms, |  | ||||||
| reviewing courts shall apply local law that most closely approximates |  | ||||||
| an absolute waiver of all civil liability in connection with the |  | ||||||
| Program, unless a warranty or assumption of liability accompanies a |  | ||||||
| copy of the Program in return for a fee. |  | ||||||
|  |  | ||||||
|                      END OF TERMS AND CONDITIONS |  | ||||||
|  |  | ||||||
|             How to Apply These Terms to Your New Programs |  | ||||||
|  |  | ||||||
|   If you develop a new program, and you want it to be of the greatest |  | ||||||
| possible use to the public, the best way to achieve this is to make it |  | ||||||
| free software which everyone can redistribute and change under these terms. |  | ||||||
|  |  | ||||||
|   To do so, attach the following notices to the program.  It is safest |  | ||||||
| to attach them to the start of each source file to most effectively |  | ||||||
| state the exclusion of warranty; and each file should have at least |  | ||||||
| the "copyright" line and a pointer to where the full notice is found. |  | ||||||
|  |  | ||||||
|     <one line to give the program's name and a brief idea of what it does.> |  | ||||||
|     Copyright (C) <year>  <name of author> |  | ||||||
|  |  | ||||||
|     This program is free software: you can redistribute it and/or modify |  | ||||||
|     it under the terms of the GNU General Public License as published by |  | ||||||
|     the Free Software Foundation, either version 3 of the License, or |  | ||||||
|     (at your option) any later version. |  | ||||||
|  |  | ||||||
|     This program is distributed in the hope that it will be useful, |  | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|     GNU General Public License for more details. |  | ||||||
|  |  | ||||||
|     You should have received a copy of the GNU General Public License |  | ||||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
| Also add information on how to contact you by electronic and paper mail. |  | ||||||
|  |  | ||||||
|   If the program does terminal interaction, make it output a short |  | ||||||
| notice like this when it starts in an interactive mode: |  | ||||||
|  |  | ||||||
|     <program>  Copyright (C) <year>  <name of author> |  | ||||||
|     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |  | ||||||
|     This is free software, and you are welcome to redistribute it |  | ||||||
|     under certain conditions; type `show c' for details. |  | ||||||
|  |  | ||||||
| The hypothetical commands `show w' and `show c' should show the appropriate |  | ||||||
| parts of the General Public License.  Of course, your program's commands |  | ||||||
| might be different; for a GUI interface, you would use an "about box". |  | ||||||
|  |  | ||||||
|   You should also get your employer (if you work as a programmer) or school, |  | ||||||
| if any, to sign a "copyright disclaimer" for the program, if necessary. |  | ||||||
| For more information on this, and how to apply and follow the GNU GPL, see |  | ||||||
| <https://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
|   The GNU General Public License does not permit incorporating your program |  | ||||||
| into proprietary programs.  If your program is a subroutine library, you |  | ||||||
| may consider it more useful to permit linking proprietary applications with |  | ||||||
| the library.  If this is what you want to do, use the GNU Lesser General |  | ||||||
| Public License instead of this License.  But first, please read |  | ||||||
| <https://www.gnu.org/licenses/why-not-lgpl.html>. |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| # Maintainer: Keinv Yue <yuezk001@gmail.com> |  | ||||||
|  |  | ||||||
| pkgname=globalprotect-openconnect |  | ||||||
| _gitname=GlobalProtect-openconnect |  | ||||||
| pkgver={PKG_VERSION} |  | ||||||
| pkgrel=1 |  | ||||||
| pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode." |  | ||||||
| arch=(x86_64 aarch64) |  | ||||||
| url="https://github.com/yuezk/${_gitname}" |  | ||||||
| license=('GPL3') |  | ||||||
| depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets) |  | ||||||
| makedepends=() |  | ||||||
| source=( |  | ||||||
|     "${_gitname}-${pkgver}.tar.gz::${url}/archive/v${pkgver}.tar.gz" |  | ||||||
|     "https://github.com/itay-grudev/SingleApplication/archive/v3.0.19.tar.gz" |  | ||||||
|     "https://github.com/SergiusTheBest/plog/archive/1.1.5.tar.gz" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| sha256sums=( |  | ||||||
|     '{SOURCE_SHA}' |  | ||||||
|     '9405fd259288b2a862e91e5135bccee936f0438e1b32c13603277132309d15e0' |  | ||||||
|     '6c80b4701183d2415bec927e1f5ca9b1761b3b5c65d3e09fb29c743e016d5609' |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| prepare() { |  | ||||||
|     mv "$srcdir/SingleApplication-3.0.19" -T "$srcdir/${_gitname}-${pkgver}/singleapplication" |  | ||||||
|     mv "$srcdir/plog-1.1.5" -T "$srcdir/${_gitname}-${pkgver}/plog" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| build() { |  | ||||||
|     cd "$srcdir/${_gitname}-${pkgver}" |  | ||||||
|     qmake CONFIG+=release "${srcdir}/${_gitname}-${pkgver}/GlobalProtect-openconnect.pro" |  | ||||||
|     make |  | ||||||
| } |  | ||||||
|  |  | ||||||
| package() { |  | ||||||
|     cd "$srcdir/${_gitname}-${pkgver}" |  | ||||||
|     make INSTALL_ROOT="$pkgdir/" install |  | ||||||
| } |  | ||||||
							
								
								
									
										121
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,119 +1,42 @@ | |||||||
| # GlobalProtect-openconnect |  | ||||||
| A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode, inspired by [gp-saml-gui](https://github.com/dlenski/gp-saml-gui). |  | ||||||
|  |  | ||||||
| <p align="center"> | <p align="center"> | ||||||
|   <img src="screenshot.png"> |   <img width="300" src="https://github.com/yuezk/GlobalProtect-openconnect/assets/3297602/9242df9c-217d-42ab-8c21-8f9f69cd4eb5"> | ||||||
| </p> | </p> | ||||||
|  |  | ||||||
| ## Features | ## Development | ||||||
|  |  | ||||||
| - Similar user experience as the official client in macOS. | ### Dependencies | ||||||
| - Supports both SAML and non-SAML authentication modes. |  | ||||||
| - Supports automatically selecting the preferred gateway from the multiple gateways. |  | ||||||
| - Supports switching gateway from the system tray menu manually. |  | ||||||
|  |  | ||||||
| ## Prerequisites | The following packages will be required to build depending on your environment: | ||||||
|  |  | ||||||
| - Openconnect v8.x | - [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) | ||||||
| - Qt5, qt5-webengine, qt5-websockets | - [pnpm](https://pnpm.io/installation) | ||||||
|  | - openconnect-devel (containing `openconnect.h`): `sudo apt install libopenconnect-dev` or `sudo yum install openconnect-devel` | ||||||
|  | - GDK dependencies `sudo apt install libgdk3.0-cil-dev libcairo2-dev libsoup2.4-dev libgdk-pixbuf-2.0-dev libjavascriptcoregtk-4.0-dev libatk1.0-dev libpango1.0-dev libwebkit2gtk-4.0-dev` | ||||||
|  |  | ||||||
| ### Ubuntu | ### Build the service | ||||||
| 1. Install openconnect v8.x |  | ||||||
|  |  | ||||||
| ```sh | ```sh | ||||||
|     sudo apt install openconnect | # Build the client first | ||||||
|     openconnect --version | cargo build -p gpclient | ||||||
|  |  | ||||||
|  | # Build the service | ||||||
|  | cargo build -p gpservice | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|    For Ubuntu 18.04 you might need to [build the latest openconnect from source code](https://gist.github.com/yuezk/ab9a4b87a9fa0182bdb2df41fab5f613). | ### Start the service | ||||||
|     |  | ||||||
| 2. Install the Qt dependencies |  | ||||||
|  |  | ||||||
|     For Ubuntu 20, this should work. |  | ||||||
|  |  | ||||||
| ```sh | ```sh | ||||||
|     sudo apt install qtbase5-dev libqt5websockets5-dev qtwebengine5-dev qttools5-dev debhelper | sudo ./target/debug/gpservice | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|     For Ubuntu 21, you need to install the base pieces separately as QT5 is the default. | ### Start the GUI | ||||||
|  |  | ||||||
| ```sh | ```sh | ||||||
|     sudo apt install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5-dev qtwebengine5-dev qttools5-dev debhelper | cd gpgui | ||||||
|  | pnpm install | ||||||
|  | pnpm tauri dev | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### OpenSUSE | ### Open the DevTools | ||||||
| Install the Qt dependencies |  | ||||||
|  |  | ||||||
| ```sh | Right-click on the GUI window and select "Inspect Element". | ||||||
| sudo zypper install libqt5-qtbase-devel libqt5-qtwebsockets-devel libqt5-qtwebengine-devel |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Install |  | ||||||
|  |  | ||||||
| ### Install from AUR (Arch/Manjaro) |  | ||||||
|  |  | ||||||
| Install [globalprotect-openconnect](https://aur.archlinux.org/packages/globalprotect-openconnect/). |  | ||||||
|  |  | ||||||
| ### Build from source code |  | ||||||
|  |  | ||||||
| ```sh |  | ||||||
| git clone https://github.com/yuezk/GlobalProtect-openconnect.git |  | ||||||
| cd GlobalProtect-openconnect |  | ||||||
| git submodule update --init |  | ||||||
|  |  | ||||||
| # qmake or qmake-qt5 |  | ||||||
| qmake CONFIG+=release |  | ||||||
| make |  | ||||||
| sudo make install |  | ||||||
| ``` |  | ||||||
| Open `GlobalProtect VPN` in the application dashboard. |  | ||||||
|  |  | ||||||
| ### Debian package |  | ||||||
|  |  | ||||||
| Relatively manual process for now: |  | ||||||
|  |  | ||||||
| * Clone the source tree |  | ||||||
|  |  | ||||||
|   ``` |  | ||||||
|   git clone https://github.com/yuezk/GlobalProtect-openconnect.git |  | ||||||
|   cd GlobalProtect-openconnect |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| * Install git-archive-all using the pip. Remember to adjust the version numbers etc. |  | ||||||
|  |  | ||||||
|   ``` |  | ||||||
|   pip install git-archive-all |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| * Next create an upstream source tree using git archive. |  | ||||||
|  |  | ||||||
|   ``` |  | ||||||
|   git-archive-all --force-submodules --prefix=globalprotect-openconnect-1.3.0/ ../globalprotect-openconnect_1.3.0.orig.tar.gz |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| * Finally extract the source tree, build the debian package, and install it. |  | ||||||
|  |  | ||||||
|   ``` |  | ||||||
|   cd .. |  | ||||||
|   tar -xzvf globalprotect-openconnect_1.3.0.orig.tar.gz |  | ||||||
|   cd globalprotect-openconnect-1.3.0 |  | ||||||
|   fakeroot dpkg-buildpackage -uc -us -sa 2>&1 | tee ../build.log |  | ||||||
|   sudo dpkg -i globalprotect-openconnect_1.3.0-1ppa1_amd64.deb |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| ### NixOS |  | ||||||
|   In `configuration.nix`: |  | ||||||
|  |  | ||||||
|   ``` |  | ||||||
|   services.globalprotect = { |  | ||||||
|     enable = true; |  | ||||||
|     # if you need a Host Integrity Protection report |  | ||||||
|     csdWrapper = "${pkgs.openconnect}/libexec/openconnect/hipreport.sh"; |  | ||||||
|   }; |  | ||||||
|    |  | ||||||
|   environment.systemPackages = [ globalprotect-openconnect ]; |  | ||||||
|   ``` |  | ||||||
|    |  | ||||||
|  |  | ||||||
| ## [License](./LICENSE) |  | ||||||
| GPLv3 |  | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								com.yuezk.gp.policy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								com.yuezk.gp.policy
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> | ||||||
|  | <policyconfig> | ||||||
|  |   <vendor>The GlobalProtect-openconnect Project</vendor> | ||||||
|  |   <vendor_url>https://github.com/yuezk/GlobalProtect-openconnect</vendor_url> | ||||||
|  |   <icon_name>gpgui</icon_name> | ||||||
|  |   <action id="com.yuezk.gp.update-gpconf"> | ||||||
|  |     <description>Update the /etc/gpservice/gp.conf</description> | ||||||
|  |     <message>Authentication is required to update the GlobalProtect service configuration</message> | ||||||
|  |     <defaults> | ||||||
|  |       <allow_any>auth_admin</allow_any> | ||||||
|  |       <allow_inactive>auth_admin</allow_inactive> | ||||||
|  |       <allow_active>auth_admin</allow_active> | ||||||
|  |     </defaults> | ||||||
|  |     <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/tee</annotate> | ||||||
|  |     <annotate key="org.freedesktop.policykit.exec.argv1">/etc/gpservice/gp.conf</annotate> | ||||||
|  |     <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate> | ||||||
|  |   </action> | ||||||
|  | </policyconfig> | ||||||
							
								
								
									
										5
									
								
								debian/README.Debian
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/README.Debian
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +0,0 @@ | |||||||
| globalprotect-openconnect for Debian |  | ||||||
|  |  | ||||||
| Added debian packaging  |  | ||||||
|  |  | ||||||
|  -- Amit Joshi <>  Fri, 29 May 2020 21:52:59 -0400 |  | ||||||
							
								
								
									
										47
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,47 +0,0 @@ | |||||||
| globalprotect-openconnect (1.3.0-1ppa1) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Bump version to 1.3.0 |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Thu, 09 Jul 2020 10:13:46 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.7-1ppa1) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Update dependencies |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Tue, 09 Jun 2020 22:13:46 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.6-1ppa1) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Add qt5-default dependency |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Tue, 09 Jun 2020 22:05:57 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.5-1ppa1) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Update version |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Mon, 08 Jun 2020 23:24:06 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.4-1ppa5) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Update dependencies |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Mon, 08 Jun 2020 23:13:44 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.4-1ppa2) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Update dependencies |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Mon, 08 Jun 2020 22:39:07 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.4-1ppa1) bionic; urgency=medium |  | ||||||
|  |  | ||||||
|   * Initial release. |  | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 07 Jun 2020 19:00:25 +0800 |  | ||||||
|  |  | ||||||
| globalprotect-openconnect (1.2.4-1) UNRELEASED; urgency=low |  | ||||||
|  |  | ||||||
|   * Initial release of debian package |  | ||||||
|  |  | ||||||
|  -- Amit Joshi <>  Fri, 29 May 2020 21:52:59 -0400 |  | ||||||
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| 11 |  | ||||||
							
								
								
									
										13
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| Source: globalprotect-openconnect |  | ||||||
| Section: net |  | ||||||
| Priority: optional |  | ||||||
| Maintainer: Kevin Yue <k3vinyue@gmail.com> |  | ||||||
| Build-Depends: debhelper (>=11~), qtbase5-dev, qttools5-dev (>=5.9), libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9) |  | ||||||
| Standards-Version: 4.1.4 |  | ||||||
| Homepage: https://github.com/yuezk/GlobalProtect-openconnect |  | ||||||
|  |  | ||||||
| Package: globalprotect-openconnect |  | ||||||
| Architecture: any |  | ||||||
| Multi-Arch: foreign |  | ||||||
| Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9) |  | ||||||
| Description: A GlobalProtect VPN client (GUI) based on OpenConnect. |  | ||||||
							
								
								
									
										982
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										982
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -1,982 +0,0 @@ | |||||||
| Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |  | ||||||
| Upstream-Name: globalprotect-openconnect |  | ||||||
| Source: https://github.com/yuezk/GlobalProtect-openconnect |  | ||||||
| # |  | ||||||
| # Please double check copyright with the licensecheck(1) command. |  | ||||||
|  |  | ||||||
| Files:     .gitignore |  | ||||||
|            .gitmodules |  | ||||||
|            GPClient/GPClient.pro |  | ||||||
|            GPClient/Makefile |  | ||||||
|            GPClient/cdpcommand.cpp |  | ||||||
|            GPClient/cdpcommand.h |  | ||||||
|            GPClient/cdpcommand.o |  | ||||||
|            GPClient/cdpcommandmanager.cpp |  | ||||||
|            GPClient/cdpcommandmanager.h |  | ||||||
|            GPClient/cdpcommandmanager.o |  | ||||||
|            GPClient/com.yuezk.qt.gpclient.desktop |  | ||||||
|            GPClient/connected.png |  | ||||||
|            GPClient/enhancedwebview.cpp |  | ||||||
|            GPClient/enhancedwebview.h |  | ||||||
|            GPClient/enhancedwebview.o |  | ||||||
|            GPClient/gatewayauthenticator.cpp |  | ||||||
|            GPClient/gatewayauthenticator.h |  | ||||||
|            GPClient/gatewayauthenticator.o |  | ||||||
|            GPClient/gpclient |  | ||||||
|            GPClient/gpclient.cpp |  | ||||||
|            GPClient/gpclient.h |  | ||||||
|            GPClient/gpclient.o |  | ||||||
|            GPClient/gpgateway.cpp |  | ||||||
|            GPClient/gpgateway.h |  | ||||||
|            GPClient/gpgateway.o |  | ||||||
|            GPClient/gphelper.cpp |  | ||||||
|            GPClient/gphelper.h |  | ||||||
|            GPClient/gphelper.o |  | ||||||
|            GPClient/gpservice_interface.cpp |  | ||||||
|            GPClient/gpservice_interface.h |  | ||||||
|            GPClient/gpservice_interface.o |  | ||||||
|            GPClient/loginparams.cpp |  | ||||||
|            GPClient/loginparams.h |  | ||||||
|            GPClient/loginparams.o |  | ||||||
|            GPClient/main.cpp |  | ||||||
|            GPClient/main.o |  | ||||||
|            GPClient/moc_cdpcommand.cpp |  | ||||||
|            GPClient/moc_cdpcommand.o |  | ||||||
|            GPClient/moc_cdpcommandmanager.cpp |  | ||||||
|            GPClient/moc_cdpcommandmanager.o |  | ||||||
|            GPClient/moc_enhancedwebview.cpp |  | ||||||
|            GPClient/moc_enhancedwebview.o |  | ||||||
|            GPClient/moc_gatewayauthenticator.cpp |  | ||||||
|            GPClient/moc_gatewayauthenticator.o |  | ||||||
|            GPClient/moc_gpclient.cpp |  | ||||||
|            GPClient/moc_gpclient.o |  | ||||||
|            GPClient/moc_gpservice_interface.cpp |  | ||||||
|            GPClient/moc_gpservice_interface.o |  | ||||||
|            GPClient/moc_normalloginwindow.cpp |  | ||||||
|            GPClient/moc_normalloginwindow.o |  | ||||||
|            GPClient/moc_portalauthenticator.cpp |  | ||||||
|            GPClient/moc_portalauthenticator.o |  | ||||||
|            GPClient/moc_predefs.h |  | ||||||
|            GPClient/moc_samlloginwindow.cpp |  | ||||||
|            GPClient/moc_samlloginwindow.o |  | ||||||
|            GPClient/moc_singleapplication.cpp |  | ||||||
|            GPClient/moc_singleapplication.o |  | ||||||
|            GPClient/moc_singleapplication_p.cpp |  | ||||||
|            GPClient/moc_singleapplication_p.o |  | ||||||
|            GPClient/normalloginwindow.cpp |  | ||||||
|            GPClient/normalloginwindow.h |  | ||||||
|            GPClient/normalloginwindow.o |  | ||||||
|            GPClient/not_connected.png |  | ||||||
|            GPClient/pending.png |  | ||||||
|            GPClient/portalauthenticator.cpp |  | ||||||
|            GPClient/portalauthenticator.h |  | ||||||
|            GPClient/portalauthenticator.o |  | ||||||
|            GPClient/portalconfigresponse.cpp |  | ||||||
|            GPClient/portalconfigresponse.h |  | ||||||
|            GPClient/portalconfigresponse.o |  | ||||||
|            GPClient/preloginresponse.cpp |  | ||||||
|            GPClient/preloginresponse.h |  | ||||||
|            GPClient/preloginresponse.o |  | ||||||
|            GPClient/qrc_resources.cpp |  | ||||||
|            GPClient/qrc_resources.o |  | ||||||
|            GPClient/radio_selected.png |  | ||||||
|            GPClient/radio_unselected.png |  | ||||||
|            GPClient/samlloginwindow.cpp |  | ||||||
|            GPClient/samlloginwindow.h |  | ||||||
|            GPClient/samlloginwindow.o |  | ||||||
|            GPClient/singleapplication.o |  | ||||||
|            GPClient/singleapplication_p.o |  | ||||||
|            GPClient/ui_gpclient.h |  | ||||||
|            GPClient/ui_normalloginwindow.h |  | ||||||
|            GPService/GPService.pro |  | ||||||
|            GPService/Makefile |  | ||||||
|            GPService/dbus/com.yuezk.qt.GPService.service |  | ||||||
|            GPService/gpservice |  | ||||||
|            GPService/gpservice.cpp |  | ||||||
|            GPService/gpservice.h |  | ||||||
|            GPService/gpservice.o |  | ||||||
|            GPService/gpservice_adaptor.cpp |  | ||||||
|            GPService/gpservice_adaptor.h |  | ||||||
|            GPService/gpservice_adaptor.o |  | ||||||
|            GPService/main.cpp |  | ||||||
|            GPService/main.o |  | ||||||
|            GPService/moc_gpservice.cpp |  | ||||||
|            GPService/moc_gpservice.o |  | ||||||
|            GPService/moc_gpservice_adaptor.cpp |  | ||||||
|            GPService/moc_gpservice_adaptor.o |  | ||||||
|            GPService/moc_predefs.h |  | ||||||
|            GPService/moc_sigwatch.cpp |  | ||||||
|            GPService/moc_singleapplication.cpp |  | ||||||
|            GPService/moc_singleapplication.o |  | ||||||
|            GPService/moc_singleapplication_p.cpp |  | ||||||
|            GPService/moc_singleapplication_p.o |  | ||||||
|            GPService/sigwatch.o |  | ||||||
|            GPService/singleapplication.o |  | ||||||
|            GPService/singleapplication_p.o |  | ||||||
|            GPService/systemd/gpservice.service |  | ||||||
|            GlobalProtect-openconnect.pro |  | ||||||
|            Makefile |  | ||||||
|            README.md |  | ||||||
|            plog/.appveyor.yml |  | ||||||
|            plog/.circleci/config.yml |  | ||||||
|            plog/.cirrus.yml |  | ||||||
|            plog/.editorconfig |  | ||||||
|            plog/.git |  | ||||||
|            plog/.gitignore |  | ||||||
|            plog/.travis.yml |  | ||||||
|            plog/CMakeLists.txt |  | ||||||
|            plog/README.md |  | ||||||
|            plog/include/plog/Appenders/AndroidAppender.h |  | ||||||
|            plog/include/plog/Appenders/ColorConsoleAppender.h |  | ||||||
|            plog/include/plog/Appenders/ConsoleAppender.h |  | ||||||
|            plog/include/plog/Appenders/DebugOutputAppender.h |  | ||||||
|            plog/include/plog/Appenders/EventLogAppender.h |  | ||||||
|            plog/include/plog/Appenders/IAppender.h |  | ||||||
|            plog/include/plog/Appenders/RollingFileAppender.h |  | ||||||
|            plog/include/plog/Converters/NativeEOLConverter.h |  | ||||||
|            plog/include/plog/Converters/UTF8Converter.h |  | ||||||
|            plog/include/plog/Formatters/CsvFormatter.h |  | ||||||
|            plog/include/plog/Formatters/FuncMessageFormatter.h |  | ||||||
|            plog/include/plog/Formatters/MessageOnlyFormatter.h |  | ||||||
|            plog/include/plog/Formatters/TxtFormatter.h |  | ||||||
|            plog/include/plog/Init.h |  | ||||||
|            plog/include/plog/Log.h |  | ||||||
|            plog/include/plog/Logger.h |  | ||||||
|            plog/include/plog/Record.h |  | ||||||
|            plog/include/plog/Severity.h |  | ||||||
|            plog/include/plog/Util.h |  | ||||||
|            plog/include/plog/WinApi.h |  | ||||||
|            plog/samples/Android/CMakeLists.txt |  | ||||||
|            plog/samples/Android/jni/Application.mk |  | ||||||
|            plog/samples/Android/jni/Sample.cpp |  | ||||||
|            plog/samples/CMakeLists.txt |  | ||||||
|            plog/samples/Chained/CMakeLists.txt |  | ||||||
|            plog/samples/Chained/ChainedApp/Main.cpp |  | ||||||
|            plog/samples/Chained/ChainedLib/Main.cpp |  | ||||||
|            plog/samples/ColorConsole/CMakeLists.txt |  | ||||||
|            plog/samples/ColorConsole/Main.cpp |  | ||||||
|            plog/samples/CustomAppender/CMakeLists.txt |  | ||||||
|            plog/samples/CustomAppender/Main.cpp |  | ||||||
|            plog/samples/CustomConverter/CMakeLists.txt |  | ||||||
|            plog/samples/CustomConverter/Main.cpp |  | ||||||
|            plog/samples/CustomFormatter/CMakeLists.txt |  | ||||||
|            plog/samples/CustomFormatter/Main.cpp |  | ||||||
|            plog/samples/CustomType/CMakeLists.txt |  | ||||||
|            plog/samples/CustomType/Main.cpp |  | ||||||
|            plog/samples/DebugOutput/CMakeLists.txt |  | ||||||
|            plog/samples/DebugOutput/Main.cpp |  | ||||||
|            plog/samples/Demo/CMakeLists.txt |  | ||||||
|            plog/samples/Demo/Customer.h |  | ||||||
|            plog/samples/Demo/Main.cpp |  | ||||||
|            plog/samples/Demo/MyClass.cpp |  | ||||||
|            plog/samples/Demo/MyClass.h |  | ||||||
|            plog/samples/EventLog/CMakeLists.txt |  | ||||||
|            plog/samples/EventLog/Main.cpp |  | ||||||
|            plog/samples/Facilities/CMakeLists.txt |  | ||||||
|            plog/samples/Facilities/Main.cpp |  | ||||||
|            plog/samples/Hello/CMakeLists.txt |  | ||||||
|            plog/samples/Hello/Main.cpp |  | ||||||
|            plog/samples/Library/CMakeLists.txt |  | ||||||
|            plog/samples/Library/LibraryApp/Main.cpp |  | ||||||
|            plog/samples/Library/LibraryLib/Lib.cpp |  | ||||||
|            plog/samples/MultiAppender/CMakeLists.txt |  | ||||||
|            plog/samples/MultiAppender/Main.cpp |  | ||||||
|            plog/samples/MultiInstance/CMakeLists.txt |  | ||||||
|            plog/samples/MultiInstance/Main.cpp |  | ||||||
|            plog/samples/ObjectiveC/CMakeLists.txt |  | ||||||
|            plog/samples/ObjectiveC/Main.mm |  | ||||||
|            plog/samples/Performance/CMakeLists.txt |  | ||||||
|            plog/samples/Performance/Main.cpp |  | ||||||
|            plog/samples/SetFileName/CMakeLists.txt |  | ||||||
|            plog/samples/SetFileName/Main.cpp |  | ||||||
|            plog/samples/Shared/CMakeLists.txt |  | ||||||
|            plog/samples/Shared/SharedApp/Main.cpp |  | ||||||
|            plog/samples/Shared/SharedLib/Main.cpp |  | ||||||
|            plog/samples/SkipNativeEOL/CMakeLists.txt |  | ||||||
|            plog/samples/SkipNativeEOL/Main.cpp |  | ||||||
|            plog/samples/UtcTime/CMakeLists.txt |  | ||||||
|            plog/samples/UtcTime/Main.cpp |  | ||||||
|            screenshot.png |  | ||||||
|            singleapplication/.git |  | ||||||
|            singleapplication/.github/FUNDING.yml |  | ||||||
|            singleapplication/.github/workflows/build-cmake.yml |  | ||||||
|            singleapplication/.gitignore |  | ||||||
|            singleapplication/CHANGELOG.md |  | ||||||
|            singleapplication/CMakeLists.txt |  | ||||||
|            singleapplication/README.md |  | ||||||
|            singleapplication/Windows.md |  | ||||||
|            singleapplication/examples/basic/CMakeLists.txt |  | ||||||
|            singleapplication/examples/basic/basic.pro |  | ||||||
|            singleapplication/examples/basic/main.cpp |  | ||||||
|            singleapplication/examples/calculator/CMakeLists.txt |  | ||||||
|            singleapplication/examples/calculator/button.cpp |  | ||||||
|            singleapplication/examples/calculator/button.h |  | ||||||
|            singleapplication/examples/calculator/calculator.cpp |  | ||||||
|            singleapplication/examples/calculator/calculator.h |  | ||||||
|            singleapplication/examples/calculator/calculator.pro |  | ||||||
|            singleapplication/examples/calculator/main.cpp |  | ||||||
|            singleapplication/examples/sending_arguments/CMakeLists.txt |  | ||||||
|            singleapplication/examples/sending_arguments/main.cpp |  | ||||||
|            singleapplication/examples/sending_arguments/messagereceiver.cpp |  | ||||||
|            singleapplication/examples/sending_arguments/messagereceiver.h |  | ||||||
|            singleapplication/examples/sending_arguments/sending_arguments.pro |  | ||||||
|            singleapplication/singleapplication.pri |  | ||||||
| Copyright: __NO_COPYRIGHT_NOR_LICENSE__ |  | ||||||
| License:   __NO_COPYRIGHT_NOR_LICENSE__ |  | ||||||
|  |  | ||||||
| Files:     GPService/sigwatch.cpp |  | ||||||
|            GPService/sigwatch.h |  | ||||||
|            singleapplication/singleapplication.cpp |  | ||||||
|            singleapplication/singleapplication.h |  | ||||||
| Copyright: 2014 Simon Knopp |  | ||||||
|            2015-2018 Itay Grudev |  | ||||||
| License:   Expat |  | ||||||
|  Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  in the Software without restriction, including without limitation the rights |  | ||||||
|  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  furnished to do so, subject to the following conditions: |  | ||||||
|  . |  | ||||||
|  The above copyright notice and this permission notice shall be included in |  | ||||||
|  all copies or substantial portions of the Software. |  | ||||||
|  . |  | ||||||
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  THE SOFTWARE. |  | ||||||
|  |  | ||||||
| Files:     singleapplication/singleapplication_p.cpp |  | ||||||
|            singleapplication/singleapplication_p.h |  | ||||||
| Copyright: 2015-2018 Itay Grudev |  | ||||||
| License:   Expat |  | ||||||
|  Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  in the Software without restriction, including without limitation the rights |  | ||||||
|  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  furnished to do so, subject to the following conditions: |  | ||||||
|  . |  | ||||||
|  The above copyright notice and this permission notice shall be included in |  | ||||||
|  all copies or substantial portions of the Software. |  | ||||||
|  . |  | ||||||
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  THE SOFTWARE. |  | ||||||
|  . |  | ||||||
|  W A R N I N G !!! |  | ||||||
|  |  | ||||||
| Files:     plog/samples/Android/jni/Android.mk |  | ||||||
| Copyright: 2009 The Android Open Source Project |  | ||||||
| License:   Apache-2.0 |  | ||||||
|  Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  you may not use this file except in compliance with the License. |  | ||||||
|  You may obtain a copy of the License at |  | ||||||
|  . |  | ||||||
|  http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  . |  | ||||||
|  Unless required by applicable law or agreed to in writing, software |  | ||||||
|  distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  See the License for the specific language governing permissions and |  | ||||||
|  limitations under the License. |  | ||||||
|  . |  | ||||||
|  On Debian systems, the complete text of the Apache License Version 2.0 |  | ||||||
|   can be found in `/usr/share/common-licenses/Apache-2.0'. |  | ||||||
|  |  | ||||||
| #---------------------------------------------------------------------------- |  | ||||||
| # xml and html files (skipped): |  | ||||||
| #         GPService/gpservice.xml |  | ||||||
| #         GPService/dbus/com.yuezk.qt.GPService.conf |  | ||||||
| #         GPClient/com.yuezk.qt.GPClient.svg |  | ||||||
| #         GPClient/gpclient.ui |  | ||||||
| #         GPClient/normalloginwindow.ui |  | ||||||
| #         GPClient/resources.qrc |  | ||||||
|  |  | ||||||
| #---------------------------------------------------------------------------- |  | ||||||
| # Files marked as NO_LICENSE_TEXT_FOUND may be covered by the following |  | ||||||
| # license/copyright files. |  | ||||||
|  |  | ||||||
| #---------------------------------------------------------------------------- |  | ||||||
| # License file: LICENSE |  | ||||||
|                      GNU GENERAL PUBLIC LICENSE |  | ||||||
|                         Version 3, 29 June 2007 |  | ||||||
|  . |  | ||||||
|   Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |  | ||||||
|   Everyone is permitted to copy and distribute verbatim copies |  | ||||||
|   of this license document, but changing it is not allowed. |  | ||||||
|  . |  | ||||||
|                              Preamble |  | ||||||
|  . |  | ||||||
|    The GNU General Public License is a free, copyleft license for |  | ||||||
|  software and other kinds of works. |  | ||||||
|  . |  | ||||||
|    The licenses for most software and other practical works are designed |  | ||||||
|  to take away your freedom to share and change the works.  By contrast, |  | ||||||
|  the GNU General Public License is intended to guarantee your freedom to |  | ||||||
|  share and change all versions of a program--to make sure it remains free |  | ||||||
|  software for all its users.  We, the Free Software Foundation, use the |  | ||||||
|  GNU General Public License for most of our software; it applies also to |  | ||||||
|  any other work released this way by its authors.  You can apply it to |  | ||||||
|  your programs, too. |  | ||||||
|  . |  | ||||||
|    When we speak of free software, we are referring to freedom, not |  | ||||||
|  price.  Our General Public Licenses are designed to make sure that you |  | ||||||
|  have the freedom to distribute copies of free software (and charge for |  | ||||||
|  them if you wish), that you receive source code or can get it if you |  | ||||||
|  want it, that you can change the software or use pieces of it in new |  | ||||||
|  free programs, and that you know you can do these things. |  | ||||||
|  . |  | ||||||
|    To protect your rights, we need to prevent others from denying you |  | ||||||
|  these rights or asking you to surrender the rights.  Therefore, you have |  | ||||||
|  certain responsibilities if you distribute copies of the software, or if |  | ||||||
|  you modify it: responsibilities to respect the freedom of others. |  | ||||||
|  . |  | ||||||
|    For example, if you distribute copies of such a program, whether |  | ||||||
|  gratis or for a fee, you must pass on to the recipients the same |  | ||||||
|  freedoms that you received.  You must make sure that they, too, receive |  | ||||||
|  or can get the source code.  And you must show them these terms so they |  | ||||||
|  know their rights. |  | ||||||
|  . |  | ||||||
|    Developers that use the GNU GPL protect your rights with two steps: |  | ||||||
|  (1) assert copyright on the software, and (2) offer you this License |  | ||||||
|  giving you legal permission to copy, distribute and/or modify it. |  | ||||||
|  . |  | ||||||
|    For the developers' and authors' protection, the GPL clearly explains |  | ||||||
|  that there is no warranty for this free software.  For both users' and |  | ||||||
|  authors' sake, the GPL requires that modified versions be marked as |  | ||||||
|  changed, so that their problems will not be attributed erroneously to |  | ||||||
|  authors of previous versions. |  | ||||||
|  . |  | ||||||
|    Some devices are designed to deny users access to install or run |  | ||||||
|  modified versions of the software inside them, although the manufacturer |  | ||||||
|  can do so.  This is fundamentally incompatible with the aim of |  | ||||||
|  protecting users' freedom to change the software.  The systematic |  | ||||||
|  pattern of such abuse occurs in the area of products for individuals to |  | ||||||
|  use, which is precisely where it is most unacceptable.  Therefore, we |  | ||||||
|  have designed this version of the GPL to prohibit the practice for those |  | ||||||
|  products.  If such problems arise substantially in other domains, we |  | ||||||
|  stand ready to extend this provision to those domains in future versions |  | ||||||
|  of the GPL, as needed to protect the freedom of users. |  | ||||||
|  . |  | ||||||
|    Finally, every program is threatened constantly by software patents. |  | ||||||
|  States should not allow patents to restrict development and use of |  | ||||||
|  software on general-purpose computers, but in those that do, we wish to |  | ||||||
|  avoid the special danger that patents applied to a free program could |  | ||||||
|  make it effectively proprietary.  To prevent this, the GPL assures that |  | ||||||
|  patents cannot be used to render the program non-free. |  | ||||||
|  . |  | ||||||
|    The precise terms and conditions for copying, distribution and |  | ||||||
|  modification follow. |  | ||||||
|  . |  | ||||||
|                         TERMS AND CONDITIONS |  | ||||||
|  . |  | ||||||
|    0. Definitions. |  | ||||||
|  . |  | ||||||
|    "This License" refers to version 3 of the GNU General Public License. |  | ||||||
|  . |  | ||||||
|    "Copyright" also means copyright-like laws that apply to other kinds of |  | ||||||
|  works, such as semiconductor masks. |  | ||||||
|  . |  | ||||||
|    "The Program" refers to any copyrightable work licensed under this |  | ||||||
|  License.  Each licensee is addressed as "you".  "Licensees" and |  | ||||||
|  "recipients" may be individuals or organizations. |  | ||||||
|  . |  | ||||||
|    To "modify" a work means to copy from or adapt all or part of the work |  | ||||||
|  in a fashion requiring copyright permission, other than the making of an |  | ||||||
|  exact copy.  The resulting work is called a "modified version" of the |  | ||||||
|  earlier work or a work "based on" the earlier work. |  | ||||||
|  . |  | ||||||
|    A "covered work" means either the unmodified Program or a work based |  | ||||||
|  on the Program. |  | ||||||
|  . |  | ||||||
|    To "propagate" a work means to do anything with it that, without |  | ||||||
|  permission, would make you directly or secondarily liable for |  | ||||||
|  infringement under applicable copyright law, except executing it on a |  | ||||||
|  computer or modifying a private copy.  Propagation includes copying, |  | ||||||
|  distribution (with or without modification), making available to the |  | ||||||
|  public, and in some countries other activities as well. |  | ||||||
|  . |  | ||||||
|    To "convey" a work means any kind of propagation that enables other |  | ||||||
|  parties to make or receive copies.  Mere interaction with a user through |  | ||||||
|  a computer network, with no transfer of a copy, is not conveying. |  | ||||||
|  . |  | ||||||
|    An interactive user interface displays "Appropriate Legal Notices" |  | ||||||
|  to the extent that it includes a convenient and prominently visible |  | ||||||
|  feature that (1) displays an appropriate copyright notice, and (2) |  | ||||||
|  tells the user that there is no warranty for the work (except to the |  | ||||||
|  extent that warranties are provided), that licensees may convey the |  | ||||||
|  work under this License, and how to view a copy of this License.  If |  | ||||||
|  the interface presents a list of user commands or options, such as a |  | ||||||
|  menu, a prominent item in the list meets this criterion. |  | ||||||
|  . |  | ||||||
|    1. Source Code. |  | ||||||
|  . |  | ||||||
|    The "source code" for a work means the preferred form of the work |  | ||||||
|  for making modifications to it.  "Object code" means any non-source |  | ||||||
|  form of a work. |  | ||||||
|  . |  | ||||||
|    A "Standard Interface" means an interface that either is an official |  | ||||||
|  standard defined by a recognized standards body, or, in the case of |  | ||||||
|  interfaces specified for a particular programming language, one that |  | ||||||
|  is widely used among developers working in that language. |  | ||||||
|  . |  | ||||||
|    The "System Libraries" of an executable work include anything, other |  | ||||||
|  than the work as a whole, that (a) is included in the normal form of |  | ||||||
|  packaging a Major Component, but which is not part of that Major |  | ||||||
|  Component, and (b) serves only to enable use of the work with that |  | ||||||
|  Major Component, or to implement a Standard Interface for which an |  | ||||||
|  implementation is available to the public in source code form.  A |  | ||||||
|  "Major Component", in this context, means a major essential component |  | ||||||
|  (kernel, window system, and so on) of the specific operating system |  | ||||||
|  (if any) on which the executable work runs, or a compiler used to |  | ||||||
|  produce the work, or an object code interpreter used to run it. |  | ||||||
|  . |  | ||||||
|    The "Corresponding Source" for a work in object code form means all |  | ||||||
|  the source code needed to generate, install, and (for an executable |  | ||||||
|  work) run the object code and to modify the work, including scripts to |  | ||||||
|  control those activities.  However, it does not include the work's |  | ||||||
|  System Libraries, or general-purpose tools or generally available free |  | ||||||
|  programs which are used unmodified in performing those activities but |  | ||||||
|  which are not part of the work.  For example, Corresponding Source |  | ||||||
|  includes interface definition files associated with source files for |  | ||||||
|  the work, and the source code for shared libraries and dynamically |  | ||||||
|  linked subprograms that the work is specifically designed to require, |  | ||||||
|  such as by intimate data communication or control flow between those |  | ||||||
|  subprograms and other parts of the work. |  | ||||||
|  . |  | ||||||
|    The Corresponding Source need not include anything that users |  | ||||||
|  can regenerate automatically from other parts of the Corresponding |  | ||||||
|  Source. |  | ||||||
|  . |  | ||||||
|    The Corresponding Source for a work in source code form is that |  | ||||||
|  same work. |  | ||||||
|  . |  | ||||||
|    2. Basic Permissions. |  | ||||||
|  . |  | ||||||
|    All rights granted under this License are granted for the term of |  | ||||||
|  copyright on the Program, and are irrevocable provided the stated |  | ||||||
|  conditions are met.  This License explicitly affirms your unlimited |  | ||||||
|  permission to run the unmodified Program.  The output from running a |  | ||||||
|  covered work is covered by this License only if the output, given its |  | ||||||
|  content, constitutes a covered work.  This License acknowledges your |  | ||||||
|  rights of fair use or other equivalent, as provided by copyright law. |  | ||||||
|  . |  | ||||||
|    You may make, run and propagate covered works that you do not |  | ||||||
|  convey, without conditions so long as your license otherwise remains |  | ||||||
|  in force.  You may convey covered works to others for the sole purpose |  | ||||||
|  of having them make modifications exclusively for you, or provide you |  | ||||||
|  with facilities for running those works, provided that you comply with |  | ||||||
|  the terms of this License in conveying all material for which you do |  | ||||||
|  not control copyright.  Those thus making or running the covered works |  | ||||||
|  for you must do so exclusively on your behalf, under your direction |  | ||||||
|  and control, on terms that prohibit them from making any copies of |  | ||||||
|  your copyrighted material outside their relationship with you. |  | ||||||
|  . |  | ||||||
|    Conveying under any other circumstances is permitted solely under |  | ||||||
|  the conditions stated below.  Sublicensing is not allowed; section 10 |  | ||||||
|  makes it unnecessary. |  | ||||||
|  . |  | ||||||
|    3. Protecting Users' Legal Rights From Anti-Circumvention Law. |  | ||||||
|  . |  | ||||||
|    No covered work shall be deemed part of an effective technological |  | ||||||
|  measure under any applicable law fulfilling obligations under article |  | ||||||
|  11 of the WIPO copyright treaty adopted on 20 December 1996, or |  | ||||||
|  similar laws prohibiting or restricting circumvention of such |  | ||||||
|  measures. |  | ||||||
|  . |  | ||||||
|    When you convey a covered work, you waive any legal power to forbid |  | ||||||
|  circumvention of technological measures to the extent such circumvention |  | ||||||
|  is effected by exercising rights under this License with respect to |  | ||||||
|  the covered work, and you disclaim any intention to limit operation or |  | ||||||
|  modification of the work as a means of enforcing, against the work's |  | ||||||
|  users, your or third parties' legal rights to forbid circumvention of |  | ||||||
|  technological measures. |  | ||||||
|  . |  | ||||||
|    4. Conveying Verbatim Copies. |  | ||||||
|  . |  | ||||||
|    You may convey verbatim copies of the Program's source code as you |  | ||||||
|  receive it, in any medium, provided that you conspicuously and |  | ||||||
|  appropriately publish on each copy an appropriate copyright notice; |  | ||||||
|  keep intact all notices stating that this License and any |  | ||||||
|  non-permissive terms added in accord with section 7 apply to the code; |  | ||||||
|  keep intact all notices of the absence of any warranty; and give all |  | ||||||
|  recipients a copy of this License along with the Program. |  | ||||||
|  . |  | ||||||
|    You may charge any price or no price for each copy that you convey, |  | ||||||
|  and you may offer support or warranty protection for a fee. |  | ||||||
|  . |  | ||||||
|    5. Conveying Modified Source Versions. |  | ||||||
|  . |  | ||||||
|    You may convey a work based on the Program, or the modifications to |  | ||||||
|  produce it from the Program, in the form of source code under the |  | ||||||
|  terms of section 4, provided that you also meet all of these conditions: |  | ||||||
|  . |  | ||||||
|      a) The work must carry prominent notices stating that you modified |  | ||||||
|      it, and giving a relevant date. |  | ||||||
|  . |  | ||||||
|      b) The work must carry prominent notices stating that it is |  | ||||||
|      released under this License and any conditions added under section |  | ||||||
|      7.  This requirement modifies the requirement in section 4 to |  | ||||||
|      "keep intact all notices". |  | ||||||
|  . |  | ||||||
|      c) You must license the entire work, as a whole, under this |  | ||||||
|      License to anyone who comes into possession of a copy.  This |  | ||||||
|      License will therefore apply, along with any applicable section 7 |  | ||||||
|      additional terms, to the whole of the work, and all its parts, |  | ||||||
|      regardless of how they are packaged.  This License gives no |  | ||||||
|      permission to license the work in any other way, but it does not |  | ||||||
|      invalidate such permission if you have separately received it. |  | ||||||
|  . |  | ||||||
|      d) If the work has interactive user interfaces, each must display |  | ||||||
|      Appropriate Legal Notices; however, if the Program has interactive |  | ||||||
|      interfaces that do not display Appropriate Legal Notices, your |  | ||||||
|      work need not make them do so. |  | ||||||
|  . |  | ||||||
|    A compilation of a covered work with other separate and independent |  | ||||||
|  works, which are not by their nature extensions of the covered work, |  | ||||||
|  and which are not combined with it such as to form a larger program, |  | ||||||
|  in or on a volume of a storage or distribution medium, is called an |  | ||||||
|  "aggregate" if the compilation and its resulting copyright are not |  | ||||||
|  used to limit the access or legal rights of the compilation's users |  | ||||||
|  beyond what the individual works permit.  Inclusion of a covered work |  | ||||||
|  in an aggregate does not cause this License to apply to the other |  | ||||||
|  parts of the aggregate. |  | ||||||
|  . |  | ||||||
|    6. Conveying Non-Source Forms. |  | ||||||
|  . |  | ||||||
|    You may convey a covered work in object code form under the terms |  | ||||||
|  of sections 4 and 5, provided that you also convey the |  | ||||||
|  machine-readable Corresponding Source under the terms of this License, |  | ||||||
|  in one of these ways: |  | ||||||
|  . |  | ||||||
|      a) Convey the object code in, or embodied in, a physical product |  | ||||||
|      (including a physical distribution medium), accompanied by the |  | ||||||
|      Corresponding Source fixed on a durable physical medium |  | ||||||
|      customarily used for software interchange. |  | ||||||
|  . |  | ||||||
|      b) Convey the object code in, or embodied in, a physical product |  | ||||||
|      (including a physical distribution medium), accompanied by a |  | ||||||
|      written offer, valid for at least three years and valid for as |  | ||||||
|      long as you offer spare parts or customer support for that product |  | ||||||
|      model, to give anyone who possesses the object code either (1) a |  | ||||||
|      copy of the Corresponding Source for all the software in the |  | ||||||
|      product that is covered by this License, on a durable physical |  | ||||||
|      medium customarily used for software interchange, for a price no |  | ||||||
|      more than your reasonable cost of physically performing this |  | ||||||
|      conveying of source, or (2) access to copy the |  | ||||||
|      Corresponding Source from a network server at no charge. |  | ||||||
|  . |  | ||||||
|      c) Convey individual copies of the object code with a copy of the |  | ||||||
|      written offer to provide the Corresponding Source.  This |  | ||||||
|      alternative is allowed only occasionally and noncommercially, and |  | ||||||
|      only if you received the object code with such an offer, in accord |  | ||||||
|      with subsection 6b. |  | ||||||
|  . |  | ||||||
|      d) Convey the object code by offering access from a designated |  | ||||||
|      place (gratis or for a charge), and offer equivalent access to the |  | ||||||
|      Corresponding Source in the same way through the same place at no |  | ||||||
|      further charge.  You need not require recipients to copy the |  | ||||||
|      Corresponding Source along with the object code.  If the place to |  | ||||||
|      copy the object code is a network server, the Corresponding Source |  | ||||||
|      may be on a different server (operated by you or a third party) |  | ||||||
|      that supports equivalent copying facilities, provided you maintain |  | ||||||
|      clear directions next to the object code saying where to find the |  | ||||||
|      Corresponding Source.  Regardless of what server hosts the |  | ||||||
|      Corresponding Source, you remain obligated to ensure that it is |  | ||||||
|      available for as long as needed to satisfy these requirements. |  | ||||||
|  . |  | ||||||
|      e) Convey the object code using peer-to-peer transmission, provided |  | ||||||
|      you inform other peers where the object code and Corresponding |  | ||||||
|      Source of the work are being offered to the general public at no |  | ||||||
|      charge under subsection 6d. |  | ||||||
|  . |  | ||||||
|    A separable portion of the object code, whose source code is excluded |  | ||||||
|  from the Corresponding Source as a System Library, need not be |  | ||||||
|  included in conveying the object code work. |  | ||||||
|  . |  | ||||||
|    A "User Product" is either (1) a "consumer product", which means any |  | ||||||
|  tangible personal property which is normally used for personal, family, |  | ||||||
|  or household purposes, or (2) anything designed or sold for incorporation |  | ||||||
|  into a dwelling.  In determining whether a product is a consumer product, |  | ||||||
|  doubtful cases shall be resolved in favor of coverage.  For a particular |  | ||||||
|  product received by a particular user, "normally used" refers to a |  | ||||||
|  typical or common use of that class of product, regardless of the status |  | ||||||
|  of the particular user or of the way in which the particular user |  | ||||||
|  actually uses, or expects or is expected to use, the product.  A product |  | ||||||
|  is a consumer product regardless of whether the product has substantial |  | ||||||
|  commercial, industrial or non-consumer uses, unless such uses represent |  | ||||||
|  the only significant mode of use of the product. |  | ||||||
|  . |  | ||||||
|    "Installation Information" for a User Product means any methods, |  | ||||||
|  procedures, authorization keys, or other information required to install |  | ||||||
|  and execute modified versions of a covered work in that User Product from |  | ||||||
|  a modified version of its Corresponding Source.  The information must |  | ||||||
|  suffice to ensure that the continued functioning of the modified object |  | ||||||
|  code is in no case prevented or interfered with solely because |  | ||||||
|  modification has been made. |  | ||||||
|  . |  | ||||||
|    If you convey an object code work under this section in, or with, or |  | ||||||
|  specifically for use in, a User Product, and the conveying occurs as |  | ||||||
|  part of a transaction in which the right of possession and use of the |  | ||||||
|  User Product is transferred to the recipient in perpetuity or for a |  | ||||||
|  fixed term (regardless of how the transaction is characterized), the |  | ||||||
|  Corresponding Source conveyed under this section must be accompanied |  | ||||||
|  by the Installation Information.  But this requirement does not apply |  | ||||||
|  if neither you nor any third party retains the ability to install |  | ||||||
|  modified object code on the User Product (for example, the work has |  | ||||||
|  been installed in ROM). |  | ||||||
|  . |  | ||||||
|    The requirement to provide Installation Information does not include a |  | ||||||
|  requirement to continue to provide support service, warranty, or updates |  | ||||||
|  for a work that has been modified or installed by the recipient, or for |  | ||||||
|  the User Product in which it has been modified or installed.  Access to a |  | ||||||
|  network may be denied when the modification itself materially and |  | ||||||
|  adversely affects the operation of the network or violates the rules and |  | ||||||
|  protocols for communication across the network. |  | ||||||
|  . |  | ||||||
|    Corresponding Source conveyed, and Installation Information provided, |  | ||||||
|  in accord with this section must be in a format that is publicly |  | ||||||
|  documented (and with an implementation available to the public in |  | ||||||
|  source code form), and must require no special password or key for |  | ||||||
|  unpacking, reading or copying. |  | ||||||
|  . |  | ||||||
|    7. Additional Terms. |  | ||||||
|  . |  | ||||||
|    "Additional permissions" are terms that supplement the terms of this |  | ||||||
|  License by making exceptions from one or more of its conditions. |  | ||||||
|  Additional permissions that are applicable to the entire Program shall |  | ||||||
|  be treated as though they were included in this License, to the extent |  | ||||||
|  that they are valid under applicable law.  If additional permissions |  | ||||||
|  apply only to part of the Program, that part may be used separately |  | ||||||
|  under those permissions, but the entire Program remains governed by |  | ||||||
|  this License without regard to the additional permissions. |  | ||||||
|  . |  | ||||||
|    When you convey a copy of a covered work, you may at your option |  | ||||||
|  remove any additional permissions from that copy, or from any part of |  | ||||||
|  it.  (Additional permissions may be written to require their own |  | ||||||
|  removal in certain cases when you modify the work.)  You may place |  | ||||||
|  additional permissions on material, added by you to a covered work, |  | ||||||
|  for which you have or can give appropriate copyright permission. |  | ||||||
|  . |  | ||||||
|    Notwithstanding any other provision of this License, for material you |  | ||||||
|  add to a covered work, you may (if authorized by the copyright holders of |  | ||||||
|  that material) supplement the terms of this License with terms: |  | ||||||
|  . |  | ||||||
|      a) Disclaiming warranty or limiting liability differently from the |  | ||||||
|      terms of sections 15 and 16 of this License; or |  | ||||||
|  . |  | ||||||
|      b) Requiring preservation of specified reasonable legal notices or |  | ||||||
|      author attributions in that material or in the Appropriate Legal |  | ||||||
|      Notices displayed by works containing it; or |  | ||||||
|  . |  | ||||||
|      c) Prohibiting misrepresentation of the origin of that material, or |  | ||||||
|      requiring that modified versions of such material be marked in |  | ||||||
|      reasonable ways as different from the original version; or |  | ||||||
|  . |  | ||||||
|      d) Limiting the use for publicity purposes of names of licensors or |  | ||||||
|      authors of the material; or |  | ||||||
|  . |  | ||||||
|      e) Declining to grant rights under trademark law for use of some |  | ||||||
|      trade names, trademarks, or service marks; or |  | ||||||
|  . |  | ||||||
|      f) Requiring indemnification of licensors and authors of that |  | ||||||
|      material by anyone who conveys the material (or modified versions of |  | ||||||
|      it) with contractual assumptions of liability to the recipient, for |  | ||||||
|      any liability that these contractual assumptions directly impose on |  | ||||||
|      those licensors and authors. |  | ||||||
|  . |  | ||||||
|    All other non-permissive additional terms are considered "further |  | ||||||
|  restrictions" within the meaning of section 10.  If the Program as you |  | ||||||
|  received it, or any part of it, contains a notice stating that it is |  | ||||||
|  governed by this License along with a term that is a further |  | ||||||
|  restriction, you may remove that term.  If a license document contains |  | ||||||
|  a further restriction but permits relicensing or conveying under this |  | ||||||
|  License, you may add to a covered work material governed by the terms |  | ||||||
|  of that license document, provided that the further restriction does |  | ||||||
|  not survive such relicensing or conveying. |  | ||||||
|  . |  | ||||||
|    If you add terms to a covered work in accord with this section, you |  | ||||||
|  must place, in the relevant source files, a statement of the |  | ||||||
|  additional terms that apply to those files, or a notice indicating |  | ||||||
|  where to find the applicable terms. |  | ||||||
|  . |  | ||||||
|    Additional terms, permissive or non-permissive, may be stated in the |  | ||||||
|  form of a separately written license, or stated as exceptions; |  | ||||||
|  the above requirements apply either way. |  | ||||||
|  . |  | ||||||
|    8. Termination. |  | ||||||
|  . |  | ||||||
|    You may not propagate or modify a covered work except as expressly |  | ||||||
|  provided under this License.  Any attempt otherwise to propagate or |  | ||||||
|  modify it is void, and will automatically terminate your rights under |  | ||||||
|  this License (including any patent licenses granted under the third |  | ||||||
|  paragraph of section 11). |  | ||||||
|  . |  | ||||||
|    However, if you cease all violation of this License, then your |  | ||||||
|  license from a particular copyright holder is reinstated (a) |  | ||||||
|  provisionally, unless and until the copyright holder explicitly and |  | ||||||
|  finally terminates your license, and (b) permanently, if the copyright |  | ||||||
|  holder fails to notify you of the violation by some reasonable means |  | ||||||
|  prior to 60 days after the cessation. |  | ||||||
|  . |  | ||||||
|    Moreover, your license from a particular copyright holder is |  | ||||||
|  reinstated permanently if the copyright holder notifies you of the |  | ||||||
|  violation by some reasonable means, this is the first time you have |  | ||||||
|  received notice of violation of this License (for any work) from that |  | ||||||
|  copyright holder, and you cure the violation prior to 30 days after |  | ||||||
|  your receipt of the notice. |  | ||||||
|  . |  | ||||||
|    Termination of your rights under this section does not terminate the |  | ||||||
|  licenses of parties who have received copies or rights from you under |  | ||||||
|  this License.  If your rights have been terminated and not permanently |  | ||||||
|  reinstated, you do not qualify to receive new licenses for the same |  | ||||||
|  material under section 10. |  | ||||||
|  . |  | ||||||
|    9. Acceptance Not Required for Having Copies. |  | ||||||
|  . |  | ||||||
|    You are not required to accept this License in order to receive or |  | ||||||
|  run a copy of the Program.  Ancillary propagation of a covered work |  | ||||||
|  occurring solely as a consequence of using peer-to-peer transmission |  | ||||||
|  to receive a copy likewise does not require acceptance.  However, |  | ||||||
|  nothing other than this License grants you permission to propagate or |  | ||||||
|  modify any covered work.  These actions infringe copyright if you do |  | ||||||
|  not accept this License.  Therefore, by modifying or propagating a |  | ||||||
|  covered work, you indicate your acceptance of this License to do so. |  | ||||||
|  . |  | ||||||
|    10. Automatic Licensing of Downstream Recipients. |  | ||||||
|  . |  | ||||||
|    Each time you convey a covered work, the recipient automatically |  | ||||||
|  receives a license from the original licensors, to run, modify and |  | ||||||
|  propagate that work, subject to this License.  You are not responsible |  | ||||||
|  for enforcing compliance by third parties with this License. |  | ||||||
|  . |  | ||||||
|    An "entity transaction" is a transaction transferring control of an |  | ||||||
|  organization, or substantially all assets of one, or subdividing an |  | ||||||
|  organization, or merging organizations.  If propagation of a covered |  | ||||||
|  work results from an entity transaction, each party to that |  | ||||||
|  transaction who receives a copy of the work also receives whatever |  | ||||||
|  licenses to the work the party's predecessor in interest had or could |  | ||||||
|  give under the previous paragraph, plus a right to possession of the |  | ||||||
|  Corresponding Source of the work from the predecessor in interest, if |  | ||||||
|  the predecessor has it or can get it with reasonable efforts. |  | ||||||
|  . |  | ||||||
|    You may not impose any further restrictions on the exercise of the |  | ||||||
|  rights granted or affirmed under this License.  For example, you may |  | ||||||
|  not impose a license fee, royalty, or other charge for exercise of |  | ||||||
|  rights granted under this License, and you may not initiate litigation |  | ||||||
|  (including a cross-claim or counterclaim in a lawsuit) alleging that |  | ||||||
|  any patent claim is infringed by making, using, selling, offering for |  | ||||||
|  sale, or importing the Program or any portion of it. |  | ||||||
|  . |  | ||||||
|    11. Patents. |  | ||||||
|  . |  | ||||||
|    A "contributor" is a copyright holder who authorizes use under this |  | ||||||
|  License of the Program or a work on which the Program is based.  The |  | ||||||
|  work thus licensed is called the contributor's "contributor version". |  | ||||||
|  . |  | ||||||
|    A contributor's "essential patent claims" are all patent claims |  | ||||||
|  owned or controlled by the contributor, whether already acquired or |  | ||||||
|  hereafter acquired, that would be infringed by some manner, permitted |  | ||||||
|  by this License, of making, using, or selling its contributor version, |  | ||||||
|  but do not include claims that would be infringed only as a |  | ||||||
|  consequence of further modification of the contributor version.  For |  | ||||||
|  purposes of this definition, "control" includes the right to grant |  | ||||||
|  patent sublicenses in a manner consistent with the requirements of |  | ||||||
|  this License. |  | ||||||
|  . |  | ||||||
|    Each contributor grants you a non-exclusive, worldwide, royalty-free |  | ||||||
|  patent license under the contributor's essential patent claims, to |  | ||||||
|  make, use, sell, offer for sale, import and otherwise run, modify and |  | ||||||
|  propagate the contents of its contributor version. |  | ||||||
|  . |  | ||||||
|    In the following three paragraphs, a "patent license" is any express |  | ||||||
|  agreement or commitment, however denominated, not to enforce a patent |  | ||||||
|  (such as an express permission to practice a patent or covenant not to |  | ||||||
|  sue for patent infringement).  To "grant" such a patent license to a |  | ||||||
|  party means to make such an agreement or commitment not to enforce a |  | ||||||
|  patent against the party. |  | ||||||
|  . |  | ||||||
|    If you convey a covered work, knowingly relying on a patent license, |  | ||||||
|  and the Corresponding Source of the work is not available for anyone |  | ||||||
|  to copy, free of charge and under the terms of this License, through a |  | ||||||
|  publicly available network server or other readily accessible means, |  | ||||||
|  then you must either (1) cause the Corresponding Source to be so |  | ||||||
|  available, or (2) arrange to deprive yourself of the benefit of the |  | ||||||
|  patent license for this particular work, or (3) arrange, in a manner |  | ||||||
|  consistent with the requirements of this License, to extend the patent |  | ||||||
|  license to downstream recipients.  "Knowingly relying" means you have |  | ||||||
|  actual knowledge that, but for the patent license, your conveying the |  | ||||||
|  covered work in a country, or your recipient's use of the covered work |  | ||||||
|  in a country, would infringe one or more identifiable patents in that |  | ||||||
|  country that you have reason to believe are valid. |  | ||||||
|  . |  | ||||||
|    If, pursuant to or in connection with a single transaction or |  | ||||||
|  arrangement, you convey, or propagate by procuring conveyance of, a |  | ||||||
|  covered work, and grant a patent license to some of the parties |  | ||||||
|  receiving the covered work authorizing them to use, propagate, modify |  | ||||||
|  or convey a specific copy of the covered work, then the patent license |  | ||||||
|  you grant is automatically extended to all recipients of the covered |  | ||||||
|  work and works based on it. |  | ||||||
|  . |  | ||||||
|    A patent license is "discriminatory" if it does not include within |  | ||||||
|  the scope of its coverage, prohibits the exercise of, or is |  | ||||||
|  conditioned on the non-exercise of one or more of the rights that are |  | ||||||
|  specifically granted under this License.  You may not convey a covered |  | ||||||
|  work if you are a party to an arrangement with a third party that is |  | ||||||
|  in the business of distributing software, under which you make payment |  | ||||||
|  to the third party based on the extent of your activity of conveying |  | ||||||
|  the work, and under which the third party grants, to any of the |  | ||||||
|  parties who would receive the covered work from you, a discriminatory |  | ||||||
|  patent license (a) in connection with copies of the covered work |  | ||||||
|  conveyed by you (or copies made from those copies), or (b) primarily |  | ||||||
|  for and in connection with specific products or compilations that |  | ||||||
|  contain the covered work, unless you entered into that arrangement, |  | ||||||
|  or that patent license was granted, prior to 28 March 2007. |  | ||||||
|  . |  | ||||||
|    Nothing in this License shall be construed as excluding or limiting |  | ||||||
|  any implied license or other defenses to infringement that may |  | ||||||
|  otherwise be available to you under applicable patent law. |  | ||||||
|  . |  | ||||||
|    12. No Surrender of Others' Freedom. |  | ||||||
|  . |  | ||||||
|    If conditions are imposed on you (whether by court order, agreement or |  | ||||||
|  otherwise) that contradict the conditions of this License, they do not |  | ||||||
|  excuse you from the conditions of this License.  If you cannot convey a |  | ||||||
|  covered work so as to satisfy simultaneously your obligations under this |  | ||||||
|  License and any other pertinent obligations, then as a consequence you may |  | ||||||
|  not convey it at all.  For example, if you agree to terms that obligate you |  | ||||||
|  to collect a royalty for further conveying from those to whom you convey |  | ||||||
|  the Program, the only way you could satisfy both those terms and this |  | ||||||
|  License would be to refrain entirely from conveying the Program. |  | ||||||
|  . |  | ||||||
|    13. Use with the GNU Affero General Public License. |  | ||||||
|  . |  | ||||||
|    Notwithstanding any other provision of this License, you have |  | ||||||
|  permission to link or combine any covered work with a work licensed |  | ||||||
|  under version 3 of the GNU Affero General Public License into a single |  | ||||||
|  combined work, and to convey the resulting work.  The terms of this |  | ||||||
|  License will continue to apply to the part which is the covered work, |  | ||||||
|  but the special requirements of the GNU Affero General Public License, |  | ||||||
|  section 13, concerning interaction through a network will apply to the |  | ||||||
|  combination as such. |  | ||||||
|  . |  | ||||||
|    14. Revised Versions of this License. |  | ||||||
|  . |  | ||||||
|    The Free Software Foundation may publish revised and/or new versions of |  | ||||||
|  the GNU General Public License from time to time.  Such new versions will |  | ||||||
|  be similar in spirit to the present version, but may differ in detail to |  | ||||||
|  address new problems or concerns. |  | ||||||
|  . |  | ||||||
|    Each version is given a distinguishing version number.  If the |  | ||||||
|  Program specifies that a certain numbered version of the GNU General |  | ||||||
|  Public License "or any later version" applies to it, you have the |  | ||||||
|  option of following the terms and conditions either of that numbered |  | ||||||
|  version or of any later version published by the Free Software |  | ||||||
|  Foundation.  If the Program does not specify a version number of the |  | ||||||
|  GNU General Public License, you may choose any version ever published |  | ||||||
|  by the Free Software Foundation. |  | ||||||
|  . |  | ||||||
|    If the Program specifies that a proxy can decide which future |  | ||||||
|  versions of the GNU General Public License can be used, that proxy's |  | ||||||
|  public statement of acceptance of a version permanently authorizes you |  | ||||||
|  to choose that version for the Program. |  | ||||||
|  . |  | ||||||
|    Later license versions may give you additional or different |  | ||||||
|  permissions.  However, no additional obligations are imposed on any |  | ||||||
|  author or copyright holder as a result of your choosing to follow a |  | ||||||
|  later version. |  | ||||||
|  . |  | ||||||
|    15. Disclaimer of Warranty. |  | ||||||
|  . |  | ||||||
|    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |  | ||||||
|  APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |  | ||||||
|  HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |  | ||||||
|  OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |  | ||||||
|  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |  | ||||||
|  PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |  | ||||||
|  IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |  | ||||||
|  ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |  | ||||||
|  . |  | ||||||
|    16. Limitation of Liability. |  | ||||||
|  . |  | ||||||
|    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |  | ||||||
|  WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |  | ||||||
|  THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |  | ||||||
|  GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |  | ||||||
|  USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |  | ||||||
|  DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |  | ||||||
|  PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |  | ||||||
|  EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |  | ||||||
|  SUCH DAMAGES. |  | ||||||
|  . |  | ||||||
|    17. Interpretation of Sections 15 and 16. |  | ||||||
|  . |  | ||||||
|    If the disclaimer of warranty and limitation of liability provided |  | ||||||
|  above cannot be given local legal effect according to their terms, |  | ||||||
|  reviewing courts shall apply local law that most closely approximates |  | ||||||
|  an absolute waiver of all civil liability in connection with the |  | ||||||
|  Program, unless a warranty or assumption of liability accompanies a |  | ||||||
|  copy of the Program in return for a fee. |  | ||||||
|  . |  | ||||||
|                       END OF TERMS AND CONDITIONS |  | ||||||
|  . |  | ||||||
|              How to Apply These Terms to Your New Programs |  | ||||||
|  . |  | ||||||
|    If you develop a new program, and you want it to be of the greatest |  | ||||||
|  possible use to the public, the best way to achieve this is to make it |  | ||||||
|  free software which everyone can redistribute and change under these terms. |  | ||||||
|  . |  | ||||||
|    To do so, attach the following notices to the program.  It is safest |  | ||||||
|  to attach them to the start of each source file to most effectively |  | ||||||
|  state the exclusion of warranty; and each file should have at least |  | ||||||
|  the "copyright" line and a pointer to where the full notice is found. |  | ||||||
|  . |  | ||||||
|      <one line to give the program's name and a brief idea of what it does.> |  | ||||||
|      Copyright (C) <year>  <name of author> |  | ||||||
|  . |  | ||||||
|      This program is free software: you can redistribute it and/or modify |  | ||||||
|      it under the terms of the GNU General Public License as published by |  | ||||||
|      the Free Software Foundation, either version 3 of the License, or |  | ||||||
|      (at your option) any later version. |  | ||||||
|  . |  | ||||||
|      This program is distributed in the hope that it will be useful, |  | ||||||
|      but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|      GNU General Public License for more details. |  | ||||||
|  . |  | ||||||
|      You should have received a copy of the GNU General Public License |  | ||||||
|      along with this program.  If not, see <https://www.gnu.org/licenses/>. |  | ||||||
|  . |  | ||||||
|  Also add information on how to contact you by electronic and paper mail. |  | ||||||
|  . |  | ||||||
|    If the program does terminal interaction, make it output a short |  | ||||||
|  notice like this when it starts in an interactive mode: |  | ||||||
|  . |  | ||||||
|      <program>  Copyright (C) <year>  <name of author> |  | ||||||
|      This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |  | ||||||
|      This is free software, and you are welcome to redistribute it |  | ||||||
|      under certain conditions; type `show c' for details. |  | ||||||
|  . |  | ||||||
|  The hypothetical commands `show w' and `show c' should show the appropriate |  | ||||||
|  parts of the General Public License.  Of course, your program's commands |  | ||||||
|  might be different; for a GUI interface, you would use an "about box". |  | ||||||
|  . |  | ||||||
|    You should also get your employer (if you work as a programmer) or school, |  | ||||||
|  if any, to sign a "copyright disclaimer" for the program, if necessary. |  | ||||||
|  For more information on this, and how to apply and follow the GNU GPL, see |  | ||||||
|  <https://www.gnu.org/licenses/>. |  | ||||||
|  . |  | ||||||
|    The GNU General Public License does not permit incorporating your program |  | ||||||
|  into proprietary programs.  If your program is a subroutine library, you |  | ||||||
|  may consider it more useful to permit linking proprietary applications with |  | ||||||
|  the library.  If this is what you want to do, use the GNU Lesser General |  | ||||||
|  Public License instead of this License.  But first, please read |  | ||||||
|  <https://www.gnu.org/licenses/why-not-lgpl.html>. |  | ||||||
							
								
								
									
										1
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| # You must remove unused comment lines for the released package. |  | ||||||
							
								
								
									
										15
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| #!/usr/bin/make -f |  | ||||||
| # You must remove unused comment lines for the released package. |  | ||||||
| export DH_VERBOSE = 1 |  | ||||||
| export DEB_BUILD_MAINT_OPTIONS = hardening=+all |  | ||||||
| export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic |  | ||||||
| export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed |  | ||||||
|  |  | ||||||
| %: |  | ||||||
| 	dh $@   |  | ||||||
|  |  | ||||||
| override_dh_auto_install: |  | ||||||
| 	dh_auto_install -- prefix=/usr |  | ||||||
|  |  | ||||||
| #override_dh_install: |  | ||||||
| #	dh_install --list-missing -X.pyc -X.pyo |  | ||||||
							
								
								
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| 3.0 (quilt) |  | ||||||
							
								
								
									
										2
									
								
								debian/source/local-options
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/source/local-options
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | |||||||
| #abort-on-upstream-changes |  | ||||||
| #unapply-patches |  | ||||||
							
								
								
									
										3
									
								
								debian/watch
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								debian/watch
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +0,0 @@ | |||||||
| version=4 |  | ||||||
| opts="mode=git" https://github.com/yuezk/GlobalProtect-openconnect.git \ |  | ||||||
|    refs/tags/v([\d\.]+) debian uupdate |  | ||||||
							
								
								
									
										1
									
								
								gpclient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gpclient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | /target | ||||||
							
								
								
									
										10
									
								
								gpclient/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								gpclient/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | [package] | ||||||
|  | name = "gpclient" | ||||||
|  | version = "0.1.0" | ||||||
|  | edition = "2021" | ||||||
|  |  | ||||||
|  | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||||
|  |  | ||||||
|  | [dependencies] | ||||||
|  | gpcommon = { path = "../gpcommon" } | ||||||
|  | tokio = { version = "1.0", features = ["full"] } | ||||||
							
								
								
									
										25
									
								
								gpclient/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								gpclient/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | use gpcommon::{Client, SOCKET_PATH}; | ||||||
|  | use tokio::{io::AsyncReadExt, net::UnixStream, sync::mpsc}; | ||||||
|  |  | ||||||
|  | #[tokio::main] | ||||||
|  | async fn main() { | ||||||
|  |     // let mut stream = UnixStream::connect(SOCKET_PATH).await.unwrap(); | ||||||
|  |  | ||||||
|  |     // let mut buf = [0u8; 34]; | ||||||
|  |     // let _ = stream.read(&mut buf).await.unwrap(); | ||||||
|  |  | ||||||
|  |     // // The first two bytes are the port number, the rest is the AES key | ||||||
|  |     // let http_port = u16::from_be_bytes([buf[0], buf[1]]); | ||||||
|  |     // let aes_key = &buf[2..]; | ||||||
|  |  | ||||||
|  |     // println!("http_port: {http_port}"); | ||||||
|  |     // println!("aes_key: {aes_key:?}"); | ||||||
|  |     let (output_tx, mut output_rx) = mpsc::channel::<String>(32); | ||||||
|  |     let client = Client::default(); | ||||||
|  |  | ||||||
|  |     tokio::select! { | ||||||
|  |         _ = client.run() => { | ||||||
|  |             println!("Client finished"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								gpcommon/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								gpcommon/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | /target | ||||||
|  | /Cargo.lock | ||||||
							
								
								
									
										27
									
								
								gpcommon/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								gpcommon/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | [package] | ||||||
|  | name = "gpcommon" | ||||||
|  | version = "0.1.0" | ||||||
|  | edition = "2021" | ||||||
|  |  | ||||||
|  | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||||
|  |  | ||||||
|  | [dependencies] | ||||||
|  | tokio = { version = "1.14", features = ["full"] } | ||||||
|  | tokio-util = "0.7" | ||||||
|  | thiserror = "1.0" | ||||||
|  | serde = { version = "1.0", features = ["derive"] } | ||||||
|  | bytes = "1.0" | ||||||
|  | serde_json = "1.0" | ||||||
|  | async-trait = "0.1" | ||||||
|  | ring = "0.16" | ||||||
|  | data-encoding = "2.3" | ||||||
|  | log = "0.4" | ||||||
|  | is_executable = "1.0" | ||||||
|  | configparser = "3.0" | ||||||
|  | shlex = "1.0" | ||||||
|  | anyhow = "1.0" | ||||||
|  | tempfile = "3.8" | ||||||
|  | lexopt = "0.3.0" | ||||||
|  |  | ||||||
|  | [build-dependencies] | ||||||
|  | cc = "1.0" | ||||||
							
								
								
									
										12
									
								
								gpcommon/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								gpcommon/build.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | fn main() { | ||||||
|  |     // Link to the native openconnect library | ||||||
|  |     println!("cargo:rustc-link-lib=openconnect"); | ||||||
|  |     println!("cargo:rerun-if-changed=src/vpn/vpn.c"); | ||||||
|  |     println!("cargo:rerun-if-changed=src/vpn/vpn.h"); | ||||||
|  |  | ||||||
|  |     // Compile the vpn.c file | ||||||
|  |     cc::Build::new() | ||||||
|  |         .file("src/vpn/vpn.c") | ||||||
|  |         .include("src/vpn") | ||||||
|  |         .compile("vpn"); | ||||||
|  | } | ||||||
							
								
								
									
										286
									
								
								gpcommon/src/client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								gpcommon/src/client.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,286 @@ | |||||||
|  | use crate::cmd::{Connect, Disconnect, GetStatus}; | ||||||
|  | use crate::reader::Reader; | ||||||
|  | use crate::request::CommandPayload; | ||||||
|  | use crate::response::ResponseData; | ||||||
|  | use crate::writer::Writer; | ||||||
|  | use crate::RequestPool; | ||||||
|  | use crate::Response; | ||||||
|  | use crate::SOCKET_PATH; | ||||||
|  | use crate::{Request, VpnStatus}; | ||||||
|  | use log::{debug, info, warn}; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::fmt::Display; | ||||||
|  | use std::sync::Arc; | ||||||
|  | use tokio::io::{self, ReadHalf, WriteHalf}; | ||||||
|  | use tokio::net::UnixStream; | ||||||
|  | use tokio::sync::{mpsc, Mutex, RwLock}; | ||||||
|  | use tokio_util::sync::CancellationToken; | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | enum ServiceEvent { | ||||||
|  |     Online, | ||||||
|  |     Response(Response), | ||||||
|  |     Offline, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Response> for ServiceEvent { | ||||||
|  |     fn from(response: Response) -> Self { | ||||||
|  |         Self::Response(response) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub enum ClientStatus { | ||||||
|  |     Vpn(VpnStatus), | ||||||
|  |     Service(bool), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct Client { | ||||||
|  |     // pool of requests that are waiting for responses | ||||||
|  |     request_pool: Arc<RequestPool>, | ||||||
|  |     // tx for sending requests to the channel | ||||||
|  |     request_tx: mpsc::Sender<Request>, | ||||||
|  |     // rx for receiving requests from the channel | ||||||
|  |     request_rx: Arc<Mutex<mpsc::Receiver<Request>>>, | ||||||
|  |     // tx for sending responses to the channel | ||||||
|  |     service_event_tx: mpsc::Sender<ServiceEvent>, | ||||||
|  |     // rx for receiving responses from the channel | ||||||
|  |     service_event_rx: Arc<Mutex<mpsc::Receiver<ServiceEvent>>>, | ||||||
|  |     is_online: Arc<RwLock<bool>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize)] | ||||||
|  | pub struct ServerApiError { | ||||||
|  |     pub message: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Display for ServerApiError { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{message}", message = self.message) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<String> for ServerApiError { | ||||||
|  |     fn from(message: String) -> Self { | ||||||
|  |         Self { message } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<&str> for ServerApiError { | ||||||
|  |     fn from(message: &str) -> Self { | ||||||
|  |         Self { | ||||||
|  |             message: message.to_string(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Default for Client { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         let (request_tx, request_rx) = mpsc::channel::<Request>(32); | ||||||
|  |         let (service_event_tx, server_event_rx) = mpsc::channel::<ServiceEvent>(32); | ||||||
|  |  | ||||||
|  |         Self { | ||||||
|  |             request_pool: Default::default(), | ||||||
|  |             request_tx, | ||||||
|  |             request_rx: Arc::new(Mutex::new(request_rx)), | ||||||
|  |             service_event_tx, | ||||||
|  |             service_event_rx: Arc::new(Mutex::new(server_event_rx)), | ||||||
|  |             is_online: Default::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Client { | ||||||
|  |     pub async fn is_online(&self) -> bool { | ||||||
|  |         *self.is_online.read().await | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn subscribe_status(&self, callback: impl Fn(ClientStatus) + Send + Sync + 'static) { | ||||||
|  |         let service_event_rx = self.service_event_rx.clone(); | ||||||
|  |  | ||||||
|  |         tokio::spawn(async move { | ||||||
|  |             loop { | ||||||
|  |                 let mut server_event_rx = service_event_rx.lock().await; | ||||||
|  |                 if let Some(server_event) = server_event_rx.recv().await { | ||||||
|  |                     match server_event { | ||||||
|  |                         ServiceEvent::Online => { | ||||||
|  |                             callback(ClientStatus::Service(true)); | ||||||
|  |                         } | ||||||
|  |                         ServiceEvent::Offline => { | ||||||
|  |                             callback(ClientStatus::Service(false)); | ||||||
|  |                         } | ||||||
|  |                         ServiceEvent::Response(response) => { | ||||||
|  |                             if let ResponseData::Status(vpn_status) = response.data() { | ||||||
|  |                                 callback(ClientStatus::Vpn(vpn_status)); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn run(&self) { | ||||||
|  |         info!("Connecting to the background service..."); | ||||||
|  |  | ||||||
|  |         // TODO exit the loop properly | ||||||
|  |         loop { | ||||||
|  |             match self.connect_to_server().await { | ||||||
|  |                 Ok(_) => { | ||||||
|  |                     debug!("Disconnected from server, reconnecting..."); | ||||||
|  |                 } | ||||||
|  |                 Err(err) => { | ||||||
|  |                     debug!("Error connecting to server, retrying, error: {:?}", err) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // wait for a second before trying to reconnect | ||||||
|  |             tokio::time::sleep(std::time::Duration::from_secs(1)).await; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn connect_to_server(&self) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |         let stream = UnixStream::connect(SOCKET_PATH).await?; | ||||||
|  |         let (read_stream, write_stream) = io::split(stream); | ||||||
|  |         let cancel_token = CancellationToken::new(); | ||||||
|  |  | ||||||
|  |         let read_handle = tokio::spawn(handle_read( | ||||||
|  |             read_stream, | ||||||
|  |             self.request_pool.clone(), | ||||||
|  |             self.service_event_tx.clone(), | ||||||
|  |             cancel_token.clone(), | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |         let write_handle = tokio::spawn(handle_write( | ||||||
|  |             write_stream, | ||||||
|  |             self.request_rx.clone(), | ||||||
|  |             cancel_token, | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |         *self.is_online.write().await = true; | ||||||
|  |         info!("Connected to the background service"); | ||||||
|  |         if let Err(err) = self.service_event_tx.send(ServiceEvent::Online).await { | ||||||
|  |             warn!("Error sending online event to the channel: {}", err); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let _ = tokio::join!(read_handle, write_handle); | ||||||
|  |         *self.is_online.write().await = false; | ||||||
|  |  | ||||||
|  |         // TODO connection was lost, cleanup the request pool | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn send_command<T: TryFrom<ResponseData>>( | ||||||
|  |         &self, | ||||||
|  |         payload: CommandPayload, | ||||||
|  |     ) -> Result<T, ServerApiError> { | ||||||
|  |         if !*self.is_online.read().await { | ||||||
|  |             return Err("Background service is not running".into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let (request, response_rx) = self.request_pool.create_request(payload).await; | ||||||
|  |  | ||||||
|  |         if let Err(err) = self.request_tx.send(request).await { | ||||||
|  |             return Err(format!("Error sending request to the channel: {}", err).into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         response_rx | ||||||
|  |             .await | ||||||
|  |             .map_err(|_| "Error receiving response from the channel".into()) | ||||||
|  |             .and_then(|response| { | ||||||
|  |                 if response.success() { | ||||||
|  |                     response | ||||||
|  |                         .data() | ||||||
|  |                         .try_into() | ||||||
|  |                         .map_err(|_| "Error parsing response data".into()) | ||||||
|  |                 } else { | ||||||
|  |                     Err(response.message().into()) | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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> { | ||||||
|  |         self.send_command(Disconnect.into()).await | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn status(&self) -> Result<VpnStatus, ServerApiError> { | ||||||
|  |         self.send_command(GetStatus.into()).await | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn handle_read( | ||||||
|  |     read_stream: ReadHalf<UnixStream>, | ||||||
|  |     request_pool: Arc<RequestPool>, | ||||||
|  |     service_event_tx: mpsc::Sender<ServiceEvent>, | ||||||
|  |     cancel_token: CancellationToken, | ||||||
|  | ) { | ||||||
|  |     let mut reader: Reader = read_stream.into(); | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         match reader.read_multiple::<Response>().await { | ||||||
|  |             Ok(responses) => { | ||||||
|  |                 for response in responses { | ||||||
|  |                     match response.request_id() { | ||||||
|  |                         Some(id) => request_pool.complete_request(id, response).await, | ||||||
|  |                         None => { | ||||||
|  |                             if let Err(err) = service_event_tx.send(response.into()).await { | ||||||
|  |                                 warn!("Error sending response to output channel: {}", err); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Err(err) if err.kind() == io::ErrorKind::ConnectionAborted => { | ||||||
|  |                 warn!("Disconnected from the background service"); | ||||||
|  |                 if let Err(err) = service_event_tx.send(ServiceEvent::Offline).await { | ||||||
|  |                     warn!( | ||||||
|  |                         "Error sending server disconnected event to channel: {}", | ||||||
|  |                         err | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 cancel_token.cancel(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             Err(err) => { | ||||||
|  |                 warn!("Error reading from server: {}", err); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn handle_write( | ||||||
|  |     write_stream: WriteHalf<UnixStream>, | ||||||
|  |     request_rx: Arc<Mutex<mpsc::Receiver<Request>>>, | ||||||
|  |     cancel_token: CancellationToken, | ||||||
|  | ) { | ||||||
|  |     let mut writer: Writer = write_stream.into(); | ||||||
|  |     loop { | ||||||
|  |         let mut request_rx = request_rx.lock().await; | ||||||
|  |         tokio::select! { | ||||||
|  |             Some(request) = request_rx.recv() => { | ||||||
|  |                 if let Err(err) = writer.write(&request).await { | ||||||
|  |                     warn!("Error writing to server: {}", err); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ = cancel_token.cancelled() => { | ||||||
|  |                 info!("The read loop has been cancelled, exiting the write loop"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             else => { | ||||||
|  |                 warn!("Error reading command from channel"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								gpcommon/src/cmd/connect.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								gpcommon/src/cmd/connect.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | use super::{Command, CommandContext, CommandError}; | ||||||
|  | use crate::{ResponseData, VpnStatus}; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
|  | pub struct Connect { | ||||||
|  |     server: String, | ||||||
|  |     cookie: String, | ||||||
|  |     user_agent: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Connect { | ||||||
|  |     pub fn new(server: String, cookie: String, user_agent: String) -> Self { | ||||||
|  |         Self { | ||||||
|  |             server, | ||||||
|  |             cookie, | ||||||
|  |             user_agent, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
|  | impl Command for Connect { | ||||||
|  |     async fn handle(&self, context: CommandContext) -> Result<ResponseData, CommandError> { | ||||||
|  |         let vpn = context.server_context.vpn(); | ||||||
|  |         let status = vpn.status().await; | ||||||
|  |  | ||||||
|  |         if status != VpnStatus::Disconnected { | ||||||
|  |             return Err(format!("VPN is already in state: {:?}", status).into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if let Err(err) = vpn.connect(&self.server, &self.cookie, &self.user_agent).await { | ||||||
|  |             return Err(err.to_string().into()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(ResponseData::Empty) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								gpcommon/src/cmd/disconnect.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								gpcommon/src/cmd/disconnect.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | use super::{Command, CommandContext, CommandError}; | ||||||
|  | use crate::ResponseData; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
|  | pub struct Disconnect; | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
|  | impl Command for Disconnect { | ||||||
|  |     async fn handle(&self, context: CommandContext) -> Result<ResponseData, CommandError> { | ||||||
|  |         context.server_context.vpn().disconnect().await; | ||||||
|  |         Ok(ResponseData::Empty) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								gpcommon/src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								gpcommon/src/cmd/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | use crate::{response::ResponseData, server::ServerContext}; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use core::fmt::Debug; | ||||||
|  | use std::{ | ||||||
|  |     fmt::{self, Display}, | ||||||
|  |     sync::Arc, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | mod connect; | ||||||
|  | mod disconnect; | ||||||
|  | mod status; | ||||||
|  |  | ||||||
|  | pub use connect::Connect; | ||||||
|  | pub use disconnect::Disconnect; | ||||||
|  | pub use status::GetStatus; | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub(crate) struct CommandContext { | ||||||
|  |     server_context: Arc<ServerContext>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Arc<ServerContext>> for CommandContext { | ||||||
|  |     fn from(server_context: Arc<ServerContext>) -> Self { | ||||||
|  |         Self { server_context } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub(crate) struct CommandError { | ||||||
|  |     message: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<String> for CommandError { | ||||||
|  |     fn from(message: String) -> Self { | ||||||
|  |         Self { message } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Display for CommandError { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         write!(f, "CommandError {:#?}", self.message) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
|  | pub(crate) trait Command: Send + Sync { | ||||||
|  |     async fn handle(&self, context: CommandContext) -> Result<ResponseData, CommandError>; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Debug for dyn Command { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         write!(f, "Command") | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								gpcommon/src/cmd/status.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								gpcommon/src/cmd/status.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | use super::{Command, CommandContext, CommandError}; | ||||||
|  | use crate::ResponseData; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
|  | pub struct GetStatus; | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
|  | impl Command for GetStatus { | ||||||
|  |     async fn handle(&self, context: CommandContext) -> Result<ResponseData, CommandError> { | ||||||
|  |         let status = context.server_context.vpn().status().await; | ||||||
|  |  | ||||||
|  |         Ok(ResponseData::Status(status)) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										179
									
								
								gpcommon/src/connection.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								gpcommon/src/connection.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | use crate::request::Request; | ||||||
|  | use crate::server::ServerContext; | ||||||
|  | use crate::Reader; | ||||||
|  | use crate::Response; | ||||||
|  | use crate::ResponseData; | ||||||
|  | use crate::VpnStatus; | ||||||
|  | use crate::Writer; | ||||||
|  | use log::{debug, info, warn}; | ||||||
|  | use std::sync::Arc; | ||||||
|  | use tokio::io::{self, ReadHalf, WriteHalf}; | ||||||
|  | use tokio::net::UnixStream; | ||||||
|  | use tokio::sync::{mpsc, watch}; | ||||||
|  | use tokio_util::sync::CancellationToken; | ||||||
|  |  | ||||||
|  | async fn handle_read( | ||||||
|  |     read_stream: ReadHalf<UnixStream>, | ||||||
|  |     server_context: Arc<ServerContext>, | ||||||
|  |     response_tx: mpsc::Sender<Response>, | ||||||
|  |     peer_pid: Option<i32>, | ||||||
|  |     cancel_token: CancellationToken, | ||||||
|  | ) { | ||||||
|  |     let mut reader: Reader = read_stream.into(); | ||||||
|  |     let mut authenticated: Option<bool> = None; | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         match reader.read_multiple::<Request>().await { | ||||||
|  |             Ok(requests) => { | ||||||
|  |                 if authenticated.is_none() { | ||||||
|  |                     authenticated = Some(authenticate(peer_pid)); | ||||||
|  |                 } | ||||||
|  |                 if !authenticated.unwrap_or(false) { | ||||||
|  |                     warn!("Client not authenticated, closing connection"); | ||||||
|  |                     cancel_token.cancel(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 for request in requests { | ||||||
|  |                     debug!("Received client request: {:?}", request); | ||||||
|  |  | ||||||
|  |                     let command = request.command(); | ||||||
|  |                     let context = server_context.clone().into(); | ||||||
|  |  | ||||||
|  |                     let mut response = match command.handle(context).await { | ||||||
|  |                         Ok(data) => Response::from(data), | ||||||
|  |                         Err(err) => Response::from(err.to_string()), | ||||||
|  |                     }; | ||||||
|  |                     response.set_request_id(request.id()); | ||||||
|  |  | ||||||
|  |                     let _ = response_tx.send(response).await; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Err(err) if err.kind() == io::ErrorKind::ConnectionAborted => { | ||||||
|  |                 info!("Client disconnected"); | ||||||
|  |                 cancel_token.cancel(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Err(err) => { | ||||||
|  |                 warn!("Error receiving request: {:?}", err); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn handle_write( | ||||||
|  |     write_stream: WriteHalf<UnixStream>, | ||||||
|  |     mut response_rx: mpsc::Receiver<Response>, | ||||||
|  |     cancel_token: CancellationToken, | ||||||
|  | ) { | ||||||
|  |     let mut writer: Writer = write_stream.into(); | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         tokio::select! { | ||||||
|  |             Some(response) = response_rx.recv() => { | ||||||
|  |                 debug!("Sending response: {:?}", response); | ||||||
|  |                 if let Err(err) = writer.write(&response).await { | ||||||
|  |                     warn!("Error sending response: {:?}", err); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ = cancel_token.cancelled() => { | ||||||
|  |                 info!("Exiting the write loop"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             else => { | ||||||
|  |                 warn!("Error receiving response from channel"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn handle_status_change( | ||||||
|  |     mut status_rx: watch::Receiver<VpnStatus>, | ||||||
|  |     response_tx: mpsc::Sender<Response>, | ||||||
|  |     cancel_token: CancellationToken, | ||||||
|  | ) { | ||||||
|  |     // Send the initial status | ||||||
|  |     send_status(&status_rx, &response_tx).await; | ||||||
|  |     debug!("Waiting for status change"); | ||||||
|  |     let start_time = std::time::Instant::now(); | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         tokio::select! { | ||||||
|  |             _ = status_rx.changed() => { | ||||||
|  |                 debug!("Status changed: {:?}", start_time.elapsed()); | ||||||
|  |                 send_status(&status_rx, &response_tx).await; | ||||||
|  |             } | ||||||
|  |             _ = cancel_token.cancelled() => { | ||||||
|  |                 info!("Exiting the status loop"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             else => { | ||||||
|  |                 warn!("Error receiving status from channel"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn send_status(status_rx: &watch::Receiver<VpnStatus>, response_tx: &mpsc::Sender<Response>) { | ||||||
|  |     let status = *status_rx.borrow(); | ||||||
|  |     if let Err(err) = response_tx | ||||||
|  |         .send(Response::from(ResponseData::Status(status))) | ||||||
|  |         .await | ||||||
|  |     { | ||||||
|  |         warn!("Error sending status: {:?}", err); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub(crate) async fn handle_connection(socket: UnixStream, context: Arc<ServerContext>) { | ||||||
|  |     let peer_pid = peer_pid(&socket); | ||||||
|  |     let (read_stream, write_stream) = io::split(socket); | ||||||
|  |     let (response_tx, response_rx) = mpsc::channel::<Response>(32); | ||||||
|  |     let cancel_token = CancellationToken::new(); | ||||||
|  |     let status_rx = context.vpn().status_rx().await; | ||||||
|  |  | ||||||
|  |     // Read requests from the client | ||||||
|  |     let read_handle = tokio::spawn(handle_read( | ||||||
|  |         read_stream, | ||||||
|  |         context.clone(), | ||||||
|  |         response_tx.clone(), | ||||||
|  |         peer_pid, | ||||||
|  |         cancel_token.clone(), | ||||||
|  |     )); | ||||||
|  |  | ||||||
|  |     // Write responses to the client | ||||||
|  |     let write_handle = tokio::spawn(handle_write( | ||||||
|  |         write_stream, | ||||||
|  |         response_rx, | ||||||
|  |         cancel_token.clone(), | ||||||
|  |     )); | ||||||
|  |  | ||||||
|  |     // Watch for status changes | ||||||
|  |     let status_handle = tokio::spawn(handle_status_change( | ||||||
|  |         status_rx, | ||||||
|  |         response_tx.clone(), | ||||||
|  |         cancel_token, | ||||||
|  |     )); | ||||||
|  |  | ||||||
|  |     let _ = tokio::join!(read_handle, write_handle, status_handle); | ||||||
|  |  | ||||||
|  |     debug!("Client connection closed"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn peer_pid(socket: &UnixStream) -> Option<i32> { | ||||||
|  |     match socket.peer_cred() { | ||||||
|  |         Ok(ucred) => ucred.pid(), | ||||||
|  |         Err(_) => None, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TODO - Implement authentication | ||||||
|  | fn authenticate(peer_pid: Option<i32>) -> bool { | ||||||
|  |     if let Some(pid) = peer_pid { | ||||||
|  |         info!("Peer PID: {}", pid); | ||||||
|  |         true | ||||||
|  |     } else { | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								gpcommon/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								gpcommon/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | use data_encoding::HEXUPPER; | ||||||
|  | use ring::digest::{Context, SHA256}; | ||||||
|  | use std::{ | ||||||
|  |     fs::File, | ||||||
|  |     io::{BufReader, Read}, | ||||||
|  |     path::Path, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | pub const SOCKET_PATH: &str = "/tmp/gpservice.sock"; | ||||||
|  |  | ||||||
|  | mod client; | ||||||
|  | mod cmd; | ||||||
|  | mod connection; | ||||||
|  | mod reader; | ||||||
|  | mod request; | ||||||
|  | mod response; | ||||||
|  | pub mod server; | ||||||
|  | mod vpn; | ||||||
|  | mod writer; | ||||||
|  |  | ||||||
|  | pub(crate) use request::Request; | ||||||
|  | pub(crate) use request::RequestPool; | ||||||
|  |  | ||||||
|  | pub use response::Response; | ||||||
|  | pub use response::ResponseData; | ||||||
|  | pub use response::TryFromResponseDataError; | ||||||
|  |  | ||||||
|  | pub(crate) use reader::Reader; | ||||||
|  | pub(crate) use writer::Writer; | ||||||
|  |  | ||||||
|  | pub use client::Client; | ||||||
|  | pub use client::ServerApiError; | ||||||
|  | pub use client::ClientStatus; | ||||||
|  | pub use vpn::VpnStatus; | ||||||
|  |  | ||||||
|  | pub fn sha256_digest<P: AsRef<Path>>(file_path: P) -> Result<String, std::io::Error> { | ||||||
|  |     let input = File::open(file_path)?; | ||||||
|  |     let mut reader = BufReader::new(input); | ||||||
|  |  | ||||||
|  |     let mut context = Context::new(&SHA256); | ||||||
|  |     let mut buffer = [0; 1024]; | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         let count = reader.read(&mut buffer)?; | ||||||
|  |         if count == 0 { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         context.update(&buffer[..count]); | ||||||
|  |     } | ||||||
|  |     Ok(HEXUPPER.encode(context.finish().as_ref())) | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								gpcommon/src/reader.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								gpcommon/src/reader.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | use serde::Deserialize; | ||||||
|  | use tokio::io::{self, AsyncReadExt, ReadHalf}; | ||||||
|  | use tokio::net::UnixStream; | ||||||
|  |  | ||||||
|  | pub(crate) struct Reader { | ||||||
|  |     stream: ReadHalf<UnixStream>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<ReadHalf<UnixStream>> for Reader { | ||||||
|  |     fn from(stream: ReadHalf<UnixStream>) -> Self { | ||||||
|  |         Self { stream } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Reader { | ||||||
|  |     pub async fn read_multiple<T: for<'a> Deserialize<'a>>(&mut self) -> Result<Vec<T>, io::Error> { | ||||||
|  |         let mut buffer = [0; 2048]; | ||||||
|  |  | ||||||
|  |         match self.stream.read(&mut buffer).await { | ||||||
|  |             Ok(0) => Err(io::Error::new( | ||||||
|  |                 io::ErrorKind::ConnectionAborted, | ||||||
|  |                 "Peer disconnected", | ||||||
|  |             )), | ||||||
|  |             Ok(bytes_read) => { | ||||||
|  |                 let response_str = String::from_utf8_lossy(&buffer[..bytes_read]); | ||||||
|  |                 let responses: Vec<&str> = response_str.split("\n\n").collect(); | ||||||
|  |                 let responses = responses | ||||||
|  |                     .iter() | ||||||
|  |                     .filter_map(|r| { | ||||||
|  |                         if !r.is_empty() { | ||||||
|  |                             serde_json::from_str(r).ok() | ||||||
|  |                         } else { | ||||||
|  |                             None | ||||||
|  |                         } | ||||||
|  |                     }) | ||||||
|  |                     .collect::<Vec<T>>(); | ||||||
|  |  | ||||||
|  |                 Ok(responses) | ||||||
|  |             } | ||||||
|  |             Err(err) => Err(err), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										105
									
								
								gpcommon/src/request.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								gpcommon/src/request.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | use crate::cmd::{Command, Connect, Disconnect, GetStatus}; | ||||||
|  | use crate::Response; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::sync::Arc; | ||||||
|  | use tokio::sync::{oneshot, RwLock}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize)] | ||||||
|  | pub(crate) struct Request { | ||||||
|  |     id: u64, | ||||||
|  |     payload: CommandPayload, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Request { | ||||||
|  |     fn new(id: u64, payload: CommandPayload) -> Self { | ||||||
|  |         Self { id, payload } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn id(&self) -> u64 { | ||||||
|  |         self.id | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn command(&self) -> Box<dyn Command> { | ||||||
|  |         match &self.payload { | ||||||
|  |             CommandPayload::GetStatus(status) => Box::new(status.clone()), | ||||||
|  |             CommandPayload::Connect(connect) => Box::new(connect.clone()), | ||||||
|  |             CommandPayload::Disconnect(disconnect) => Box::new(disconnect.clone()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize)] | ||||||
|  | pub(crate) enum CommandPayload { | ||||||
|  |     GetStatus(GetStatus), | ||||||
|  |     Connect(Connect), | ||||||
|  |     Disconnect(Disconnect), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<GetStatus> for CommandPayload { | ||||||
|  |     fn from(status: GetStatus) -> Self { | ||||||
|  |         Self::GetStatus(status) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Connect> for CommandPayload { | ||||||
|  |     fn from(connect: Connect) -> Self { | ||||||
|  |         Self::Connect(connect) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Disconnect> for CommandPayload { | ||||||
|  |     fn from(disconnect: Disconnect) -> Self { | ||||||
|  |         Self::Disconnect(disconnect) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct RequestHandle { | ||||||
|  |     id: u64, | ||||||
|  |     response_tx: oneshot::Sender<Response>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | struct IdGenerator { | ||||||
|  |     current_id: u64, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl IdGenerator { | ||||||
|  |     fn next(&mut self) -> u64 { | ||||||
|  |         let current_id = self.current_id; | ||||||
|  |         self.current_id = self.current_id.wrapping_add(1); | ||||||
|  |         current_id | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub(crate) struct RequestPool { | ||||||
|  |     id_generator: Arc<RwLock<IdGenerator>>, | ||||||
|  |     request_handles: Arc<RwLock<Vec<RequestHandle>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl RequestPool { | ||||||
|  |     pub async fn create_request( | ||||||
|  |         &self, | ||||||
|  |         payload: CommandPayload, | ||||||
|  |     ) -> (Request, oneshot::Receiver<Response>) { | ||||||
|  |         let id = self.id_generator.write().await.next(); | ||||||
|  |         let (response_tx, response_rx) = oneshot::channel(); | ||||||
|  |         let request_handle = RequestHandle { id, response_tx }; | ||||||
|  |  | ||||||
|  |         self.request_handles.write().await.push(request_handle); | ||||||
|  |         (Request::new(id, payload), response_rx) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn complete_request(&self, id: u64, response: Response) { | ||||||
|  |         let mut request_handles = self.request_handles.write().await; | ||||||
|  |         let request_handle = request_handles | ||||||
|  |             .iter() | ||||||
|  |             .position(|handle| handle.id == id) | ||||||
|  |             .map(|index| request_handles.remove(index)); | ||||||
|  |  | ||||||
|  |         if let Some(request_handle) = request_handle { | ||||||
|  |             let _ = request_handle.response_tx.send(response); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								gpcommon/src/response.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								gpcommon/src/response.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | |||||||
|  | use crate::vpn::VpnStatus; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, Clone)] | ||||||
|  | pub struct Response { | ||||||
|  |     request_id: Option<u64>, | ||||||
|  |     success: bool, | ||||||
|  |     message: String, | ||||||
|  |     data: ResponseData, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<ResponseData> for Response { | ||||||
|  |     fn from(data: ResponseData) -> Self { | ||||||
|  |         Self { | ||||||
|  |             request_id: None, | ||||||
|  |             success: true, | ||||||
|  |             message: String::from("Success"), | ||||||
|  |             data, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<String> for Response { | ||||||
|  |     fn from(message: String) -> Self { | ||||||
|  |         Self { | ||||||
|  |             request_id: None, | ||||||
|  |             success: false, | ||||||
|  |             message, | ||||||
|  |             data: ResponseData::Empty, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Response { | ||||||
|  |     pub fn success(&self) -> bool { | ||||||
|  |         self.success | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn message(&self) -> &str { | ||||||
|  |         &self.message | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn set_request_id(&mut self, command_id: u64) { | ||||||
|  |         self.request_id = Some(command_id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn request_id(&self) -> Option<u64> { | ||||||
|  |         self.request_id | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn data(&self) -> ResponseData { | ||||||
|  |         self.data | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, Clone, Copy)] | ||||||
|  | pub enum ResponseData { | ||||||
|  |     Status(VpnStatus), | ||||||
|  |     Empty, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<VpnStatus> for ResponseData { | ||||||
|  |     fn from(status: VpnStatus) -> Self { | ||||||
|  |         Self::Status(status) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<()> for ResponseData { | ||||||
|  |     fn from(_: ()) -> Self { | ||||||
|  |         Self::Empty | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct TryFromResponseDataError { | ||||||
|  |     message: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl std::fmt::Display for TryFromResponseDataError { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "Invalid ResponseData: {}", self.message) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<&str> for TryFromResponseDataError { | ||||||
|  |     fn from(message: &str) -> Self { | ||||||
|  |         Self { | ||||||
|  |             message: message.into(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<ResponseData> for VpnStatus { | ||||||
|  |     type Error = TryFromResponseDataError; | ||||||
|  |  | ||||||
|  |     fn try_from(value: ResponseData) -> Result<Self, Self::Error> { | ||||||
|  |         match value { | ||||||
|  |             ResponseData::Status(status) => Ok(status), | ||||||
|  |             _ => Err("ResponseData is not a VpnStatus".into()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<ResponseData> for () { | ||||||
|  |     type Error = TryFromResponseDataError; | ||||||
|  |  | ||||||
|  |     fn try_from(value: ResponseData) -> Result<Self, Self::Error> { | ||||||
|  |         match value { | ||||||
|  |             ResponseData::Empty => Ok(()), | ||||||
|  |             _ => Err("ResponseData is not empty".into()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										91
									
								
								gpcommon/src/server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								gpcommon/src/server.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | use crate::{connection::handle_connection, vpn::Vpn}; | ||||||
|  | use log::{warn, info}; | ||||||
|  | use std::{future::Future, os::unix::prelude::PermissionsExt, path::Path, sync::Arc}; | ||||||
|  | use tokio::fs; | ||||||
|  | use tokio::net::{UnixListener, UnixStream}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub(crate) struct ServerContext { | ||||||
|  |     vpn: Arc<Vpn>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Server { | ||||||
|  |     socket_path: String, | ||||||
|  |     context: Arc<ServerContext>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl ServerContext { | ||||||
|  |     pub fn vpn(&self) -> Arc<Vpn> { | ||||||
|  |         self.vpn.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Server { | ||||||
|  |     fn new(socket_path: String) -> Self { | ||||||
|  |         Self { | ||||||
|  |             socket_path, | ||||||
|  |             context: Default::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Check if an instance of the server is already running. | ||||||
|  |     // by trying to connect to the socket. | ||||||
|  |     async fn is_running(&self) -> bool { | ||||||
|  |         UnixStream::connect(&self.socket_path).await.is_ok() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn start(&self) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |         if Path::new(&self.socket_path).exists() { | ||||||
|  |             fs::remove_file(&self.socket_path).await?; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let listener = UnixListener::bind(&self.socket_path)?; | ||||||
|  |         info!("Listening on socket: {:?}", listener.local_addr()?); | ||||||
|  |  | ||||||
|  |         let metadata = fs::metadata(&self.socket_path).await?; | ||||||
|  |         let mut permissions = metadata.permissions(); | ||||||
|  |         permissions.set_mode(0o666); | ||||||
|  |         fs::set_permissions(&self.socket_path, permissions).await?; | ||||||
|  |  | ||||||
|  |         loop { | ||||||
|  |             match listener.accept().await { | ||||||
|  |                 Ok((socket, _)) => { | ||||||
|  |                     info!("Accepted connection: {:?}", socket.peer_addr()?); | ||||||
|  |                     tokio::spawn(handle_connection(socket, self.context.clone())); | ||||||
|  |                 } | ||||||
|  |                 Err(err) => { | ||||||
|  |                     warn!("Error accepting connection: {:?}", err); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn stop(&self) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |         self.context.vpn().disconnect().await; | ||||||
|  |         fs::remove_file(&self.socket_path).await?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub async fn run( | ||||||
|  |     socket_path: &str, | ||||||
|  |     shutdown: impl Future, | ||||||
|  | ) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |     let server = Server::new(socket_path.to_string()); | ||||||
|  |  | ||||||
|  |     if server.is_running().await { | ||||||
|  |         return Err("Another instance of the server is already running".into()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     tokio::select! { | ||||||
|  |         res = server.start() => { | ||||||
|  |             res? | ||||||
|  |         }, | ||||||
|  |         _ = shutdown => { | ||||||
|  |             info!("Shutting down the server..."); | ||||||
|  |             server.stop().await?; | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								gpcommon/src/vpn/ffi.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								gpcommon/src/vpn/ffi.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | use log::{debug, info, trace, warn}; | ||||||
|  | use std::ffi::{c_char, c_int, c_void}; | ||||||
|  | use tokio::sync::mpsc; | ||||||
|  |  | ||||||
|  | #[repr(C)] | ||||||
|  | #[derive(Debug, Copy, Clone)] | ||||||
|  | pub(crate) struct Options { | ||||||
|  |     pub server: *const c_char, | ||||||
|  |     pub cookie: *const c_char, | ||||||
|  |     pub user_agent: *const c_char, | ||||||
|  |     pub user_data: *mut c_void, | ||||||
|  |  | ||||||
|  |     pub script: *const c_char, | ||||||
|  |     pub certificate: *const c_char, | ||||||
|  |     pub servercert: *const c_char, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[link(name = "vpn")] | ||||||
|  | extern "C" { | ||||||
|  |     #[link_name = "vpn_connect"] | ||||||
|  |     pub(crate) fn connect(options: *const Options) -> c_int; | ||||||
|  |  | ||||||
|  |     #[link_name = "vpn_disconnect"] | ||||||
|  |     pub(crate) fn disconnect(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[no_mangle] | ||||||
|  | extern "C" fn on_vpn_connected(value: i32, sender: *mut c_void) { | ||||||
|  |     let sender = unsafe { &*(sender as *const mpsc::Sender<i32>) }; | ||||||
|  |     sender | ||||||
|  |         .blocking_send(value) | ||||||
|  |         .expect("Failed to send VPN connection code"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Logger used in the C code. | ||||||
|  | // 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 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 | ||||||
|  |     let message = message.trim_end_matches('\n'); | ||||||
|  |  | ||||||
|  |     if level == 0 { | ||||||
|  |         warn!("{}", message); | ||||||
|  |     } else if level == 1 { | ||||||
|  |         info!("{}", message); | ||||||
|  |     } else if level == 2 { | ||||||
|  |         debug!("{}", message); | ||||||
|  |     } else if level == 3 { | ||||||
|  |         trace!("{}", message); | ||||||
|  |     } else { | ||||||
|  |         warn!( | ||||||
|  |             "Unknown log level: {}, enable DEBUG log level to see more details", | ||||||
|  |             level | ||||||
|  |         ); | ||||||
|  |         debug!("{}", message); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										164
									
								
								gpcommon/src/vpn/gpconf.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								gpcommon/src/vpn/gpconf.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | |||||||
|  | use anyhow::{Error, Ok, Result}; | ||||||
|  | use configparser::ini::Ini; | ||||||
|  | use lexopt::Parser; | ||||||
|  | use log::warn; | ||||||
|  |  | ||||||
|  | const GPCONF_PATH: &str = "/etc/gpservice/gp.conf"; | ||||||
|  | const DEFAULT_SECTION: &str = "*"; | ||||||
|  | const PROP_OPENCONNECT_ARGS: &str = "openconnect-args"; | ||||||
|  |  | ||||||
|  | /// A struct representing the CLI arguments for the `openconnect` command. | ||||||
|  | /// Supports most of the options from the `openconnect` command line. | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub(crate) struct OpenconnectArgs { | ||||||
|  |     script: Option<String>, | ||||||
|  |     certificate: Option<String>, | ||||||
|  |     servercert: Option<String>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl OpenconnectArgs { | ||||||
|  |     pub fn script(&self) -> Option<String> { | ||||||
|  |         self.script.to_owned() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn certificate(&self) -> Option<String> { | ||||||
|  |         self.certificate.to_owned() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn servercert(&self) -> Option<String> { | ||||||
|  |         self.servercert.to_owned() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Read the `gp.conf` file and return the `openconnect` arguments for the given server. | ||||||
|  | /// If the server is not found, the default section is used. | ||||||
|  | pub(crate) fn read_conf(server: &str) -> Result<OpenconnectArgs> { | ||||||
|  |     read_conf_from(GPCONF_PATH, server) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Private function to read the `openconnect` arguments for the given server from a given path. | ||||||
|  | /// Make it easy to write tests. | ||||||
|  | fn read_conf_from(path: &str, server: &str) -> Result<OpenconnectArgs> { | ||||||
|  |     let mut config = Ini::new(); | ||||||
|  |  | ||||||
|  |     config.set_default_section(DEFAULT_SECTION); | ||||||
|  |     config.set_multiline(true); | ||||||
|  |  | ||||||
|  |     config.load(path).map_err(Error::msg)?; | ||||||
|  |  | ||||||
|  |     let default_openconnect_config = config | ||||||
|  |         .get(DEFAULT_SECTION, PROP_OPENCONNECT_ARGS) | ||||||
|  |         .unwrap_or_default(); | ||||||
|  |     let server_openconnect_config = config | ||||||
|  |         .get(server, PROP_OPENCONNECT_ARGS) | ||||||
|  |         .unwrap_or(default_openconnect_config); | ||||||
|  |  | ||||||
|  |     let args = shlex::split(&server_openconnect_config).unwrap_or_default(); | ||||||
|  |     parse_args(&args) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn parse_args(args: &Vec<String>) -> Result<OpenconnectArgs> { | ||||||
|  |     use lexopt::prelude::*; | ||||||
|  |  | ||||||
|  |     let mut parser = Parser::from_args(args); | ||||||
|  |  | ||||||
|  |     let mut script: Option<String> = None; | ||||||
|  |     let mut certificate: Option<String> = None; | ||||||
|  |     let mut servercert: Option<String> = None; | ||||||
|  |     while let Some(arg) = parser.next()? { | ||||||
|  |         match arg { | ||||||
|  |             Long("script") | Short('s') => { | ||||||
|  |                 script = Some(parser.value()?.parse()?); | ||||||
|  |             } | ||||||
|  |             Long("certificate") | Short('c') => { | ||||||
|  |                 certificate = Some(parser.value()?.parse()?); | ||||||
|  |             } | ||||||
|  |             Long("servercert") => { | ||||||
|  |                 servercert = Some(parser.value()?.parse()?); | ||||||
|  |             } | ||||||
|  |             _ => { | ||||||
|  |                 warn!("Ignoring unknown argument: {}", arg.unexpected()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(OpenconnectArgs { | ||||||
|  |         script, | ||||||
|  |         certificate, | ||||||
|  |         servercert, | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use std::io::Write; | ||||||
|  |     use tempfile::NamedTempFile; | ||||||
|  |  | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     // Macro to create a temporary file with the given content. | ||||||
|  |     macro_rules! tempfile { | ||||||
|  |         ($content:expr) => {{ | ||||||
|  |             let mut file = NamedTempFile::new().unwrap(); | ||||||
|  |             write!(file, $content).unwrap(); | ||||||
|  |             file | ||||||
|  |         }}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_config_not_found() { | ||||||
|  |         let args = read_conf_from("non-existent-file", "server"); | ||||||
|  |         assert!(args.is_err()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_default_config() { | ||||||
|  |         let file = tempfile!( | ||||||
|  |             r#" | ||||||
|  | [*] | ||||||
|  | openconnect-args=--script=/script.sh | ||||||
|  | "# | ||||||
|  |         ); | ||||||
|  |         let path = file.path().to_str().unwrap(); | ||||||
|  |         let args = read_conf_from(path, "any server").unwrap(); | ||||||
|  |  | ||||||
|  |         assert_eq!(args.script, Some("/script.sh".to_string())); | ||||||
|  |         assert_eq!(args.certificate, None); | ||||||
|  |         assert_eq!(args.servercert, None); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_server_config() { | ||||||
|  |         let file = tempfile!( | ||||||
|  |             r#" | ||||||
|  | [*] | ||||||
|  | openconnect-args=--script=/script.sh | ||||||
|  |  | ||||||
|  | [server] | ||||||
|  | openconnect-args=--certificate=/cert.pem | ||||||
|  | "# | ||||||
|  |         ); | ||||||
|  |         let path = file.path().to_str().unwrap(); | ||||||
|  |         let args = read_conf_from(path, "server").unwrap(); | ||||||
|  |  | ||||||
|  |         assert_eq!(args.script, None); | ||||||
|  |         assert_eq!(args.certificate, Some("/cert.pem".to_string())); | ||||||
|  |         assert_eq!(args.servercert, None); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_ignore_unknown_args() { | ||||||
|  |         let file = tempfile!( | ||||||
|  |             r#" | ||||||
|  | [*] | ||||||
|  | openconnect-args=--script=/script.sh --unknown-arg -c /cert.pem | ||||||
|  | "# | ||||||
|  |         ); | ||||||
|  |         let path = file.path().to_str().unwrap(); | ||||||
|  |         let args = read_conf_from(path, "server").unwrap(); | ||||||
|  |  | ||||||
|  |         assert_eq!(args.script, Some("/script.sh".to_string())); | ||||||
|  |         assert_eq!(args.certificate, Some("/cert.pem".to_string())); | ||||||
|  |         assert_eq!(args.servercert, None); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										145
									
								
								gpcommon/src/vpn/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								gpcommon/src/vpn/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | |||||||
|  | use self::vpn_options::VpnOptions; | ||||||
|  | use log::{debug, info, warn}; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::ffi::c_void; | ||||||
|  | use std::sync::Arc; | ||||||
|  | use std::thread; | ||||||
|  | use tokio::sync::watch; | ||||||
|  | use tokio::sync::{mpsc, Mutex}; | ||||||
|  |  | ||||||
|  | mod ffi; | ||||||
|  | mod gpconf; | ||||||
|  | mod vpn_options; | ||||||
|  | mod vpnc_script; | ||||||
|  |  | ||||||
|  | #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] | ||||||
|  | #[serde(rename_all = "lowercase")] | ||||||
|  | pub enum VpnStatus { | ||||||
|  |     Disconnected, | ||||||
|  |     Connecting, | ||||||
|  |     Connected, | ||||||
|  |     Disconnecting, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct StatusHolder { | ||||||
|  |     status: VpnStatus, | ||||||
|  |     status_tx: watch::Sender<VpnStatus>, | ||||||
|  |     status_rx: watch::Receiver<VpnStatus>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Default for StatusHolder { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         let (status_tx, status_rx) = watch::channel(VpnStatus::Disconnected); | ||||||
|  |  | ||||||
|  |         Self { | ||||||
|  |             status: VpnStatus::Disconnected, | ||||||
|  |             status_tx, | ||||||
|  |             status_rx, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl StatusHolder { | ||||||
|  |     fn set(&mut self, status: VpnStatus) { | ||||||
|  |         self.status = status; | ||||||
|  |         if let Err(err) = self.status_tx.send(status) { | ||||||
|  |             warn!("Failed to send VPN status: {}", err); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn status_rx(&self) -> watch::Receiver<VpnStatus> { | ||||||
|  |         self.status_rx.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub(crate) struct Vpn { | ||||||
|  |     status_holder: Arc<Mutex<StatusHolder>>, | ||||||
|  |     vpn_options: Arc<Mutex<Option<VpnOptions>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Vpn { | ||||||
|  |     pub async fn status_rx(&self) -> watch::Receiver<VpnStatus> { | ||||||
|  |         self.status_holder.lock().await.status_rx() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn connect( | ||||||
|  |         &self, | ||||||
|  |         server: &str, | ||||||
|  |         cookie: &str, | ||||||
|  |         user_agent: &str, | ||||||
|  |     ) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|  |         let mut options_builder = VpnOptions::builder(); | ||||||
|  |         let mut options_builder = options_builder | ||||||
|  |             .server(server) | ||||||
|  |             .cookie(cookie) | ||||||
|  |             .user_agent(user_agent); | ||||||
|  |  | ||||||
|  |         if let Ok(openconnect_args) = gpconf::read_conf(server) { | ||||||
|  |             info!("Found openconnect args in /etc/gpservice/gp.conf"); | ||||||
|  |             options_builder = options_builder.with_openconnect_args(openconnect_args); | ||||||
|  |         } | ||||||
|  |         // Save the VPN options so we can use them later, e.g. reconnect | ||||||
|  |         *self.vpn_options.lock().await = Some(options_builder.build()); | ||||||
|  |  | ||||||
|  |         let vpn_options = self.vpn_options.clone(); | ||||||
|  |         let status_holder = self.status_holder.clone(); | ||||||
|  |         let (vpn_tx, mut vpn_rx) = mpsc::channel::<i32>(1); | ||||||
|  |  | ||||||
|  |         thread::spawn(move || { | ||||||
|  |             let vpn_tx = &vpn_tx as *const _ as *mut c_void; | ||||||
|  |             let oc_options = vpn_options | ||||||
|  |                 .blocking_lock() | ||||||
|  |                 .as_ref() | ||||||
|  |                 .expect("Failed to unwrap vpn_options") | ||||||
|  |                 .as_oc_options(vpn_tx); | ||||||
|  |  | ||||||
|  |             // Start the VPN connection, this will block until the connection is closed | ||||||
|  |             status_holder.blocking_lock().set(VpnStatus::Connecting); | ||||||
|  |             let ret = unsafe { ffi::connect(&oc_options) }; | ||||||
|  |  | ||||||
|  |             info!("VPN connection closed with code: {}", ret); | ||||||
|  |             status_holder.blocking_lock().set(VpnStatus::Disconnected); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         info!("Waiting for the VPN connection..."); | ||||||
|  |  | ||||||
|  |         if let Some(cmd_pipe_fd) = vpn_rx.recv().await { | ||||||
|  |             info!("VPN connection started, cmd_pipe_fd: {}", cmd_pipe_fd); | ||||||
|  |             self.status_holder.lock().await.set(VpnStatus::Connected); | ||||||
|  |         } else { | ||||||
|  |             warn!("VPN connection failed to start"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn disconnect(&self) { | ||||||
|  |         if self.status().await == VpnStatus::Disconnected { | ||||||
|  |             info!("VPN is not connected, nothing to do"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         info!("Disconnecting VPN..."); | ||||||
|  |         self.status_holder | ||||||
|  |             .lock() | ||||||
|  |             .await | ||||||
|  |             .set(VpnStatus::Disconnecting); | ||||||
|  |         unsafe { ffi::disconnect() }; | ||||||
|  |  | ||||||
|  |         let mut status_rx = self.status_rx().await; | ||||||
|  |         debug!("Waiting for the VPN to disconnect..."); | ||||||
|  |  | ||||||
|  |         while status_rx.changed().await.is_ok() { | ||||||
|  |             if *status_rx.borrow() == VpnStatus::Disconnected { | ||||||
|  |                 info!("VPN disconnected"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn status(&self) -> VpnStatus { | ||||||
|  |         self.status_holder.lock().await.status | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								gpcommon/src/vpn/vpn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								gpcommon/src/vpn/vpn.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/utsname.h> | ||||||
|  | #include <openconnect.h> | ||||||
|  |  | ||||||
|  | #include "vpn.h" | ||||||
|  |  | ||||||
|  | void *g_user_data; | ||||||
|  |  | ||||||
|  | static int g_cmd_pipe_fd; | ||||||
|  | const char *g_vpnc_script; | ||||||
|  |  | ||||||
|  | /* Validate the peer certificate */ | ||||||
|  | static int validate_peer_cert(__attribute__((unused)) void *_vpninfo, const char *reason) | ||||||
|  | { | ||||||
|  |     INFO("Validating peer cert: %s", reason); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Print progress messages */ | ||||||
|  | static void print_progress(__attribute__((unused)) void *_vpninfo, int level, const char *format, ...) | ||||||
|  | { | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, format); | ||||||
|  |     char *message = format_message(format, args); | ||||||
|  |     va_end(args); | ||||||
|  |  | ||||||
|  |     if (message == NULL) | ||||||
|  |     { | ||||||
|  |         ERROR("Failed to format log message"); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         LOG(level, message); | ||||||
|  |         free(message); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setup_tun_handler(void *_vpninfo) | ||||||
|  | { | ||||||
|  |     openconnect_setup_tun_device(_vpninfo, g_vpnc_script, NULL); | ||||||
|  |     on_vpn_connected(g_cmd_pipe_fd, g_user_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initialize VPN connection */ | ||||||
|  | int vpn_connect(const vpn_options *options) | ||||||
|  | { | ||||||
|  |     struct openconnect_info *vpninfo; | ||||||
|  |     struct utsname utsbuf; | ||||||
|  |  | ||||||
|  |     g_user_data = options->user_data; | ||||||
|  |     g_vpnc_script = options->script; | ||||||
|  |  | ||||||
|  |     vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL); | ||||||
|  |  | ||||||
|  |     if (!vpninfo) | ||||||
|  |     { | ||||||
|  |         ERROR("openconnect_vpninfo_new failed"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     openconnect_set_loglevel(vpninfo, 1); | ||||||
|  |     openconnect_init_ssl(); | ||||||
|  |     openconnect_set_protocol(vpninfo, "gp"); | ||||||
|  |     openconnect_set_hostname(vpninfo, options->server); | ||||||
|  |     openconnect_set_cookie(vpninfo, options->cookie); | ||||||
|  |  | ||||||
|  |     if (options->certificate) | ||||||
|  |     { | ||||||
|  |         INFO("Setting client certificate: %s", options->certificate); | ||||||
|  |         openconnect_set_client_cert(vpninfo, options->certificate, NULL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (options->servercert) { | ||||||
|  |         INFO("Setting server certificate: %s", options->servercert); | ||||||
|  |         openconnect_set_system_trust(vpninfo, 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     g_cmd_pipe_fd = openconnect_setup_cmd_pipe(vpninfo); | ||||||
|  |     if (g_cmd_pipe_fd < 0) | ||||||
|  |     { | ||||||
|  |         ERROR("openconnect_setup_cmd_pipe failed"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!uname(&utsbuf)) | ||||||
|  |     { | ||||||
|  |         openconnect_set_localname(vpninfo, utsbuf.nodename); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Essential step | ||||||
|  |     if (openconnect_make_cstp_connection(vpninfo) != 0) | ||||||
|  |     { | ||||||
|  |         ERROR("openconnect_make_cstp_connection failed"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (openconnect_setup_dtls(vpninfo, 60) != 0) | ||||||
|  |     { | ||||||
|  |         openconnect_disable_dtls(vpninfo); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Essential step | ||||||
|  |     openconnect_set_setup_tun_handler(vpninfo, setup_tun_handler); | ||||||
|  |  | ||||||
|  |     while (1) | ||||||
|  |     { | ||||||
|  |         int ret = openconnect_mainloop(vpninfo, 300, 10); | ||||||
|  |  | ||||||
|  |         if (ret) | ||||||
|  |         { | ||||||
|  |             INFO("openconnect_mainloop returned %d, exiting", ret); | ||||||
|  |             openconnect_vpninfo_free(vpninfo); | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         INFO("openconnect_mainloop returned 0, reconnecting"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Stop the VPN connection */ | ||||||
|  | void vpn_disconnect() | ||||||
|  | { | ||||||
|  |     char cmd = OC_CMD_CANCEL; | ||||||
|  |     if (write(g_cmd_pipe_fd, &cmd, 1) < 0) | ||||||
|  |     { | ||||||
|  |         ERROR("Failed to write to command pipe, VPN connection may not be stopped"); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								gpcommon/src/vpn/vpn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								gpcommon/src/vpn/vpn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <openconnect.h> | ||||||
|  |  | ||||||
|  | typedef struct vpn_options | ||||||
|  | { | ||||||
|  |     const char *server; | ||||||
|  |     const char *cookie; | ||||||
|  |     const char *user_agent; | ||||||
|  |     void *user_data; | ||||||
|  |  | ||||||
|  |     const char *script; | ||||||
|  |     const char *certificate; | ||||||
|  |     const char *servercert; | ||||||
|  | } vpn_options; | ||||||
|  |  | ||||||
|  | int vpn_connect(const vpn_options *options); | ||||||
|  | void vpn_disconnect(); | ||||||
|  |  | ||||||
|  | extern void on_vpn_connected(int cmd_pipe_fd, void *user_data); | ||||||
|  | extern void vpn_log(int level, const char *msg); | ||||||
|  |  | ||||||
|  | static char *format_message(const char *format, va_list args) | ||||||
|  | { | ||||||
|  |     va_list args_copy; | ||||||
|  |     va_copy(args_copy, args); | ||||||
|  |     int len = vsnprintf(NULL, 0, format, args_copy); | ||||||
|  |     va_end(args_copy); | ||||||
|  |  | ||||||
|  |     char *buffer = malloc(len + 1); | ||||||
|  |     if (buffer == NULL) | ||||||
|  |     { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     vsnprintf(buffer, len + 1, format, args); | ||||||
|  |     return buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void _log(int level, ...) | ||||||
|  | { | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, level); | ||||||
|  |  | ||||||
|  |     char *format = va_arg(args, char *); | ||||||
|  |     char *message = format_message(format, args); | ||||||
|  |  | ||||||
|  |     va_end(args); | ||||||
|  |  | ||||||
|  |     if (message == NULL) | ||||||
|  |     { | ||||||
|  |         vpn_log(PRG_ERR, "Failed to format log message"); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         vpn_log(level, message); | ||||||
|  |         free(message); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LOG(level, ...) _log(level, __VA_ARGS__) | ||||||
|  | #define ERROR(...) LOG(PRG_ERR, __VA_ARGS__) | ||||||
|  | #define INFO(...) LOG(PRG_INFO, __VA_ARGS__) | ||||||
|  | #define DEBUG(...) LOG(PRG_DEBUG, __VA_ARGS__) | ||||||
|  | #define TRACE(...) LOG(PRG_TRACE, __VA_ARGS__) | ||||||
							
								
								
									
										93
									
								
								gpcommon/src/vpn/vpn_options.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								gpcommon/src/vpn/vpn_options.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | use super::{ffi, gpconf::OpenconnectArgs, vpnc_script::find_default_vpnc_script}; | ||||||
|  | use std::ffi::{c_char, c_void, CString}; | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub(crate) struct VpnOptions { | ||||||
|  |     server: CString, | ||||||
|  |     cookie: CString, | ||||||
|  |     user_agent: CString, | ||||||
|  |  | ||||||
|  |     script: CString, | ||||||
|  |     certificate: Option<CString>, | ||||||
|  |     servercert: Option<CString>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl VpnOptions { | ||||||
|  |     pub fn builder() -> VpnOptionsBuilder { | ||||||
|  |         VpnOptionsBuilder::default() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn as_oc_options(&self, user_data: *mut c_void) -> ffi::Options { | ||||||
|  |         ffi::Options { | ||||||
|  |             server: self.server.as_ptr(), | ||||||
|  |             cookie: self.cookie.as_ptr(), | ||||||
|  |             user_agent: self.user_agent.as_ptr(), | ||||||
|  |             user_data, | ||||||
|  |  | ||||||
|  |             script: self.script.as_ptr(), | ||||||
|  |             certificate: Self::option_as_ptr(&self.certificate), | ||||||
|  |             servercert: Self::option_as_ptr(&self.servercert), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn option_as_ptr(value: &Option<CString>) -> *const c_char { | ||||||
|  |         match value { | ||||||
|  |             Some(value) => value.as_ptr(), | ||||||
|  |             None => std::ptr::null(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub(crate) struct VpnOptionsBuilder { | ||||||
|  |     server: String, | ||||||
|  |     cookie: String, | ||||||
|  |     user_agent: String, | ||||||
|  |     openconnect_args: OpenconnectArgs, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl VpnOptionsBuilder { | ||||||
|  |     pub fn server(&mut self, server: &str) -> &mut Self { | ||||||
|  |         self.server = server.to_string(); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn cookie(&mut self, cookie: &str) -> &mut Self { | ||||||
|  |         self.cookie = cookie.to_string(); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn user_agent(&mut self, user_agent: &str) -> &mut Self { | ||||||
|  |         self.user_agent = user_agent.to_string(); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn with_openconnect_args(&mut self, openconnect_args: OpenconnectArgs) -> &mut Self { | ||||||
|  |         self.openconnect_args = openconnect_args; | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn to_cstr(value: &str) -> CString { | ||||||
|  |         CString::new(value.to_string()).expect("Failed to convert to CString") | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn build(&self) -> VpnOptions { | ||||||
|  |         let openconnect_args = &self.openconnect_args; | ||||||
|  |         let script = openconnect_args | ||||||
|  |             .script() | ||||||
|  |             .or_else(|| find_default_vpnc_script().map(|s| s.to_string())) | ||||||
|  |             .map(|s| Self::to_cstr(&s)) | ||||||
|  |             .unwrap_or_default(); | ||||||
|  |         let certificate = openconnect_args.certificate().map(|s| Self::to_cstr(&s)); | ||||||
|  |         let servercert = openconnect_args.servercert().map(|s| Self::to_cstr(&s)); | ||||||
|  |  | ||||||
|  |         VpnOptions { | ||||||
|  |             server: Self::to_cstr(&self.server), | ||||||
|  |             cookie: Self::to_cstr(&self.cookie), | ||||||
|  |             user_agent: Self::to_cstr(&self.user_agent), | ||||||
|  |             script, | ||||||
|  |             certificate, | ||||||
|  |             servercert, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								gpcommon/src/vpn/vpnc_script.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								gpcommon/src/vpn/vpnc_script.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | use std::path::Path; | ||||||
|  |  | ||||||
|  | use is_executable::IsExecutable; | ||||||
|  |  | ||||||
|  | const VPNC_SCRIPT_LOCATIONS: [&str; 4] = [ | ||||||
|  |     "/usr/local/share/vpnc-scripts/vpnc-script", | ||||||
|  |     "/usr/local/sbin/vpnc-script", | ||||||
|  |     "/usr/share/vpnc-scripts/vpnc-script", | ||||||
|  |     "/usr/sbin/vpnc-script /etc/vpnc/vpnc-script", | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | pub(crate) fn find_default_vpnc_script() -> Option<&'static str> { | ||||||
|  |     for location in VPNC_SCRIPT_LOCATIONS.iter() { | ||||||
|  |         let path = Path::new(location); | ||||||
|  |         if path.is_executable() { | ||||||
|  |             return Some(location); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     None | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								gpcommon/src/writer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								gpcommon/src/writer.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | use serde::Serialize; | ||||||
|  | use tokio::io::{self, AsyncWriteExt, WriteHalf}; | ||||||
|  | use tokio::net::UnixStream; | ||||||
|  |  | ||||||
|  | pub(crate) struct Writer { | ||||||
|  |     stream: WriteHalf<UnixStream>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<WriteHalf<UnixStream>> for Writer { | ||||||
|  |     fn from(stream: WriteHalf<UnixStream>) -> Self { | ||||||
|  |         Self { stream } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Writer { | ||||||
|  |     pub async fn write<T: Serialize>(&mut self, data: &T) -> Result<(), io::Error> { | ||||||
|  |         let data = serde_json::to_string(data)?; | ||||||
|  |         let data = format!("{}\n\n", data); | ||||||
|  |  | ||||||
|  |         self.stream.write_all(data.as_bytes()).await?; | ||||||
|  |         self.stream.flush().await?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								gpgui/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								gpgui/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | # Logs | ||||||
|  | logs | ||||||
|  | *.log | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | pnpm-debug.log* | ||||||
|  | lerna-debug.log* | ||||||
|  |  | ||||||
|  | node_modules | ||||||
|  | dist | ||||||
|  | dist-ssr | ||||||
|  | *.local | ||||||
|  |  | ||||||
|  | # Editor directories and files | ||||||
|  | .vscode/* | ||||||
|  | !.vscode/extensions.json | ||||||
|  | .idea | ||||||
|  | .DS_Store | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.sw? | ||||||
							
								
								
									
										27
									
								
								gpgui/docs/gateway-login-response.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								gpgui/docs/gateway-login-response.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <jnlp> | ||||||
|  |     <application-desc> | ||||||
|  |         <argument>(null)</argument> | ||||||
|  |         <argument>44d9988f3a3b5a247d359b1d39229add</argument> | ||||||
|  |         <argument>1cb389d44ec35e98665211761b65a049ef7ba77e</argument> | ||||||
|  |         <argument>GP-Gateway-N</argument> | ||||||
|  |         <argument>user</argument> | ||||||
|  |         <argument>AD_Authentication</argument> | ||||||
|  |         <argument>vsys1</argument> | ||||||
|  |         <argument>vpn.example.com</argument> | ||||||
|  |         <argument>(null)</argument> | ||||||
|  |         <argument></argument> | ||||||
|  |         <argument></argument> | ||||||
|  |         <argument></argument> | ||||||
|  |         <argument>tunnel</argument> | ||||||
|  |         <argument>-1</argument> | ||||||
|  |         <argument>4100</argument> | ||||||
|  |         <argument></argument> | ||||||
|  |         <argument>xxxxxxxxxxxxxxxx</argument> | ||||||
|  |         <argument>xxxxxxxxxxxxxxxx</argument> | ||||||
|  |         <argument></argument> | ||||||
|  |         <argument>4</argument> | ||||||
|  |         <argument>unknown</argument> | ||||||
|  |         <argument></argument> | ||||||
|  |     </application-desc> | ||||||
|  | </jnlp> | ||||||
							
								
								
									
										212
									
								
								gpgui/docs/portal-config-response.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								gpgui/docs/portal-config-response.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <policy> | ||||||
|  |     <portal-name>vpn.example.com</portal-name> | ||||||
|  |     <portal-config-version>4100</portal-config-version> | ||||||
|  |     <version>6.0.1-19 </version> | ||||||
|  |     <client-role>global-protect-full</client-role> | ||||||
|  |     <agent-user-override-key>****</agent-user-override-key> | ||||||
|  |     <root-ca> | ||||||
|  |         <entry name="DigiCert Global Root CA"> | ||||||
|  |             <cert> | ||||||
|  |                 -----BEGIN CERTIFICATE----- | ||||||
|  |                 -----END CERTIFICATE----- | ||||||
|  |             </cert> | ||||||
|  |             <install-in-cert-store>yes</install-in-cert-store> | ||||||
|  |         </entry> | ||||||
|  |         <entry name="Thawte RSA CA 2018"> | ||||||
|  |             <cert> | ||||||
|  |                 -----BEGIN CERTIFICATE----- | ||||||
|  |                 -----END CERTIFICATE----- | ||||||
|  |             </cert> | ||||||
|  |             <install-in-cert-store>yes</install-in-cert-store> | ||||||
|  |         </entry> | ||||||
|  |         <entry name="Temp_VPN_Root_Certificate"> | ||||||
|  |             <cert> | ||||||
|  |                 -----BEGIN CERTIFICATE----- | ||||||
|  |                 -----END CERTIFICATE----- | ||||||
|  |             </cert> | ||||||
|  |             <install-in-cert-store>no</install-in-cert-store> | ||||||
|  |         </entry> | ||||||
|  |     </root-ca> | ||||||
|  |     <connect-method>on-demand</connect-method> | ||||||
|  |     <pre-logon-then-on-demand>yes</pre-logon-then-on-demand> | ||||||
|  |     <refresh-config>yes</refresh-config> | ||||||
|  |     <refresh-config-interval>24</refresh-config-interval> | ||||||
|  |     <authentication-modifier> | ||||||
|  |         <none /> | ||||||
|  |     </authentication-modifier> | ||||||
|  |     <authentication-override> | ||||||
|  |         <accept-cookie>yes</accept-cookie> | ||||||
|  |         <generate-cookie>yes</generate-cookie> | ||||||
|  |         <cookie-lifetime> | ||||||
|  |             <lifetime-in-days>365</lifetime-in-days> | ||||||
|  |         </cookie-lifetime> | ||||||
|  |         <cookie-encrypt-decrypt-cert>vpn.example.com</cookie-encrypt-decrypt-cert> | ||||||
|  |     </authentication-override> | ||||||
|  |     <use-sso>yes</use-sso> | ||||||
|  |     <ip-address></ip-address> | ||||||
|  |     <host></host> | ||||||
|  |     <gateways> | ||||||
|  |         <cutoff-time>5</cutoff-time> | ||||||
|  |         <external> | ||||||
|  |             <list> | ||||||
|  |                 <entry name="xxx.xxx.xxx.xxx"> | ||||||
|  |                     <priority-rule> | ||||||
|  |                         <entry name="Any"> | ||||||
|  |                             <priority>1</priority> | ||||||
|  |                         </entry> | ||||||
|  |                     </priority-rule> | ||||||
|  |                     <priority>1</priority> | ||||||
|  |                     <description>vpn_gateway</description> | ||||||
|  |                 </entry> | ||||||
|  |             </list> | ||||||
|  |         </external> | ||||||
|  |     </gateways> | ||||||
|  |     <gateways-v6> | ||||||
|  |         <cutoff-time>5</cutoff-time> | ||||||
|  |         <external> | ||||||
|  |             <list> | ||||||
|  |                 <entry name="vpn_gateway"> | ||||||
|  |                     <ipv4>xxx.xxx.xxx.xxx</ipv4> | ||||||
|  |                     <priority-rule> | ||||||
|  |                         <entry name="Any"> | ||||||
|  |                             <priority>1</priority> | ||||||
|  |                         </entry> | ||||||
|  |                     </priority-rule> | ||||||
|  |                     <priority>1</priority> | ||||||
|  |                 </entry> | ||||||
|  |             </list> | ||||||
|  |         </external> | ||||||
|  |     </gateways-v6> | ||||||
|  |     <agent-ui> | ||||||
|  |         <can-save-password>yes</can-save-password> | ||||||
|  |         <passcode></passcode> | ||||||
|  |         <uninstall-passwd></uninstall-passwd> | ||||||
|  |         <agent-user-override-timeout>0</agent-user-override-timeout> | ||||||
|  |         <max-agent-user-overrides>0</max-agent-user-overrides> | ||||||
|  |         <help-page></help-page> | ||||||
|  |         <help-page-2></help-page-2> | ||||||
|  |         <welcome-page> | ||||||
|  |             <display>no</display> | ||||||
|  |             <page></page> | ||||||
|  |         </welcome-page> | ||||||
|  |         <agent-user-override>allowed</agent-user-override> | ||||||
|  |         <enable-advanced-view>yes</enable-advanced-view> | ||||||
|  |         <enable-do-not-display-this-welcome-page-again>yes</enable-do-not-display-this-welcome-page-again> | ||||||
|  |         <can-change-portal>yes</can-change-portal> | ||||||
|  |         <show-agent-icon>yes</show-agent-icon> | ||||||
|  |         <password-expiry-message></password-expiry-message> | ||||||
|  |         <init-panel>no</init-panel> | ||||||
|  |         <user-input-on-top>no</user-input-on-top> | ||||||
|  |     </agent-ui> | ||||||
|  |     <hip-collection> | ||||||
|  |         <hip-report-interval>3600</hip-report-interval> | ||||||
|  |         <max-wait-time>20</max-wait-time> | ||||||
|  |         <collect-hip-data>yes</collect-hip-data> | ||||||
|  |         <default> | ||||||
|  |             <category> | ||||||
|  |                 <member>antivirus</member> | ||||||
|  |                 <member>anti-spyware</member> | ||||||
|  |                 <member>host-info</member> | ||||||
|  |                 <member>data-loss-prevention</member> | ||||||
|  |                 <member>patch-management</member> | ||||||
|  |                 <member>firewall</member> | ||||||
|  |                 <member>anti-malware</member> | ||||||
|  |                 <member>disk-backup</member> | ||||||
|  |                 <member>disk-encryption</member> | ||||||
|  |             </category> | ||||||
|  |         </default> | ||||||
|  |     </hip-collection> | ||||||
|  |     <agent-config> | ||||||
|  |         <save-user-credentials>1</save-user-credentials> | ||||||
|  |         <portal-2fa>no</portal-2fa> | ||||||
|  |         <internal-gateway-2fa>no</internal-gateway-2fa> | ||||||
|  |         <auto-discovery-external-gateway-2fa>no</auto-discovery-external-gateway-2fa> | ||||||
|  |         <manual-only-gateway-2fa>no</manual-only-gateway-2fa> | ||||||
|  |         <disconnect-reasons></disconnect-reasons> | ||||||
|  |         <uninstall>allowed</uninstall> | ||||||
|  |         <client-upgrade>prompt</client-upgrade> | ||||||
|  |         <enable-signout>yes</enable-signout> | ||||||
|  |         <use-sso-pin>no</use-sso-pin> | ||||||
|  |         <use-sso-macos>no</use-sso-macos> | ||||||
|  |         <logout-remove-sso>yes</logout-remove-sso> | ||||||
|  |         <krb-auth-fail-fallback>yes</krb-auth-fail-fallback> | ||||||
|  |         <default-browser>no</default-browser> | ||||||
|  |         <retry-tunnel>30</retry-tunnel> | ||||||
|  |         <retry-timeout>5</retry-timeout> | ||||||
|  |         <traffic-enforcement>no</traffic-enforcement> | ||||||
|  |         <enforce-globalprotect>no</enforce-globalprotect> | ||||||
|  |         <enforcer-exception-list /> | ||||||
|  |         <enforcer-exception-list-domain /> | ||||||
|  |         <captive-portal-exception-timeout>0</captive-portal-exception-timeout> | ||||||
|  |         <captive-portal-login-url></captive-portal-login-url> | ||||||
|  |         <traffic-blocking-notification-delay>15</traffic-blocking-notification-delay> | ||||||
|  |         <display-traffic-blocking-notification-msg>yes</display-traffic-blocking-notification-msg> | ||||||
|  |         <traffic-blocking-notification-msg><div style="font-family:'Helvetica | ||||||
|  |             Neue';"><h1 style="color:red;text-align:center; margin: 0; font-size: | ||||||
|  |             30px;">Notice</h1><p style="margin: 0;font-size: 15px; | ||||||
|  |             line-height: 1.2em;">To access the network, you must first connect to | ||||||
|  |             GlobalProtect.</p></div></traffic-blocking-notification-msg> | ||||||
|  |         <allow-traffic-blocking-notification-dismissal>yes</allow-traffic-blocking-notification-dismissal> | ||||||
|  |         <display-captive-portal-detection-msg>no</display-captive-portal-detection-msg> | ||||||
|  |         <captive-portal-detection-msg><div style="font-family:'Helvetica | ||||||
|  |             Neue';"><h1 style="color:red;text-align:center; margin: 0; font-size: | ||||||
|  |             30px;">Captive Portal Detected</h1><p style="margin: 0; font-size: | ||||||
|  |             15px; line-height: 1.2em;">GlobalProtect has temporarily permitted network | ||||||
|  |             access for you to connect to the Internet. Follow instructions from your internet | ||||||
|  |             provider.</p><p style="margin: 0; font-size: 15px; line-height: | ||||||
|  |             1.2em;">If you let the connection time out, open GlobalProtect and click Connect | ||||||
|  |             to try again.</p></div></captive-portal-detection-msg> | ||||||
|  |         <captive-portal-notification-delay>5</captive-portal-notification-delay> | ||||||
|  |         <certificate-store-lookup>user-and-machine</certificate-store-lookup> | ||||||
|  |         <scep-certificate-renewal-period>7</scep-certificate-renewal-period> | ||||||
|  |         <ext-key-usage-oid-for-client-cert></ext-key-usage-oid-for-client-cert> | ||||||
|  |         <retain-connection-smartcard-removal>yes</retain-connection-smartcard-removal> | ||||||
|  |         <user-accept-terms-before-creating-tunnel>no</user-accept-terms-before-creating-tunnel> | ||||||
|  |         <rediscover-network>yes</rediscover-network> | ||||||
|  |         <resubmit-host-info>yes</resubmit-host-info> | ||||||
|  |         <can-continue-if-portal-cert-invalid>yes</can-continue-if-portal-cert-invalid> | ||||||
|  |         <user-switch-tunnel-rename-timeout>0</user-switch-tunnel-rename-timeout> | ||||||
|  |         <pre-logon-tunnel-rename-timeout>0</pre-logon-tunnel-rename-timeout> | ||||||
|  |         <preserve-tunnel-upon-user-logoff-timeout>0</preserve-tunnel-upon-user-logoff-timeout> | ||||||
|  |         <ipsec-failover-ssl>0</ipsec-failover-ssl> | ||||||
|  |         <display-tunnel-fallback-notification>yes</display-tunnel-fallback-notification> | ||||||
|  |         <ssl-only-selection>0</ssl-only-selection> | ||||||
|  |         <tunnel-mtu>1400</tunnel-mtu> | ||||||
|  |         <max-internal-gateway-connection-attempts>0</max-internal-gateway-connection-attempts> | ||||||
|  |         <adv-internal-host-detection>no</adv-internal-host-detection> | ||||||
|  |         <portal-timeout>30</portal-timeout> | ||||||
|  |         <connect-timeout>60</connect-timeout> | ||||||
|  |         <receive-timeout>30</receive-timeout> | ||||||
|  |         <split-tunnel-option>network-traffic</split-tunnel-option> | ||||||
|  |         <enforce-dns>yes</enforce-dns> | ||||||
|  |         <append-local-search-domain>no</append-local-search-domain> | ||||||
|  |         <flush-dns>no</flush-dns> | ||||||
|  |         <auto-proxy-pac></auto-proxy-pac> | ||||||
|  |         <proxy-multiple-autodetect>no</proxy-multiple-autodetect> | ||||||
|  |         <use-proxy>yes</use-proxy> | ||||||
|  |         <wsc-autodetect>yes</wsc-autodetect> | ||||||
|  |         <mfa-enabled>no</mfa-enabled> | ||||||
|  |         <mfa-listening-port>4501</mfa-listening-port> | ||||||
|  |         <mfa-trusted-host-list /> | ||||||
|  |         <mfa-notification-msg>You have attempted to access a protected resource that requires | ||||||
|  |             additional authentication. Proceed to authenticate at</mfa-notification-msg> | ||||||
|  |         <mfa-prompt-suppress-time>0</mfa-prompt-suppress-time> | ||||||
|  |         <ipv6-preferred>yes</ipv6-preferred> | ||||||
|  |         <change-password-message></change-password-message> | ||||||
|  |         <log-gateway>no</log-gateway> | ||||||
|  |         <cdl-log>no</cdl-log> | ||||||
|  |         <dem-notification>yes</dem-notification> | ||||||
|  |         <diagnostic-servers /> | ||||||
|  |         <dem-agent>not-install</dem-agent> | ||||||
|  |         <quarantine-add-message>Access to the network from this device has been restricted as per | ||||||
|  |             your organization's security policy. Please contact your IT Administrator.</quarantine-add-message> | ||||||
|  |         <quarantine-remove-message>Access to the network from this device has been restored as per | ||||||
|  |             your organization's security policy.</quarantine-remove-message> | ||||||
|  |  | ||||||
|  |     </agent-config> | ||||||
|  |     <user-email>user@example.com</user-email> | ||||||
|  |     <portal-userauthcookie>xxxxxx</portal-userauthcookie> | ||||||
|  |     <portal-prelogonuserauthcookie>xxxxxx</portal-prelogonuserauthcookie> | ||||||
|  |     <config-digest>2d8e997765a2f59cbf80284b2f2fbd38</config-digest> | ||||||
|  | </policy> | ||||||
							
								
								
									
										19
									
								
								gpgui/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								gpgui/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8" /> | ||||||
|  |     <link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||||
|  |     <title>GlobalProtect</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <script> | ||||||
|  |       /* workaround to webview font size auto scaling */ | ||||||
|  |       var htmlFontSize = getComputedStyle(document.documentElement).fontSize; | ||||||
|  |       var ratio = parseInt(htmlFontSize, 10) / 16; | ||||||
|  |       document.documentElement.style.fontSize = 16 / ratio + "px"; | ||||||
|  |     </script> | ||||||
|  |     <div id="root" data-tauri-drag-region></div> | ||||||
|  |     <script type="module" src="/src/pages/main.tsx"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user