mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			194 Commits
		
	
	
		
			v1.2.5
			...
			e8259b841b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e8259b841b | ||
|  | 939f2bd94a | ||
|  | abffa21268 | ||
|  | 99342372d2 | ||
|  | cd8d794655 | ||
|  | 705b03c0bb | ||
|  | 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 | ||
|  | f089996cdc | ||
|  | 260b557238 | ||
|  | 3495dbfe18 | ||
|  | cdf193024c | ||
|  | 76de070d78 | ||
|  | 420ae27888 | ||
|  | 6a347746cc | ||
|  | 624babb380 | ||
|  | 511b20fdcd | ||
|  | abe33c7407 | ||
|  | 99a82c8641 | ||
|  | e5d0acad3c | ||
|  | 38a1eded19 | ||
|  | 3e23e7eaae | ||
|  | cf46848e63 | ||
|  | 2e826201d2 | ||
|  | adba408dc3 | ||
|  | 5d613369ee | ||
|  | ebd3de6f63 | ||
|  | 266ab65892 | ||
|  | ccaf93ec31 | ||
|  | e08d7d7c4d | ||
|  | c14a6ad1d2 | 
							
								
								
									
										13
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | # top-most EditorConfig file | ||||||
|  | root = true | ||||||
|  |  | ||||||
|  | # Unix-style newlines with a newline ending every file | ||||||
|  | [*] | ||||||
|  | end_of_line = lf | ||||||
|  | insert_final_newline = false | ||||||
|  | trim_trailing_whitespace=true | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 4 | ||||||
|  |  | ||||||
|  | [*.sh] | ||||||
|  | indent_style = tab | ||||||
							
								
								
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | ko_fi: yuezk | ||||||
|  | custom: ["https://buymeacoffee.com/yuezk", "https://paypal.me/zongkun"] | ||||||
							
								
								
									
										275
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | |||||||
|  | name: Build | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  |       - develop | ||||||
|  |     tags: | ||||||
|  |       - "v*.*.*" | ||||||
|  |     paths-ignore: | ||||||
|  |       - LICENSE | ||||||
|  |       - "*.md" | ||||||
|  |       - .vscode | ||||||
|  |   workflow_dispatch: | ||||||
|  |  | ||||||
|  | # A workflow run is made up of one or more jobs that can run sequentially or in parallel | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  |     strategy: | ||||||
|  |       matrix: | ||||||
|  |         os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04] | ||||||
|  |  | ||||||
|  |     runs-on: ${{ matrix.os }} | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       # Checkout repository and submodules | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |  | ||||||
|  |       - name: Build | ||||||
|  |         run: | | ||||||
|  |           ./scripts/install-ubuntu.sh | ||||||
|  |           # assert no library missing | ||||||
|  |           test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0 | ||||||
|  |  | ||||||
|  |   snapshot-archive-all: | ||||||
|  |     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||||
|  |     needs: build | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |           fetch-depth: 0 | ||||||
|  |  | ||||||
|  |       - name: Install dependencies | ||||||
|  |         run: | | ||||||
|  |           python -m pip install --upgrade pip | ||||||
|  |           pip install git-archive-all | ||||||
|  |  | ||||||
|  |       - name: Archive all | ||||||
|  |         run: | | ||||||
|  |           ./scripts/snapshot-archive-all.sh | ||||||
|  |  | ||||||
|  |       - name: Verify debian package | ||||||
|  |         run: | | ||||||
|  |           ./scripts/verify-debian-package.sh | ||||||
|  |  | ||||||
|  |       - uses: actions/upload-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: snapshot-source-code | ||||||
|  |           path: ./artifacts/* | ||||||
|  |  | ||||||
|  |   snapshot-ppa: | ||||||
|  |     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||||
|  |     needs: snapshot-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: snapshot-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - name: Extract source code | ||||||
|  |         run: | | ||||||
|  |           cd $GITHUB_WORKSPACE/artifacts | ||||||
|  |           mkdir deb-build && cp *.tar.gz deb-build && cd deb-build | ||||||
|  |           tar xf *.tar.gz | ||||||
|  |  | ||||||
|  |       - name: Publish PPA | ||||||
|  |         uses: yuezk/publish-ppa-package@develop | ||||||
|  |         with: | ||||||
|  |           repository: 'ppa:yuezk/globalprotect-openconnect-snapshot' | ||||||
|  |           gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} | ||||||
|  |           gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} | ||||||
|  |           pkgdir: '${{ github.workspace }}/artifacts/deb-build/globalprotect-openconnect*/' | ||||||
|  |  | ||||||
|  |   snapshot-aur: | ||||||
|  |     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||||
|  |     needs: snapshot-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: snapshot-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - name: Publish AUR package | ||||||
|  |         uses: yuezk/github-actions-deploy-aur@update-pkgver | ||||||
|  |         with: | ||||||
|  |           pkgname: globalprotect-openconnect-git | ||||||
|  |           pkgbuild: ./artifacts/aur/PKGBUILD | ||||||
|  |           assets: ./artifacts/aur/gp.install | ||||||
|  |           update_pkgver: true | ||||||
|  |           commit_username: ${{ secrets.AUR_USERNAME }} | ||||||
|  |           commit_email: ${{ secrets.AUR_EMAIL }} | ||||||
|  |           ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} | ||||||
|  |           commit_message: 'Snapshot release: git#${{ github.sha }}' | ||||||
|  |  | ||||||
|  |   snapshot-obs: | ||||||
|  |     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||||
|  |     needs: snapshot-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: snapshot-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - uses: yuezk/publish-obs-package@main | ||||||
|  |         with: | ||||||
|  |           project: home:yuezk | ||||||
|  |           package: globalprotect-openconnect-snapshot | ||||||
|  |           username: yuezk | ||||||
|  |           password: ${{ secrets.OBS_PASSWORD }} | ||||||
|  |           files: ./artifacts/obs/* | ||||||
|  |  | ||||||
|  |   snapshot-snap: | ||||||
|  |     # if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||||
|  |     if: ${{ false }} | ||||||
|  |     needs: snapshot-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: snapshot-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - name: Extract source code | ||||||
|  |         run: | | ||||||
|  |           mkdir snap-source | ||||||
|  |           tar xvf ./artifacts/globalprotect-openconnect-*tar.gz \ | ||||||
|  |             --directory snap-source \ | ||||||
|  |             --strip 1 | ||||||
|  |  | ||||||
|  |       - uses: snapcore/action-build@v1 | ||||||
|  |         id: build | ||||||
|  |         with: | ||||||
|  |           path: ./snap-source | ||||||
|  |  | ||||||
|  |       - uses: snapcore/action-publish@v1 | ||||||
|  |         with: | ||||||
|  |           store_login: ${{ secrets.SNAPSTORE_LOGIN }} | ||||||
|  |           snap: ${{ steps.build.outputs.snap }} | ||||||
|  |           release: edge | ||||||
|  |  | ||||||
|  |   release-archive-all: | ||||||
|  |     if: startsWith(github.ref, 'refs/tags/v') | ||||||
|  |     needs: build | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |           fetch-depth: 0 | ||||||
|  |  | ||||||
|  |       - name: Install dependencies | ||||||
|  |         run: | | ||||||
|  |           python -m pip install --upgrade pip | ||||||
|  |           pip install git-archive-all | ||||||
|  |  | ||||||
|  |       - name: Archive all | ||||||
|  |         run: | | ||||||
|  |           ./scripts/release-archive-all.sh | ||||||
|  |  | ||||||
|  |       - name: Verify debian package | ||||||
|  |         run: | | ||||||
|  |           ./scripts/verify-debian-package.sh | ||||||
|  |  | ||||||
|  |       - uses: actions/upload-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: release-source-code | ||||||
|  |           path: ./artifacts/* | ||||||
|  |  | ||||||
|  |   release-ppa: | ||||||
|  |     if: startsWith(github.ref, 'refs/tags/v') | ||||||
|  |     needs: release-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: release-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - name: Extract source code | ||||||
|  |         run: | | ||||||
|  |           cd $GITHUB_WORKSPACE/artifacts | ||||||
|  |           mkdir deb-build && cp *.tar.gz deb-build && cd deb-build | ||||||
|  |           tar xf *.tar.gz | ||||||
|  |  | ||||||
|  |       - name: Publish PPA | ||||||
|  |         uses: yuezk/publish-ppa-package@develop | ||||||
|  |         with: | ||||||
|  |           repository: 'ppa:yuezk/globalprotect-openconnect' | ||||||
|  |           gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }} | ||||||
|  |           gpg_passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }} | ||||||
|  |           pkgdir: '${{ github.workspace }}/artifacts/deb-build/globalprotect-openconnect*/' | ||||||
|  |  | ||||||
|  |   release-aur: | ||||||
|  |     if: startsWith(github.ref, 'refs/tags/v') | ||||||
|  |     needs: release-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: release-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - name: Publish AUR package | ||||||
|  |         uses: yuezk/github-actions-deploy-aur@update-pkgver | ||||||
|  |         with: | ||||||
|  |           pkgname: globalprotect-openconnect-git | ||||||
|  |           pkgbuild: ./artifacts/aur/PKGBUILD | ||||||
|  |           assets: ./artifacts/aur/gp.install | ||||||
|  |           update_pkgver: true | ||||||
|  |           commit_username: ${{ secrets.AUR_USERNAME }} | ||||||
|  |           commit_email: ${{ secrets.AUR_EMAIL }} | ||||||
|  |           ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} | ||||||
|  |           commit_message: 'Release ${{ github.ref }}' | ||||||
|  |  | ||||||
|  |   release-obs: | ||||||
|  |     if: startsWith(github.ref, 'refs/tags/v') | ||||||
|  |     needs: release-archive-all | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: release-source-code | ||||||
|  |           path: artifacts | ||||||
|  |  | ||||||
|  |       - uses: yuezk/publish-obs-package@main | ||||||
|  |         with: | ||||||
|  |           project: home:yuezk | ||||||
|  |           package: globalprotect-openconnect | ||||||
|  |           username: yuezk | ||||||
|  |           password: ${{ secrets.OBS_PASSWORD }} | ||||||
|  |           files: ./artifacts/obs/* | ||||||
|  |  | ||||||
|  |   release-github: | ||||||
|  |     if: startsWith(github.ref, 'refs/tags/v') | ||||||
|  |     needs: | ||||||
|  |       - release-ppa | ||||||
|  |       - release-aur | ||||||
|  |       - release-obs | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/download-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: release-source-code | ||||||
|  |           path: artifacts | ||||||
|  |       - uses: softprops/action-gh-release@v1 | ||||||
|  |         with: | ||||||
|  |           files: | | ||||||
|  |             ./artifacts/*.tar.gz | ||||||
							
								
								
									
										33
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | name: PR Build | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  |       - develop | ||||||
|  |     paths-ignore: | ||||||
|  |       - LICENSE | ||||||
|  |       - "*.md" | ||||||
|  |       - .vscode | ||||||
|  |   workflow_dispatch: | ||||||
|  |  | ||||||
|  | # A workflow run is made up of one or more jobs that can run sequentially or in parallel | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  |     strategy: | ||||||
|  |       matrix: | ||||||
|  |         os: [ubuntu-18.04, ubuntu-20.04] | ||||||
|  |  | ||||||
|  |     runs-on: ${{ matrix.os }} | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       # Checkout repository and submodules | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |  | ||||||
|  |       - name: Build | ||||||
|  |         run: | | ||||||
|  |           ./scripts/install-ubuntu.sh | ||||||
|  |           # assert no library missing | ||||||
|  |           test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0 | ||||||
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,11 +1,21 @@ | |||||||
| # Binaries | # Binaries | ||||||
| gpclient | *.rpm | ||||||
| gpservice | *.gz | ||||||
|  | *.snap | ||||||
|  | .DS_Store | ||||||
|  | build-debian | ||||||
|  | build | ||||||
|  | artifacts | ||||||
|  |  | ||||||
|  | .cmake | ||||||
|  | .idea | ||||||
|  |  | ||||||
| # Auto generated DBus files | # Auto generated DBus files | ||||||
| *_adaptor.cpp | *_adaptor.cpp | ||||||
| *_adaptor.h | *_adaptor.h | ||||||
|  |  | ||||||
|  | gpservice_interface.* | ||||||
|  |  | ||||||
| # C++ objects and libs | # C++ objects and libs | ||||||
| *.slo | *.slo | ||||||
| *.lo | *.lo | ||||||
| @@ -53,7 +63,7 @@ target_wrapper.* | |||||||
| # QtCreator CMake | # QtCreator CMake | ||||||
| CMakeLists.txt.user* | CMakeLists.txt.user* | ||||||
|  |  | ||||||
| # QtCreator 4.8< compilation database  | # QtCreator 4.8< compilation database | ||||||
| compile_commands.json | compile_commands.json | ||||||
|  |  | ||||||
| # QtCreator local machine specific files for imported projects | # QtCreator local machine specific files for imported projects | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,10 @@ | |||||||
| [submodule "singleapplication"] | [submodule "singleapplication"] | ||||||
| 	path = singleapplication | 	path = 3rdparty/SingleApplication | ||||||
| 	url = https://github.com/itay-grudev/SingleApplication.git | 	url = https://github.com/itay-grudev/SingleApplication.git | ||||||
|  |  | ||||||
| [submodule "plog"] | [submodule "plog"] | ||||||
| 	path = plog | 	path = 3rdparty/plog | ||||||
| 	url = https://github.com/SergiusTheBest/plog.git | 	url = https://github.com/SergiusTheBest/plog.git | ||||||
|  | [submodule "3rdparty/qtkeychain"] | ||||||
|  | 	path = 3rdparty/qtkeychain | ||||||
|  | 	url = git@github.com:frankosterfeld/qtkeychain.git | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | { | ||||||
|  |     "files.watcherExclude": { | ||||||
|  |         "**/artifacts/**": true, | ||||||
|  |     }, | ||||||
|  |     "files.associations": { | ||||||
|  |         "qregularexpression": "cpp", | ||||||
|  |         "qfileinfo": "cpp", | ||||||
|  |         "qregularexpressionmatch": "cpp", | ||||||
|  |         "qdatetime": "cpp", | ||||||
|  |         "qprocess": "cpp", | ||||||
|  |         "qobject": "cpp", | ||||||
|  |         "qstandardpaths": "cpp", | ||||||
|  |         "qmainwindow": "cpp", | ||||||
|  |         "qsystemtrayicon": "cpp", | ||||||
|  |         "qpushbutton": "cpp", | ||||||
|  |         "qmenu": "cpp", | ||||||
|  |         "qjsondocument": "cpp", | ||||||
|  |         "qnetworkaccessmanager": "cpp", | ||||||
|  |         "qwebengineview": "cpp", | ||||||
|  |         "qprocessenvironment": "cpp", | ||||||
|  |         "qnetworkreply": "cpp", | ||||||
|  |         "qicon": "cpp", | ||||||
|  |         "qsslsocket": "cpp", | ||||||
|  |         "qapplication": "cpp" | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								3rdparty/SingleApplication
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								3rdparty/SingleApplication
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule 3rdparty/SingleApplication added at bdbb09b5f2
									
								
							
							
								
								
									
										12
									
								
								3rdparty/inih/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								3rdparty/inih/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | cmake_minimum_required(VERSION 3.10.0) | ||||||
|  |  | ||||||
|  | set(CMAKE_CXX_STANDARD 17) | ||||||
|  | project(inih) | ||||||
|  |  | ||||||
|  | add_library(inih STATIC | ||||||
|  |     ini.h | ||||||
|  |     ini.c | ||||||
|  |     cpp/INIReader.h | ||||||
|  |     cpp/INIReader.cpp | ||||||
|  | ) | ||||||
|  | target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp") | ||||||
							
								
								
									
										27
									
								
								3rdparty/inih/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								3rdparty/inih/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  |  | ||||||
|  | The "inih" library is distributed under the New BSD license: | ||||||
|  |  | ||||||
|  | Copyright (c) 2009, Ben Hoyt | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  |     * Redistributions of source code must retain the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer. | ||||||
|  |     * Redistributions in binary form must reproduce the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer in the | ||||||
|  |       documentation and/or other materials provided with the distribution. | ||||||
|  |     * Neither the name of Ben Hoyt nor the names of its contributors | ||||||
|  |       may be used to endorse or promote products derived from this software | ||||||
|  |       without specific prior written permission. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY | ||||||
|  | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY | ||||||
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||||
|  | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										116
									
								
								3rdparty/inih/cpp/INIReader.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								3rdparty/inih/cpp/INIReader.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | |||||||
|  | // Read an INI file into easy-to-access name/value pairs. | ||||||
|  |  | ||||||
|  | // SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  | ||||||
|  | // Copyright (C) 2009-2020, Ben Hoyt | ||||||
|  |  | ||||||
|  | // inih and INIReader are released under the New BSD license (see LICENSE.txt). | ||||||
|  | // Go to the project home page for more info: | ||||||
|  | // | ||||||
|  | // https://github.com/benhoyt/inih | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cctype> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include "../ini.h" | ||||||
|  | #include "INIReader.h" | ||||||
|  |  | ||||||
|  | using std::string; | ||||||
|  |  | ||||||
|  | INIReader::INIReader(const string& filename) | ||||||
|  | { | ||||||
|  |     _error = ini_parse(filename.c_str(), ValueHandler, this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | INIReader::INIReader(const char *buffer, size_t buffer_size) | ||||||
|  | { | ||||||
|  |   string content(buffer, buffer_size); | ||||||
|  |   _error = ini_parse_string(content.c_str(), ValueHandler, this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int INIReader::ParseError() const | ||||||
|  | { | ||||||
|  |     return _error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | string INIReader::Get(const string& section, const string& name, const string& default_value) const | ||||||
|  | { | ||||||
|  |     string key = MakeKey(section, name); | ||||||
|  |     // Use _values.find() here instead of _values.at() to support pre C++11 compilers | ||||||
|  |     return _values.count(key) ? _values.find(key)->second : default_value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | string INIReader::GetString(const string& section, const string& name, const string& default_value) const | ||||||
|  | { | ||||||
|  |     const string str = Get(section, name, ""); | ||||||
|  |     return str.empty() ? default_value : str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long INIReader::GetInteger(const string& section, const string& name, long default_value) const | ||||||
|  | { | ||||||
|  |     string valstr = Get(section, name, ""); | ||||||
|  |     const char* value = valstr.c_str(); | ||||||
|  |     char* end; | ||||||
|  |     // This parses "1234" (decimal) and also "0x4D2" (hex) | ||||||
|  |     long n = strtol(value, &end, 0); | ||||||
|  |     return end > value ? n : default_value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | double INIReader::GetReal(const string& section, const string& name, double default_value) const | ||||||
|  | { | ||||||
|  |     string valstr = Get(section, name, ""); | ||||||
|  |     const char* value = valstr.c_str(); | ||||||
|  |     char* end; | ||||||
|  |     double n = strtod(value, &end); | ||||||
|  |     return end > value ? n : default_value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool INIReader::GetBoolean(const string& section, const string& name, bool default_value) const | ||||||
|  | { | ||||||
|  |     string valstr = Get(section, name, ""); | ||||||
|  |     // Convert to lower case to make string comparisons case-insensitive | ||||||
|  |     std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); | ||||||
|  |     if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") | ||||||
|  |         return true; | ||||||
|  |     else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") | ||||||
|  |         return false; | ||||||
|  |     else | ||||||
|  |         return default_value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool INIReader::HasSection(const string& section) const | ||||||
|  | { | ||||||
|  |     const string key = MakeKey(section, ""); | ||||||
|  |     std::map<string, string>::const_iterator pos = _values.lower_bound(key); | ||||||
|  |     if (pos == _values.end()) | ||||||
|  |         return false; | ||||||
|  |     // Does the key at the lower_bound pos start with "section"? | ||||||
|  |     return pos->first.compare(0, key.length(), key) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool INIReader::HasValue(const string& section, const string& name) const | ||||||
|  | { | ||||||
|  |     string key = MakeKey(section, name); | ||||||
|  |     return _values.count(key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | string INIReader::MakeKey(const string& section, const string& name) | ||||||
|  | { | ||||||
|  |     string key = section + "=" + name; | ||||||
|  |     // Convert to lower case to make section/name lookups case-insensitive | ||||||
|  |     std::transform(key.begin(), key.end(), key.begin(), ::tolower); | ||||||
|  |     return key; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int INIReader::ValueHandler(void* user, const char* section, const char* name, | ||||||
|  |                             const char* value) | ||||||
|  | { | ||||||
|  |     if (!name)  // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled | ||||||
|  |         return 1; | ||||||
|  |     INIReader* reader = static_cast<INIReader*>(user); | ||||||
|  |     string key = MakeKey(section, name); | ||||||
|  |     if (reader->_values[key].size() > 0) | ||||||
|  |         reader->_values[key] += "\n"; | ||||||
|  |     reader->_values[key] += value ? value : ""; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								3rdparty/inih/cpp/INIReader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								3rdparty/inih/cpp/INIReader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | // Read an INI file into easy-to-access name/value pairs. | ||||||
|  |  | ||||||
|  | // SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  | ||||||
|  | // Copyright (C) 2009-2020, Ben Hoyt | ||||||
|  |  | ||||||
|  | // inih and INIReader are released under the New BSD license (see LICENSE.txt). | ||||||
|  | // Go to the project home page for more info: | ||||||
|  | // | ||||||
|  | // https://github.com/benhoyt/inih | ||||||
|  |  | ||||||
|  | #ifndef INIREADER_H | ||||||
|  | #define INIREADER_H | ||||||
|  |  | ||||||
|  | #include <map> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // Visibility symbols, required for Windows DLLs | ||||||
|  | #ifndef INI_API | ||||||
|  | #if defined _WIN32 || defined __CYGWIN__ | ||||||
|  | #	ifdef INI_SHARED_LIB | ||||||
|  | #		ifdef INI_SHARED_LIB_BUILDING | ||||||
|  | #			define INI_API __declspec(dllexport) | ||||||
|  | #		else | ||||||
|  | #			define INI_API __declspec(dllimport) | ||||||
|  | #		endif | ||||||
|  | #	else | ||||||
|  | #		define INI_API | ||||||
|  | #	endif | ||||||
|  | #else | ||||||
|  | #	if defined(__GNUC__) && __GNUC__ >= 4 | ||||||
|  | #		define INI_API __attribute__ ((visibility ("default"))) | ||||||
|  | #	else | ||||||
|  | #		define INI_API | ||||||
|  | #	endif | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // Read an INI file into easy-to-access name/value pairs. (Note that I've gone | ||||||
|  | // for simplicity here rather than speed, but it should be pretty decent.) | ||||||
|  | class INIReader | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     // Construct INIReader and parse given filename. See ini.h for more info | ||||||
|  |     // about the parsing. | ||||||
|  |     INI_API explicit INIReader(const std::string& filename); | ||||||
|  |  | ||||||
|  |     // Construct INIReader and parse given buffer. See ini.h for more info | ||||||
|  |     // about the parsing. | ||||||
|  |     INI_API explicit INIReader(const char *buffer, size_t buffer_size); | ||||||
|  |  | ||||||
|  |     // Return the result of ini_parse(), i.e., 0 on success, line number of | ||||||
|  |     // first error on parse error, or -1 on file open error. | ||||||
|  |     INI_API int ParseError() const; | ||||||
|  |  | ||||||
|  |     // Get a string value from INI file, returning default_value if not found. | ||||||
|  |     INI_API std::string Get(const std::string& section, const std::string& name, | ||||||
|  |                     const std::string& default_value) const; | ||||||
|  |  | ||||||
|  |     // Get a string value from INI file, returning default_value if not found, | ||||||
|  |     // empty, or contains only whitespace. | ||||||
|  |     INI_API std::string GetString(const std::string& section, const std::string& name, | ||||||
|  |                     const std::string& default_value) const; | ||||||
|  |  | ||||||
|  |     // Get an integer (long) value from INI file, returning default_value if | ||||||
|  |     // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). | ||||||
|  |     INI_API long GetInteger(const std::string& section, const std::string& name, long default_value) const; | ||||||
|  |  | ||||||
|  |     // Get a real (floating point double) value from INI file, returning | ||||||
|  |     // default_value if not found or not a valid floating point value | ||||||
|  |     // according to strtod(). | ||||||
|  |     INI_API double GetReal(const std::string& section, const std::string& name, double default_value) const; | ||||||
|  |  | ||||||
|  |     // Get a boolean value from INI file, returning default_value if not found or if | ||||||
|  |     // not a valid true/false value. Valid true values are "true", "yes", "on", "1", | ||||||
|  |     // and valid false values are "false", "no", "off", "0" (not case sensitive). | ||||||
|  |     INI_API bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; | ||||||
|  |  | ||||||
|  |     // Return true if the given section exists (section must contain at least | ||||||
|  |     // one name=value pair). | ||||||
|  |     INI_API bool HasSection(const std::string& section) const; | ||||||
|  |  | ||||||
|  |     // Return true if a value exists with the given section and field names. | ||||||
|  |     INI_API bool HasValue(const std::string& section, const std::string& name) const; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     int _error; | ||||||
|  |     std::map<std::string, std::string> _values; | ||||||
|  |     static std::string MakeKey(const std::string& section, const std::string& name); | ||||||
|  |     static int ValueHandler(void* user, const char* section, const char* name, | ||||||
|  |                             const char* value); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif  // INIREADER_H | ||||||
							
								
								
									
										298
									
								
								3rdparty/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								3rdparty/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,298 @@ | |||||||
|  | /* inih -- simple .INI file parser | ||||||
|  |  | ||||||
|  | SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  | ||||||
|  | Copyright (C) 2009-2020, Ben Hoyt | ||||||
|  |  | ||||||
|  | inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||||
|  | home page for more info: | ||||||
|  |  | ||||||
|  | https://github.com/benhoyt/inih | ||||||
|  |  | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) | ||||||
|  | #define _CRT_SECURE_NO_WARNINGS | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "ini.h" | ||||||
|  |  | ||||||
|  | #if !INI_USE_STACK | ||||||
|  | #if INI_CUSTOM_ALLOCATOR | ||||||
|  | #include <stddef.h> | ||||||
|  | void* ini_malloc(size_t size); | ||||||
|  | void ini_free(void* ptr); | ||||||
|  | void* ini_realloc(void* ptr, size_t size); | ||||||
|  | #else | ||||||
|  | #include <stdlib.h> | ||||||
|  | #define ini_malloc malloc | ||||||
|  | #define ini_free free | ||||||
|  | #define ini_realloc realloc | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define MAX_SECTION 50 | ||||||
|  | #define MAX_NAME 50 | ||||||
|  |  | ||||||
|  | /* Used by ini_parse_string() to keep track of string parsing state. */ | ||||||
|  | typedef struct { | ||||||
|  |     const char* ptr; | ||||||
|  |     size_t num_left; | ||||||
|  | } ini_parse_string_ctx; | ||||||
|  |  | ||||||
|  | /* Strip whitespace chars off end of given string, in place. Return s. */ | ||||||
|  | static char* rstrip(char* s) | ||||||
|  | { | ||||||
|  |     char* p = s + strlen(s); | ||||||
|  |     while (p > s && isspace((unsigned char)(*--p))) | ||||||
|  |         *p = '\0'; | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return pointer to first non-whitespace char in given string. */ | ||||||
|  | static char* lskip(const char* s) | ||||||
|  | { | ||||||
|  |     while (*s && isspace((unsigned char)(*s))) | ||||||
|  |         s++; | ||||||
|  |     return (char*)s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return pointer to first char (of chars) or inline comment in given string, | ||||||
|  |    or pointer to NUL at end of string if neither found. Inline comment must | ||||||
|  |    be prefixed by a whitespace character to register as a comment. */ | ||||||
|  | static char* find_chars_or_comment(const char* s, const char* chars) | ||||||
|  | { | ||||||
|  | #if INI_ALLOW_INLINE_COMMENTS | ||||||
|  |     int was_space = 0; | ||||||
|  |     while (*s && (!chars || !strchr(chars, *s)) && | ||||||
|  |            !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { | ||||||
|  |         was_space = isspace((unsigned char)(*s)); | ||||||
|  |         s++; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     while (*s && (!chars || !strchr(chars, *s))) { | ||||||
|  |         s++; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |     return (char*)s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Similar to strncpy, but ensures dest (size bytes) is | ||||||
|  |    NUL-terminated, and doesn't pad with NULs. */ | ||||||
|  | static char* strncpy0(char* dest, const char* src, size_t size) | ||||||
|  | { | ||||||
|  |     /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ | ||||||
|  |     size_t i; | ||||||
|  |     for (i = 0; i < size - 1 && src[i]; i++) | ||||||
|  |         dest[i] = src[i]; | ||||||
|  |     dest[i] = '\0'; | ||||||
|  |     return dest; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* See documentation in header file. */ | ||||||
|  | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||||
|  |                      void* user) | ||||||
|  | { | ||||||
|  |     /* Uses a fair bit of stack (use heap instead if you need to) */ | ||||||
|  | #if INI_USE_STACK | ||||||
|  |     char line[INI_MAX_LINE]; | ||||||
|  |     int max_line = INI_MAX_LINE; | ||||||
|  | #else | ||||||
|  |     char* line; | ||||||
|  |     size_t max_line = INI_INITIAL_ALLOC; | ||||||
|  | #endif | ||||||
|  | #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||||
|  |     char* new_line; | ||||||
|  |     size_t offset; | ||||||
|  | #endif | ||||||
|  |     char section[MAX_SECTION] = ""; | ||||||
|  |     char prev_name[MAX_NAME] = ""; | ||||||
|  |  | ||||||
|  |     char* start; | ||||||
|  |     char* end; | ||||||
|  |     char* name; | ||||||
|  |     char* value; | ||||||
|  |     int lineno = 0; | ||||||
|  |     int error = 0; | ||||||
|  |  | ||||||
|  | #if !INI_USE_STACK | ||||||
|  |     line = (char*)ini_malloc(INI_INITIAL_ALLOC); | ||||||
|  |     if (!line) { | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if INI_HANDLER_LINENO | ||||||
|  | #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) | ||||||
|  | #else | ||||||
|  | #define HANDLER(u, s, n, v) handler(u, s, n, v) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     /* Scan through stream line by line */ | ||||||
|  |     while (reader(line, (int)max_line, stream) != NULL) { | ||||||
|  | #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||||
|  |         offset = strlen(line); | ||||||
|  |         while (offset == max_line - 1 && line[offset - 1] != '\n') { | ||||||
|  |             max_line *= 2; | ||||||
|  |             if (max_line > INI_MAX_LINE) | ||||||
|  |                 max_line = INI_MAX_LINE; | ||||||
|  |             new_line = ini_realloc(line, max_line); | ||||||
|  |             if (!new_line) { | ||||||
|  |                 ini_free(line); | ||||||
|  |                 return -2; | ||||||
|  |             } | ||||||
|  |             line = new_line; | ||||||
|  |             if (reader(line + offset, (int)(max_line - offset), stream) == NULL) | ||||||
|  |                 break; | ||||||
|  |             if (max_line >= INI_MAX_LINE) | ||||||
|  |                 break; | ||||||
|  |             offset += strlen(line + offset); | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |         lineno++; | ||||||
|  |  | ||||||
|  |         start = line; | ||||||
|  | #if INI_ALLOW_BOM | ||||||
|  |         if (lineno == 1 && (unsigned char)start[0] == 0xEF && | ||||||
|  |                            (unsigned char)start[1] == 0xBB && | ||||||
|  |                            (unsigned char)start[2] == 0xBF) { | ||||||
|  |             start += 3; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |         start = lskip(rstrip(start)); | ||||||
|  |  | ||||||
|  |         if (strchr(INI_START_COMMENT_PREFIXES, *start)) { | ||||||
|  |             /* Start-of-line comment */ | ||||||
|  |         } | ||||||
|  | #if INI_ALLOW_MULTILINE | ||||||
|  |         else if (*prev_name && *start && start > line) { | ||||||
|  |             /* Non-blank line with leading whitespace, treat as continuation | ||||||
|  |                of previous name's value (as per Python configparser). */ | ||||||
|  |             if (!HANDLER(user, section, prev_name, start) && !error) | ||||||
|  |                 error = lineno; | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |         else if (*start == '[') { | ||||||
|  |             /* A "[section]" line */ | ||||||
|  |             end = find_chars_or_comment(start + 1, "]"); | ||||||
|  |             if (*end == ']') { | ||||||
|  |                 *end = '\0'; | ||||||
|  |                 strncpy0(section, start + 1, sizeof(section)); | ||||||
|  |                 *prev_name = '\0'; | ||||||
|  | #if INI_CALL_HANDLER_ON_NEW_SECTION | ||||||
|  |                 if (!HANDLER(user, section, NULL, NULL) && !error) | ||||||
|  |                     error = lineno; | ||||||
|  | #endif | ||||||
|  |             } | ||||||
|  |             else if (!error) { | ||||||
|  |                 /* No ']' found on section line */ | ||||||
|  |                 error = lineno; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if (*start) { | ||||||
|  |             /* Not a comment, must be a name[=:]value pair */ | ||||||
|  |             end = find_chars_or_comment(start, "=:"); | ||||||
|  |             if (*end == '=' || *end == ':') { | ||||||
|  |                 *end = '\0'; | ||||||
|  |                 name = rstrip(start); | ||||||
|  |                 value = end + 1; | ||||||
|  | #if INI_ALLOW_INLINE_COMMENTS | ||||||
|  |                 end = find_chars_or_comment(value, NULL); | ||||||
|  |                 if (*end) | ||||||
|  |                     *end = '\0'; | ||||||
|  | #endif | ||||||
|  |                 value = lskip(value); | ||||||
|  |                 rstrip(value); | ||||||
|  |  | ||||||
|  |                 /* Valid name[=:]value pair found, call handler */ | ||||||
|  |                 strncpy0(prev_name, name, sizeof(prev_name)); | ||||||
|  |                 if (!HANDLER(user, section, name, value) && !error) | ||||||
|  |                     error = lineno; | ||||||
|  |             } | ||||||
|  |             else if (!error) { | ||||||
|  |                 /* No '=' or ':' found on name[=:]value line */ | ||||||
|  | #if INI_ALLOW_NO_VALUE | ||||||
|  |                 *end = '\0'; | ||||||
|  |                 name = rstrip(start); | ||||||
|  |                 if (!HANDLER(user, section, name, NULL) && !error) | ||||||
|  |                     error = lineno; | ||||||
|  | #else | ||||||
|  |                 error = lineno; | ||||||
|  | #endif | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #if INI_STOP_ON_FIRST_ERROR | ||||||
|  |         if (error) | ||||||
|  |             break; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if !INI_USE_STACK | ||||||
|  |     ini_free(line); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     return error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* See documentation in header file. */ | ||||||
|  | int ini_parse_file(FILE* file, ini_handler handler, void* user) | ||||||
|  | { | ||||||
|  |     return ini_parse_stream((ini_reader)fgets, file, handler, user); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* See documentation in header file. */ | ||||||
|  | int ini_parse(const char* filename, ini_handler handler, void* user) | ||||||
|  | { | ||||||
|  |     FILE* file; | ||||||
|  |     int error; | ||||||
|  |  | ||||||
|  |     file = fopen(filename, "r"); | ||||||
|  |     if (!file) | ||||||
|  |         return -1; | ||||||
|  |     error = ini_parse_file(file, handler, user); | ||||||
|  |     fclose(file); | ||||||
|  |     return error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* An ini_reader function to read the next line from a string buffer. This | ||||||
|  |    is the fgets() equivalent used by ini_parse_string(). */ | ||||||
|  | static char* ini_reader_string(char* str, int num, void* stream) { | ||||||
|  |     ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; | ||||||
|  |     const char* ctx_ptr = ctx->ptr; | ||||||
|  |     size_t ctx_num_left = ctx->num_left; | ||||||
|  |     char* strp = str; | ||||||
|  |     char c; | ||||||
|  |  | ||||||
|  |     if (ctx_num_left == 0 || num < 2) | ||||||
|  |         return NULL; | ||||||
|  |  | ||||||
|  |     while (num > 1 && ctx_num_left != 0) { | ||||||
|  |         c = *ctx_ptr++; | ||||||
|  |         ctx_num_left--; | ||||||
|  |         *strp++ = c; | ||||||
|  |         if (c == '\n') | ||||||
|  |             break; | ||||||
|  |         num--; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     *strp = '\0'; | ||||||
|  |     ctx->ptr = ctx_ptr; | ||||||
|  |     ctx->num_left = ctx_num_left; | ||||||
|  |     return str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* See documentation in header file. */ | ||||||
|  | int ini_parse_string(const char* string, ini_handler handler, void* user) { | ||||||
|  |     ini_parse_string_ctx ctx; | ||||||
|  |  | ||||||
|  |     ctx.ptr = string; | ||||||
|  |     ctx.num_left = strlen(string); | ||||||
|  |     return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, | ||||||
|  |                             user); | ||||||
|  | } | ||||||
							
								
								
									
										178
									
								
								3rdparty/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								3rdparty/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | |||||||
|  | /* inih -- simple .INI file parser | ||||||
|  |  | ||||||
|  | SPDX-License-Identifier: BSD-3-Clause | ||||||
|  |  | ||||||
|  | Copyright (C) 2009-2020, Ben Hoyt | ||||||
|  |  | ||||||
|  | inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||||
|  | home page for more info: | ||||||
|  |  | ||||||
|  | https://github.com/benhoyt/inih | ||||||
|  |  | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef INI_H | ||||||
|  | #define INI_H | ||||||
|  |  | ||||||
|  | /* Make this header file easier to include in C++ code */ | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | /* Nonzero if ini_handler callback should accept lineno parameter. */ | ||||||
|  | #ifndef INI_HANDLER_LINENO | ||||||
|  | #define INI_HANDLER_LINENO 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Visibility symbols, required for Windows DLLs */ | ||||||
|  | #ifndef INI_API | ||||||
|  | #if defined _WIN32 || defined __CYGWIN__ | ||||||
|  | #	ifdef INI_SHARED_LIB | ||||||
|  | #		ifdef INI_SHARED_LIB_BUILDING | ||||||
|  | #			define INI_API __declspec(dllexport) | ||||||
|  | #		else | ||||||
|  | #			define INI_API __declspec(dllimport) | ||||||
|  | #		endif | ||||||
|  | #	else | ||||||
|  | #		define INI_API | ||||||
|  | #	endif | ||||||
|  | #else | ||||||
|  | #	if defined(__GNUC__) && __GNUC__ >= 4 | ||||||
|  | #		define INI_API __attribute__ ((visibility ("default"))) | ||||||
|  | #	else | ||||||
|  | #		define INI_API | ||||||
|  | #	endif | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Typedef for prototype of handler function. */ | ||||||
|  | #if INI_HANDLER_LINENO | ||||||
|  | typedef int (*ini_handler)(void* user, const char* section, | ||||||
|  |                            const char* name, const char* value, | ||||||
|  |                            int lineno); | ||||||
|  | #else | ||||||
|  | typedef int (*ini_handler)(void* user, const char* section, | ||||||
|  |                            const char* name, const char* value); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Typedef for prototype of fgets-style reader function. */ | ||||||
|  | typedef char* (*ini_reader)(char* str, int num, void* stream); | ||||||
|  |  | ||||||
|  | /* Parse given INI-style file. May have [section]s, name=value pairs | ||||||
|  |    (whitespace stripped), and comments starting with ';' (semicolon). Section | ||||||
|  |    is "" if name=value pair parsed before any section heading. name:value | ||||||
|  |    pairs are also supported as a concession to Python's configparser. | ||||||
|  |  | ||||||
|  |    For each name=value pair parsed, call handler function with given user | ||||||
|  |    pointer as well as section, name, and value (data only valid for duration | ||||||
|  |    of handler call). Handler should return nonzero on success, zero on error. | ||||||
|  |  | ||||||
|  |    Returns 0 on success, line number of first error on parse error (doesn't | ||||||
|  |    stop on first error), -1 on file open error, or -2 on memory allocation | ||||||
|  |    error (only when INI_USE_STACK is zero). | ||||||
|  | */ | ||||||
|  | INI_API int ini_parse(const char* filename, ini_handler handler, void* user); | ||||||
|  |  | ||||||
|  | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't | ||||||
|  |    close the file when it's finished -- the caller must do that. */ | ||||||
|  | INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user); | ||||||
|  |  | ||||||
|  | /* Same as ini_parse(), but takes an ini_reader function pointer instead of | ||||||
|  |    filename. Used for implementing custom or string-based I/O (see also | ||||||
|  |    ini_parse_string). */ | ||||||
|  | INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||||
|  |                      void* user); | ||||||
|  |  | ||||||
|  | /* Same as ini_parse(), but takes a zero-terminated string with the INI data | ||||||
|  | instead of a file. Useful for parsing INI data from a network socket or | ||||||
|  | already in memory. */ | ||||||
|  | INI_API int ini_parse_string(const char* string, ini_handler handler, void* user); | ||||||
|  |  | ||||||
|  | /* Nonzero to allow multi-line value parsing, in the style of Python's | ||||||
|  |    configparser. If allowed, ini_parse() will call the handler with the same | ||||||
|  |    name for each subsequent line parsed. */ | ||||||
|  | #ifndef INI_ALLOW_MULTILINE | ||||||
|  | #define INI_ALLOW_MULTILINE 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of | ||||||
|  |    the file. See https://github.com/benhoyt/inih/issues/21 */ | ||||||
|  | #ifndef INI_ALLOW_BOM | ||||||
|  | #define INI_ALLOW_BOM 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Chars that begin a start-of-line comment. Per Python configparser, allow | ||||||
|  |    both ; and # comments at the start of a line by default. */ | ||||||
|  | #ifndef INI_START_COMMENT_PREFIXES | ||||||
|  | #define INI_START_COMMENT_PREFIXES ";#" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to allow inline comments (with valid inline comment characters | ||||||
|  |    specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match | ||||||
|  |    Python 3.2+ configparser behaviour. */ | ||||||
|  | #ifndef INI_ALLOW_INLINE_COMMENTS | ||||||
|  | #define INI_ALLOW_INLINE_COMMENTS 1 | ||||||
|  | #endif | ||||||
|  | #ifndef INI_INLINE_COMMENT_PREFIXES | ||||||
|  | #define INI_INLINE_COMMENT_PREFIXES ";" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ | ||||||
|  | #ifndef INI_USE_STACK | ||||||
|  | #define INI_USE_STACK 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Maximum line length for any line in INI file (stack or heap). Note that | ||||||
|  |    this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ | ||||||
|  | #ifndef INI_MAX_LINE | ||||||
|  | #define INI_MAX_LINE 200 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a | ||||||
|  |    fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is | ||||||
|  |    zero. */ | ||||||
|  | #ifndef INI_ALLOW_REALLOC | ||||||
|  | #define INI_ALLOW_REALLOC 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK | ||||||
|  |    is zero. */ | ||||||
|  | #ifndef INI_INITIAL_ALLOC | ||||||
|  | #define INI_INITIAL_ALLOC 200 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Stop parsing on first error (default is to keep parsing). */ | ||||||
|  | #ifndef INI_STOP_ON_FIRST_ERROR | ||||||
|  | #define INI_STOP_ON_FIRST_ERROR 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to call the handler at the start of each new section (with | ||||||
|  |    name and value NULL). Default is to only call the handler on | ||||||
|  |    each name=value pair. */ | ||||||
|  | #ifndef INI_CALL_HANDLER_ON_NEW_SECTION | ||||||
|  | #define INI_CALL_HANDLER_ON_NEW_SECTION 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to allow a name without a value (no '=' or ':' on the line) and | ||||||
|  |    call the handler with value NULL in this case. Default is to treat | ||||||
|  |    no-value lines as an error. */ | ||||||
|  | #ifndef INI_ALLOW_NO_VALUE | ||||||
|  | #define INI_ALLOW_NO_VALUE 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory | ||||||
|  |    allocation functions (INI_USE_STACK must also be 0). These functions must | ||||||
|  |    have the same signatures as malloc/free/realloc and behave in a similar | ||||||
|  |    way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ | ||||||
|  | #ifndef INI_CUSTOM_ALLOCATOR | ||||||
|  | #define INI_CUSTOM_ALLOCATOR 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* INI_H */ | ||||||
							
								
								
									
										1
									
								
								3rdparty/plog
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								3rdparty/plog
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule 3rdparty/plog added at 914e799d2b
									
								
							
							
								
								
									
										14
									
								
								3rdparty/qt-unix-signals/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								3rdparty/qt-unix-signals/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | cmake_minimum_required(VERSION 3.1.0) | ||||||
|  |  | ||||||
|  | project(QtSignals LANGUAGES CXX) | ||||||
|  |  | ||||||
|  | set(CMAKE_CXX_STANDARD 17) | ||||||
|  | set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||||||
|  | # Instruct CMake to run moc automatically when needed. | ||||||
|  | set(CMAKE_AUTOMOC ON) | ||||||
|  |  | ||||||
|  | find_package(Qt5 REQUIRED COMPONENTS Core) | ||||||
|  |  | ||||||
|  | add_library(QtSignals STATIC sigwatch.cpp) | ||||||
|  | target_include_directories(QtSignals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
|  | target_link_libraries(QtSignals Qt5::Core) | ||||||
							
								
								
									
										21
									
								
								3rdparty/qt-unix-signals/LICENCE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								3rdparty/qt-unix-signals/LICENCE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | 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. | ||||||
							
								
								
									
										1
									
								
								3rdparty/qtkeychain
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								3rdparty/qtkeychain
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule 3rdparty/qtkeychain added at f197cdb935
									
								
							
							
								
								
									
										39
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | cmake_minimum_required(VERSION 3.10.0) | ||||||
|  |  | ||||||
|  | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") | ||||||
|  | set(CMAKE_CXX_STANDARD 17) | ||||||
|  | set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||||||
|  |  | ||||||
|  | set(CMAKE_AUTOMOC ON) | ||||||
|  | set(CMAKE_AUTORCC ON) | ||||||
|  | set(CMAKE_AUTOUIC ON) | ||||||
|  |  | ||||||
|  | file(STRINGS "VERSION" version) | ||||||
|  | project(GlobalProtect-openconnect LANGUAGES CXX) | ||||||
|  |  | ||||||
|  | # Set the CMAKE_INSTALL_PREFIX to /usr if not specified | ||||||
|  | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) | ||||||
|  |     set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "The default install prefix" FORCE) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | message(STATUS "CMAKE_INSTALL_PREFIX was set to: ${CMAKE_INSTALL_PREFIX}") | ||||||
|  |  | ||||||
|  | configure_file(version.h.in version.h) | ||||||
|  |  | ||||||
|  | find_package(Qt5 REQUIRED COMPONENTS | ||||||
|  |     Core | ||||||
|  |     Widgets | ||||||
|  |     Network | ||||||
|  |     WebSockets | ||||||
|  |     WebEngine | ||||||
|  |     WebEngineWidgets | ||||||
|  |     DBus | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | find_package(Qt5Keychain REQUIRED) | ||||||
|  |  | ||||||
|  | add_subdirectory(3rdparty/qt-unix-signals) | ||||||
|  | add_subdirectory(3rdparty/inih) | ||||||
|  | add_subdirectory(GPService) | ||||||
|  | add_subdirectory(GPClient) | ||||||
|  | add_dependencies(gpclient gpservice) | ||||||
							
								
								
									
										111
									
								
								GPClient/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								GPClient/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | include("${CMAKE_SOURCE_DIR}/cmake/Add3rdParty.cmake") | ||||||
|  |  | ||||||
|  | project(GPClient) | ||||||
|  |  | ||||||
|  | set(gpclient_GENERATED_SOURCES) | ||||||
|  |  | ||||||
|  | configure_file(com.yuezk.qt.gpclient.desktop.in com.yuezk.qt.gpclient.desktop) | ||||||
|  | configure_file(com.yuezk.qt.gpclient.metainfo.xml.in com.yuezk.qt.gpclient.metainfo.xml) | ||||||
|  |  | ||||||
|  | qt5_add_dbus_interface( | ||||||
|  |     gpclient_GENERATED_SOURCES | ||||||
|  |     ${CMAKE_BINARY_DIR}/com.yuezk.qt.GPService.xml | ||||||
|  |     gpserviceinterface | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_executable(gpclient | ||||||
|  |     cdpcommand.cpp | ||||||
|  |     cdpcommandmanager.cpp | ||||||
|  |     enhancedwebview.cpp | ||||||
|  |     enhancedwebpage.cpp | ||||||
|  |     gatewayauthenticator.cpp | ||||||
|  |     gatewayauthenticatorparams.cpp | ||||||
|  |     gpgateway.cpp | ||||||
|  |     gphelper.cpp | ||||||
|  |     loginparams.cpp | ||||||
|  |     main.cpp | ||||||
|  |     standardloginwindow.cpp | ||||||
|  |     portalauthenticator.cpp | ||||||
|  |     portalconfigresponse.cpp | ||||||
|  |     preloginresponse.cpp | ||||||
|  |     samlloginwindow.cpp | ||||||
|  |     gpclient.cpp | ||||||
|  |     settingsdialog.cpp | ||||||
|  |     gpclient.ui | ||||||
|  |     standardloginwindow.ui | ||||||
|  |     settingsdialog.ui | ||||||
|  |     challengedialog.h | ||||||
|  |     challengedialog.cpp | ||||||
|  |     challengedialog.ui | ||||||
|  |     vpn_dbus.cpp | ||||||
|  |     vpn_json.cpp | ||||||
|  |     resources.qrc | ||||||
|  |     ${gpclient_GENERATED_SOURCES} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_3rdparty( | ||||||
|  |     SingleApplication | ||||||
|  |     GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication.git | ||||||
|  |     GIT_TAG v3.3.0 | ||||||
|  |     CMAKE_ARGS | ||||||
|  |         -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} | ||||||
|  |         -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} | ||||||
|  |         -DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} | ||||||
|  |         -DCMAKE_PREFIX_PATH=$ENV{CMAKE_PREFIX_PATH} | ||||||
|  |         -DQAPPLICATION_CLASS=QApplication | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_3rdparty( | ||||||
|  |     plog | ||||||
|  |     GIT_REPOSITORY https://github.com/SergiusTheBest/plog.git | ||||||
|  |     GIT_TAG master | ||||||
|  |     CMAKE_ARGS | ||||||
|  |         -DPLOG_BUILD_SAMPLES=OFF | ||||||
|  |         -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} | ||||||
|  |         -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ExternalProject_Get_Property(SingleApplication-${PROJECT_NAME} SOURCE_DIR BINARY_DIR) | ||||||
|  | set(SingleApplication_INCLUDE_DIR ${SOURCE_DIR}) | ||||||
|  | set(SingleApplication_LIBRARY ${BINARY_DIR}/libSingleApplication.a) | ||||||
|  |  | ||||||
|  | ExternalProject_Get_Property(plog-${PROJECT_NAME} SOURCE_DIR) | ||||||
|  | set(plog_INCLUDE_DIR "${SOURCE_DIR}/include") | ||||||
|  |  | ||||||
|  | add_dependencies(gpclient | ||||||
|  |     SingleApplication-${PROJECT_NAME} | ||||||
|  |     plog-${PROJECT_NAME} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | target_include_directories(gpclient PRIVATE | ||||||
|  |     ${CMAKE_BINARY_DIR} | ||||||
|  |     ${CMAKE_CURRENT_SOURCE_DIR} | ||||||
|  |     ${CMAKE_CURRENT_BINARY_DIR} | ||||||
|  |     ${SingleApplication_INCLUDE_DIR} | ||||||
|  |     ${plog_INCLUDE_DIR} | ||||||
|  |     ${QTKEYCHAIN_INCLUDE_DIRS}/qt5keychain | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | target_link_libraries(gpclient | ||||||
|  |     ${SingleApplication_LIBRARY} | ||||||
|  |     Qt5::Widgets | ||||||
|  |     Qt5::Network | ||||||
|  |     Qt5::WebSockets | ||||||
|  |     Qt5::WebEngine | ||||||
|  |     Qt5::WebEngineWidgets | ||||||
|  |     Qt5::DBus | ||||||
|  |     QtSignals | ||||||
|  |     ${QTKEYCHAIN_LIBRARIES} | ||||||
|  |     inih | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0 AND CMAKE_BUILD_TYPE STREQUAL Release) | ||||||
|  |     target_compile_options(gpclient PUBLIC "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | target_compile_definitions(gpclient PUBLIC QAPPLICATION_CLASS=QApplication) | ||||||
|  |  | ||||||
|  | install(TARGETS gpclient DESTINATION bin) | ||||||
|  | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.yuezk.qt.gpclient.metainfo.xml" DESTINATION share/metainfo) | ||||||
|  | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.yuezk.qt.gpclient.desktop" DESTINATION share/applications) | ||||||
|  | install(FILES "com.yuezk.qt.gpclient.svg" DESTINATION share/icons/hicolor/scalable/apps) | ||||||
| @@ -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,8 +1,8 @@ | |||||||
| #include "cdpcommand.h" | #include <QtCore/QVariantMap> | ||||||
|  | #include <QtCore/QJsonDocument> | ||||||
|  | #include <QtCore/QJsonObject> | ||||||
|  |  | ||||||
| #include <QVariantMap> | #include "cdpcommand.h" | ||||||
| #include <QJsonDocument> |  | ||||||
| #include <QJsonObject> |  | ||||||
|  |  | ||||||
| CDPCommand::CDPCommand(QObject *parent) : QObject(parent) | CDPCommand::CDPCommand(QObject *parent) : QObject(parent) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| #ifndef CDPCOMMAND_H | #ifndef CDPCOMMAND_H | ||||||
| #define CDPCOMMAND_H | #define CDPCOMMAND_H | ||||||
|  |  | ||||||
| #include <QObject> | #include <QtCore/QObject> | ||||||
|  |  | ||||||
| class CDPCommand : public QObject | class CDPCommand : public QObject | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| #include "cdpcommandmanager.h" | #include <QtCore/QVariantMap> | ||||||
| #include <QVariantMap> |  | ||||||
| #include <plog/Log.h> | #include <plog/Log.h> | ||||||
|  |  | ||||||
|  | #include "cdpcommandmanager.h" | ||||||
|  |  | ||||||
| CDPCommandManager::CDPCommandManager(QObject *parent) | CDPCommandManager::CDPCommandManager(QObject *parent) | ||||||
|     : QObject(parent) |     : QObject(parent) | ||||||
|     , networkManager(new QNetworkAccessManager) |     , networkManager(new QNetworkAccessManager) | ||||||
| @@ -28,7 +29,7 @@ void CDPCommandManager::initialize(QString endpoint) | |||||||
|         reply, &QNetworkReply::finished, |         reply, &QNetworkReply::finished, | ||||||
|         [reply, this]() { |         [reply, this]() { | ||||||
|             if (reply->error()) { |             if (reply->error()) { | ||||||
|                 PLOGE << "CDP request error"; |                 LOGE << "CDP request error"; | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -77,10 +78,10 @@ void CDPCommandManager::onTextMessageReceived(QString message) | |||||||
|  |  | ||||||
| void CDPCommandManager::onSocketDisconnected() | void CDPCommandManager::onSocketDisconnected() | ||||||
| { | { | ||||||
|     PLOGI << "WebSocket disconnected"; |     LOGI << "WebSocket disconnected"; | ||||||
| } | } | ||||||
|  |  | ||||||
| void CDPCommandManager::onSocketError(QAbstractSocket::SocketError error) | void CDPCommandManager::onSocketError(QAbstractSocket::SocketError error) | ||||||
| { | { | ||||||
|     PLOGE << "WebSocket error" << error; |     LOGE << "WebSocket error" << error; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| #ifndef CDPCOMMANDMANAGER_H | #ifndef CDPCOMMANDMANAGER_H | ||||||
| #define CDPCOMMANDMANAGER_H | #define CDPCOMMANDMANAGER_H | ||||||
|  |  | ||||||
|  | #include <QtCore/QObject> | ||||||
|  | #include <QtCore/QHash> | ||||||
|  | #include <QtWebSockets/QtWebSockets> | ||||||
|  | #include <QtNetwork/QNetworkAccessManager> | ||||||
|  |  | ||||||
| #include "cdpcommand.h" | #include "cdpcommand.h" | ||||||
| #include <QObject> |  | ||||||
| #include <QHash> |  | ||||||
| #include <QtWebSockets> |  | ||||||
| #include <QNetworkAccessManager> |  | ||||||
|  |  | ||||||
| class CDPCommandManager : public QObject | class CDPCommandManager : public QObject | ||||||
| { | { | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								GPClient/challengedialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								GPClient/challengedialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | #include <QtWidgets/QDialogButtonBox> | ||||||
|  | #include <QtWidgets/QPushButton> | ||||||
|  |  | ||||||
|  | #include "challengedialog.h" | ||||||
|  | #include "ui_challengedialog.h" | ||||||
|  |  | ||||||
|  | ChallengeDialog::ChallengeDialog(QWidget *parent) : | ||||||
|  |     QDialog(parent), | ||||||
|  |     ui(new Ui::ChallengeDialog) | ||||||
|  | { | ||||||
|  |     ui->setupUi(this); | ||||||
|  |     ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ChallengeDialog::~ChallengeDialog() | ||||||
|  | { | ||||||
|  |     delete ui; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ChallengeDialog::setMessage(const QString &message) | ||||||
|  | { | ||||||
|  |     ui->challengeMessage->setText(message); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString ChallengeDialog::getChallenge() | ||||||
|  | { | ||||||
|  |     return ui->challengeInput->text(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ChallengeDialog::on_challengeInput_textChanged(const QString &value) | ||||||
|  | { | ||||||
|  |     QPushButton *okBtn = ui->buttonBox->button(QDialogButtonBox::Ok); | ||||||
|  |     if (value.isEmpty()) { | ||||||
|  |         okBtn->setDisabled(true); | ||||||
|  |     } else { | ||||||
|  |         okBtn->setEnabled(true); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								GPClient/challengedialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								GPClient/challengedialog.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | #ifndef CHALLENGEDIALOG_H | ||||||
|  | #define CHALLENGEDIALOG_H | ||||||
|  |  | ||||||
|  | #include <QDialog> | ||||||
|  |  | ||||||
|  | namespace Ui { | ||||||
|  | class ChallengeDialog; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class ChallengeDialog : public QDialog | ||||||
|  | { | ||||||
|  |     Q_OBJECT | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit ChallengeDialog(QWidget *parent = nullptr); | ||||||
|  |     ~ChallengeDialog(); | ||||||
|  |  | ||||||
|  |     void setMessage(const QString &message); | ||||||
|  |     const QString getChallenge(); | ||||||
|  |  | ||||||
|  | private slots: | ||||||
|  |     void on_challengeInput_textChanged(const QString &arg1); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Ui::ChallengeDialog *ui; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // CHALLENGEDIALOG_H | ||||||
							
								
								
									
										111
									
								
								GPClient/challengedialog.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								GPClient/challengedialog.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>ChallengeDialog</class> | ||||||
|  |  <widget class="QDialog" name="ChallengeDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>405</width> | ||||||
|  |     <height>200</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>GlobalProtect Challenge</string> | ||||||
|  |   </property> | ||||||
|  |   <property name="modal"> | ||||||
|  |    <bool>true</bool> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <item> | ||||||
|  |     <layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,1"> | ||||||
|  |      <item> | ||||||
|  |       <widget class="QLabel" name="label"> | ||||||
|  |        <property name="font"> | ||||||
|  |         <font> | ||||||
|  |          <pointsize>14</pointsize> | ||||||
|  |          <weight>50</weight> | ||||||
|  |          <bold>false</bold> | ||||||
|  |         </font> | ||||||
|  |        </property> | ||||||
|  |        <property name="text"> | ||||||
|  |         <string>Sign In</string> | ||||||
|  |        </property> | ||||||
|  |       </widget> | ||||||
|  |      </item> | ||||||
|  |      <item> | ||||||
|  |       <widget class="QLabel" name="challengeMessage"> | ||||||
|  |        <property name="text"> | ||||||
|  |         <string>Duo two-factor login for [redacted]  Enter a passcode or select one of the following options:   1. Duo Push to XXX-XXX-[redacted]  2. SMS passcodes to XXX-XXX-[redacted]  Passcode or option (1-2): </string> | ||||||
|  |        </property> | ||||||
|  |        <property name="alignment"> | ||||||
|  |         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||||||
|  |        </property> | ||||||
|  |        <property name="wordWrap"> | ||||||
|  |         <bool>true</bool> | ||||||
|  |        </property> | ||||||
|  |       </widget> | ||||||
|  |      </item> | ||||||
|  |     </layout> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QLineEdit" name="challengeInput"> | ||||||
|  |      <property name="echoMode"> | ||||||
|  |       <enum>QLineEdit::Password</enum> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |      <property name="layoutDirection"> | ||||||
|  |       <enum>Qt::LeftToRight</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Horizontal</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="standardButtons"> | ||||||
|  |       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||||
|  |      </property> | ||||||
|  |      <property name="centerButtons"> | ||||||
|  |       <bool>false</bool> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>ChallengeDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>248</x> | ||||||
|  |      <y>254</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>157</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>ChallengeDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>316</x> | ||||||
|  |      <y>260</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>286</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
| @@ -1,10 +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; |  | ||||||
							
								
								
									
										12
									
								
								GPClient/com.yuezk.qt.gpclient.desktop.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								GPClient/com.yuezk.qt.gpclient.desktop.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | [Desktop Entry] | ||||||
|  |  | ||||||
|  | Type=Application | ||||||
|  | Version=1.0 | ||||||
|  | Name=GlobalProtect VPN | ||||||
|  | Comment=A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports SAML auth mode. | ||||||
|  | GenericName=GlobalProtect VPN client, supports SAML auth mode | ||||||
|  | Categories=Network;Dialup; | ||||||
|  | Exec=env QT_AUTO_SCREEN_SCALE_FACTOR=1 @CMAKE_INSTALL_PREFIX@/bin/gpclient | ||||||
|  | Icon=com.yuezk.qt.gpclient | ||||||
|  | Keywords=GlobalProtect;Openconnect;SAML;connection;VPN; | ||||||
|  | StartupWMClass=gpclient | ||||||
							
								
								
									
										43
									
								
								GPClient/com.yuezk.qt.gpclient.metainfo.xml.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								GPClient/com.yuezk.qt.gpclient.metainfo.xml.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <component type="desktop-application"> | ||||||
|  |     <id>com.yuezk.qt.gpclient</id> | ||||||
|  |  | ||||||
|  |     <name>globalprotect-openconnect</name> | ||||||
|  |     <summary>A GlobalProtect VPN client powered by OpenConnect</summary> | ||||||
|  |  | ||||||
|  |     <metadata_license>CC0-1.0</metadata_license> | ||||||
|  |     <project_license>AGPL-3.0-or-later</project_license> | ||||||
|  |  | ||||||
|  |     <description> | ||||||
|  |         <p>A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports the SAML auth mode.</p> | ||||||
|  |     </description> | ||||||
|  |  | ||||||
|  |     <categories> | ||||||
|  |         <category>Network</category> | ||||||
|  |     </categories> | ||||||
|  |  | ||||||
|  |     <update_contact>k3vinyue_AT_gmail.com</update_contact> | ||||||
|  |     <developer_name>Kevin Yue</developer_name> | ||||||
|  |  | ||||||
|  |     <url type="homepage">https://github.com/yuezk/GlobalProtect-openconnect</url> | ||||||
|  |     <url type="bugtracker">https://github.com/yuezk/GlobalProtect-openconnect/issues</url> | ||||||
|  |     <url type="help">https://github.com/yuezk/GlobalProtect-openconnect/issues</url> | ||||||
|  |  | ||||||
|  |     <keywords> | ||||||
|  |         <keyword>globalprotect</keyword> | ||||||
|  |         <keyword>openconnect</keyword> | ||||||
|  |         <keyword>vpn</keyword> | ||||||
|  |         <keyword>saml</keyword> | ||||||
|  |     </keywords> | ||||||
|  |  | ||||||
|  |     <launchable type="desktop-id">com.yuezk.qt.gpclient.desktop</launchable> | ||||||
|  |     <screenshots> | ||||||
|  |         <screenshot type="default"> | ||||||
|  |             <image>https://user-images.githubusercontent.com/3297602/133869036-5c02b0d9-c2d9-4f87-8c81-e44f68cfd6ac.png</image> | ||||||
|  |         </screenshot> | ||||||
|  |     </screenshots> | ||||||
|  |     <provides> | ||||||
|  |         <binary>@CMAKE_INSTALL_PREFIX@/bin/gpclient</binary> | ||||||
|  |         <dbus type="system">com.yuezk.qt.GPService</dbus> | ||||||
|  |     </provides> | ||||||
|  | </component> | ||||||
| @@ -16,7 +16,7 @@ | |||||||
|    viewBox="0 0 96 96" |    viewBox="0 0 96 96" | ||||||
|    style="enable-background:new 0 0 96 96;" |    style="enable-background:new 0 0 96 96;" | ||||||
|    xml:space="preserve" |    xml:space="preserve" | ||||||
|    sodipodi:docname="com.yuezk.qt.GPClient.svg" |    sodipodi:docname="com.yuezk.qt.gpclient.svg" | ||||||
|    inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata |    inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata | ||||||
|    id="metadata14"><rdf:RDF><cc:Work |    id="metadata14"><rdf:RDF><cc:Work | ||||||
|        rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type |        rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type | ||||||
| Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB | 
							
								
								
									
										8
									
								
								GPClient/enhancedwebpage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								GPClient/enhancedwebpage.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | #include "enhancedwebpage.h" | ||||||
|  | #include <QWebEngineCertificateError> | ||||||
|  | #include <plog/Log.h> | ||||||
|  |  | ||||||
|  | bool EnhancedWebPage::certificateError(const QWebEngineCertificateError &certificateError) { | ||||||
|  |     LOGI << "An error occurred during certificate verification for " << certificateError.url().toString() << "; " << certificateError.errorDescription(); | ||||||
|  |     return certificateError.isOverridable(); | ||||||
|  | }; | ||||||
							
								
								
									
										12
									
								
								GPClient/enhancedwebpage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								GPClient/enhancedwebpage.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #ifndef ENHANCEDWEBPAGE_H | ||||||
|  | #define ENHANCEDWEBPAGE_H | ||||||
|  |  | ||||||
|  | #include <QtWebEngineWidgets/qwebenginepage.h> | ||||||
|  |  | ||||||
|  | class EnhancedWebPage : public QWebEnginePage | ||||||
|  | { | ||||||
|  | protected: | ||||||
|  |     bool certificateError(const QWebEngineCertificateError &certificateError) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // !ECHANCEDWEBPAG | ||||||
| @@ -1,25 +1,22 @@ | |||||||
|  | #include <QtCore/QProcessEnvironment> | ||||||
|  | #include <QtWebEngineWidgets/QWebEngineView> | ||||||
|  |  | ||||||
|  | #include "enhancedwebpage.h" | ||||||
| #include "enhancedwebview.h" | #include "enhancedwebview.h" | ||||||
| #include "cdpcommandmanager.h" | #include "cdpcommandmanager.h" | ||||||
|  |  | ||||||
| #include <QtWebEngineWidgets/QWebEngineView> |  | ||||||
| #include <QProcessEnvironment> |  | ||||||
|  |  | ||||||
| EnhancedWebView::EnhancedWebView(QWidget *parent) | EnhancedWebView::EnhancedWebView(QWidget *parent) | ||||||
|     : QWebEngineView(parent) |     : QWebEngineView(parent) | ||||||
|     , cdp(new CDPCommandManager) |     , cdp(new CDPCommandManager) | ||||||
| { | { | ||||||
|     QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady); |    QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady); | ||||||
|     QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived); |    QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived); | ||||||
| } |  | ||||||
|  |  | ||||||
| EnhancedWebView::~EnhancedWebView() |  | ||||||
| { |  | ||||||
|     delete cdp; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void EnhancedWebView::initialize() | void EnhancedWebView::initialize() | ||||||
| { | { | ||||||
|     QString port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT); |     setPage(new EnhancedWebPage()); | ||||||
|  |     auto port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT); | ||||||
|     cdp->initialize("http://127.0.0.1:" + port + "/json"); |     cdp->initialize("http://127.0.0.1:" + port + "/json"); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| #ifndef ENHANCEDWEBVIEW_H | #ifndef ENHANCEDWEBVIEW_H | ||||||
| #define ENHANCEDWEBVIEW_H | #define ENHANCEDWEBVIEW_H | ||||||
|  |  | ||||||
| #include "cdpcommandmanager.h" |  | ||||||
| #include <QtWebEngineWidgets/QWebEngineView> | #include <QtWebEngineWidgets/QWebEngineView> | ||||||
|  |  | ||||||
|  | #include "cdpcommandmanager.h" | ||||||
|  |  | ||||||
| #define ENV_CDP_PORT "QTWEBENGINE_REMOTE_DEBUGGING" | #define ENV_CDP_PORT "QTWEBENGINE_REMOTE_DEBUGGING" | ||||||
|  |  | ||||||
| class EnhancedWebView : public QWebEngineView | class EnhancedWebView : public QWebEngineView | ||||||
| @@ -11,7 +12,6 @@ class EnhancedWebView : public QWebEngineView | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: | public: | ||||||
|     explicit EnhancedWebView(QWidget *parent = nullptr); |     explicit EnhancedWebView(QWidget *parent = nullptr); | ||||||
|     ~EnhancedWebView(); |  | ||||||
|  |  | ||||||
|     void initialize(); |     void initialize(); | ||||||
|  |  | ||||||
| @@ -23,7 +23,7 @@ private slots: | |||||||
|     void onEventReceived(QString eventName, QJsonObject params); |     void onEventReceived(QString eventName, QJsonObject params); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     CDPCommandManager *cdp; |     CDPCommandManager *cdp { nullptr }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // ENHANCEDWEBVIEW_H | #endif // ENHANCEDWEBVIEW_H | ||||||
|   | |||||||
| @@ -1,55 +1,59 @@ | |||||||
|  | #include <QtNetwork/QNetworkReply> | ||||||
|  | #include <QtCore/QRegularExpression> | ||||||
|  | #include <QtCore/QRegularExpressionMatch> | ||||||
|  | #include <plog/Log.h> | ||||||
|  |  | ||||||
| #include "gatewayauthenticator.h" | #include "gatewayauthenticator.h" | ||||||
| #include "gphelper.h" | #include "gphelper.h" | ||||||
| #include "loginparams.h" | #include "loginparams.h" | ||||||
| #include "preloginresponse.h" | #include "preloginresponse.h" | ||||||
|  | #include "challengedialog.h" | ||||||
| #include <QNetworkReply> |  | ||||||
| #include <plog/Log.h> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; | using namespace gpclient::helper; | ||||||
|  |  | ||||||
| GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig) | GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params) | ||||||
|     : QObject() |     : QObject() | ||||||
|     , preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") |     , gateway(gateway) | ||||||
|  |     , params(params) | ||||||
|  |     , preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100") | ||||||
|     , loginUrl("https://" + gateway + "/ssl-vpn/login.esp") |     , loginUrl("https://" + gateway + "/ssl-vpn/login.esp") | ||||||
|     , portalConfig(portalConfig) |  | ||||||
| { | { | ||||||
| } |     if (!params.clientos().isEmpty()) { | ||||||
|  |         preloginUrl = preloginUrl + "&clientos=" + params.clientos(); | ||||||
| GatewayAuthenticator::~GatewayAuthenticator() |     } | ||||||
| { |  | ||||||
|     delete normalLoginWindow; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::authenticate() | void GatewayAuthenticator::authenticate() | ||||||
| { | { | ||||||
|     PLOGI << "Start gateway authentication..."; |     LOGI << "Start gateway authentication..."; | ||||||
|  |  | ||||||
|     LoginParams params; |     LoginParams loginParams { params.clientos() }; | ||||||
|     params.setUser(portalConfig.username()); |     loginParams.setUser(params.username()); | ||||||
|     params.setPassword(portalConfig.password()); |     loginParams.setPassword(params.password()); | ||||||
|     params.setUserAuthCookie(portalConfig.userAuthCookie()); |     loginParams.setUserAuthCookie(params.userAuthCookie()); | ||||||
|  |     loginParams.setInputStr(params.inputStr()); | ||||||
|  |  | ||||||
|     login(params); |     login(loginParams); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::login(const LoginParams ¶ms) | void GatewayAuthenticator::login(const LoginParams &loginParams) | ||||||
| { | { | ||||||
|     PLOGI << "Trying to login the gateway at " << loginUrl << " with " << params.toUtf8(); |     LOGI << QString("Trying to login the gateway at %1, with %2").arg(loginUrl).arg(QString(loginParams.toUtf8())); | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(loginUrl, params.toUtf8()); |     auto *reply = createRequest(loginUrl, loginParams.toUtf8()); | ||||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished); |     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginFinished() | void GatewayAuthenticator::onLoginFinished() | ||||||
| { | { | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); | ||||||
|  |     QByteArray response = reply->readAll(); | ||||||
|  |  | ||||||
|     if (reply->error()) { |     if (reply->error() || response.contains("Authentication failure")) { | ||||||
|         PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl).arg(reply->errorString()); |         LOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString()); | ||||||
|  |  | ||||||
|         if (normalLoginWindow) { |         if (standardLoginWindow) { | ||||||
|             normalLoginWindow->setProcessing(false); |             standardLoginWindow->setProcessing(false); | ||||||
|             openMessageBox("Gateway login failed.", "Please check your credentials and try again."); |             openMessageBox("Gateway login failed.", "Please check your credentials and try again."); | ||||||
|         } else { |         } else { | ||||||
|             doAuth(); |             doAuth(); | ||||||
| @@ -57,43 +61,50 @@ void GatewayAuthenticator::onLoginFinished() | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (normalLoginWindow) { |     // 2FA | ||||||
|         normalLoginWindow->close(); |     if (response.contains("Challenge")) { | ||||||
|  |         LOGI << "The server need input the challenge..."; | ||||||
|  |         showChallenge(response); | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const QUrlQuery params = gpclient::helper::parseGatewayResponse(reply->readAll()); |     if (standardLoginWindow) { | ||||||
|  |         standardLoginWindow->close(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const auto params = gpclient::helper::parseGatewayResponse(response); | ||||||
|     emit success(params.toString()); |     emit success(params.toString()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::doAuth() | void GatewayAuthenticator::doAuth() | ||||||
| { | { | ||||||
|     PLOGI << "Perform the gateway prelogin at " << preloginUrl; |     LOGI << "Perform the gateway prelogin at " << preloginUrl; | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(preloginUrl); |     auto *reply = createRequest(preloginUrl); | ||||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onPreloginFinished); |     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onPreloginFinished); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onPreloginFinished() | void GatewayAuthenticator::onPreloginFinished() | ||||||
| { | { | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |     auto *reply = qobject_cast<QNetworkReply*>(sender()); | ||||||
|  |  | ||||||
|     if (reply->error()) { |     if (reply->error()) { | ||||||
|         PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl).arg(reply->errorString()); |         LOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString()); | ||||||
|  |  | ||||||
|         emit fail("Error occurred on the gateway prelogin interface."); |         emit fail("Error occurred on the gateway prelogin interface."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Gateway prelogin succeeded."; |     LOGI << "Gateway prelogin succeeded."; | ||||||
|  |  | ||||||
|     PreloginResponse response = PreloginResponse::parse(reply->readAll()); |     auto response = PreloginResponse::parse(reply->readAll()); | ||||||
|  |  | ||||||
|     if (response.hasSamlAuthFields()) { |     if (response.hasSamlAuthFields()) { | ||||||
|         samlAuth(response.samlMethod(), response.samlRequest(), reply->url().toString()); |         samlAuth(response.samlMethod(), response.samlRequest(), reply->url().toString()); | ||||||
|     } else if (response.hasNormalAuthFields()) { |     } else if (response.hasNormalAuthFields()) { | ||||||
|         normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage()); |         normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage()); | ||||||
|     } else { |     } else { | ||||||
|         PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl).arg(QString::fromUtf8(response.rawResponse())); |         LOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse())); | ||||||
|         emit fail("Unknown response for gateway prelogin interface."); |         emit fail("Unknown response for gateway prelogin interface."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -102,31 +113,27 @@ void GatewayAuthenticator::onPreloginFinished() | |||||||
|  |  | ||||||
| void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPassword, QString authMessage) | 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); |     LOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword); | ||||||
|  |  | ||||||
|     normalLoginWindow = new NormalLoginWindow; |     standardLoginWindow = new StandardLoginWindow {gateway, labelUsername, labelPassword, authMessage}; | ||||||
|     normalLoginWindow->setPortalAddress(gateway); |  | ||||||
|     normalLoginWindow->setAuthMessage(authMessage); |  | ||||||
|     normalLoginWindow->setUsernameLabel(labelUsername); |  | ||||||
|     normalLoginWindow->setPasswordLabel(labelPassword); |  | ||||||
|  |  | ||||||
|     // Do login |     // Do login | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformNormalLogin); |     connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformStandardLogin); | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); |     connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished); |     connect(standardLoginWindow, &StandardLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished); | ||||||
|  |  | ||||||
|     normalLoginWindow->show(); |     standardLoginWindow->show(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) | void GatewayAuthenticator::onPerformStandardLogin(const QString &username, const QString &password) | ||||||
| { | { | ||||||
|     PLOGI << "Start to perform normal login..."; |     LOGI << "Start to perform normal login..."; | ||||||
|  |  | ||||||
|     normalLoginWindow->setProcessing(true); |     standardLoginWindow->setProcessing(true); | ||||||
|     LoginParams params; |     params.setUsername(username); | ||||||
|     params.setUser(username); |  | ||||||
|     params.setPassword(password); |     params.setPassword(password); | ||||||
|     login(params); |  | ||||||
|  |     authenticate(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginWindowRejected() | void GatewayAuthenticator::onLoginWindowRejected() | ||||||
| @@ -136,19 +143,28 @@ void GatewayAuthenticator::onLoginWindowRejected() | |||||||
|  |  | ||||||
| void GatewayAuthenticator::onLoginWindowFinished() | void GatewayAuthenticator::onLoginWindowFinished() | ||||||
| { | { | ||||||
|     delete normalLoginWindow; |     delete standardLoginWindow; | ||||||
|     normalLoginWindow = nullptr; |     standardLoginWindow = nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl) | void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl) | ||||||
| { | { | ||||||
|     PLOGI << "Trying to perform SAML login with saml-method " << samlMethod; |     LOGI << "Trying to perform SAML login with saml-method " << samlMethod; | ||||||
|  |  | ||||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; |     auto *loginWindow = new SAMLLoginWindow(gateway); | ||||||
|  |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &GatewayAuthenticator::onSAMLLoginSuccess); |     connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> &samlResult) { | ||||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &GatewayAuthenticator::onSAMLLoginFail); |         this->onSAMLLoginSuccess(samlResult); | ||||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString &error) { | ||||||
|  |         this->onSAMLLoginFail(code, error); | ||||||
|  |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() { | ||||||
|  |         this->onLoginWindowRejected(); | ||||||
|  |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     loginWindow->login(samlMethod, samlRequest, preloginUrl); |     loginWindow->login(samlMethod, samlRequest, preloginUrl); | ||||||
| } | } | ||||||
| @@ -156,20 +172,55 @@ void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QSt | |||||||
| void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult) | void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult) | ||||||
| { | { | ||||||
|     if (samlResult.contains("preloginCookie")) { |     if (samlResult.contains("preloginCookie")) { | ||||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); |         LOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); | ||||||
|     } else { |     } else { | ||||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); |         LOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     LoginParams params; |     LoginParams loginParams { params.clientos() }; | ||||||
|     params.setUser(samlResult.value("username")); |     loginParams.setUser(samlResult.value("username")); | ||||||
|     params.setPreloginCookie(samlResult.value("preloginCookie")); |     loginParams.setPreloginCookie(samlResult.value("preloginCookie")); | ||||||
|     params.setUserAuthCookie(samlResult.value("userAuthCookie")); |     loginParams.setUserAuthCookie(samlResult.value("userAuthCookie")); | ||||||
|  |  | ||||||
|     login(params); |     login(loginParams); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GatewayAuthenticator::onSAMLLoginFail(const QString msg) | void GatewayAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg) | ||||||
| { | { | ||||||
|     emit fail(msg); |     emit fail(msg); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticator::showChallenge(const QString &responseText) | ||||||
|  | { | ||||||
|  |     QRegularExpression re("\"(.*?)\";"); | ||||||
|  |     QRegularExpressionMatchIterator i = re.globalMatch(responseText); | ||||||
|  |  | ||||||
|  |     i.next(); // Skip the status value | ||||||
|  |     QString message = i.next().captured(1); | ||||||
|  |     QString inputStr = i.next().captured(1); | ||||||
|  |     // update the inputSrc field | ||||||
|  |     params.setInputStr(inputStr); | ||||||
|  |  | ||||||
|  |     challengeDialog = new ChallengeDialog; | ||||||
|  |     challengeDialog->setMessage(message); | ||||||
|  |  | ||||||
|  |     connect(challengeDialog, &ChallengeDialog::accepted, this, [this] { | ||||||
|  |         params.setPassword(challengeDialog->getChallenge()); | ||||||
|  |         LOGI << "Challenge submitted, try to re-authenticate..."; | ||||||
|  |         authenticate(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     connect(challengeDialog, &ChallengeDialog::rejected, this, [this] { | ||||||
|  |         if (standardLoginWindow) { | ||||||
|  |             standardLoginWindow->close(); | ||||||
|  |         } | ||||||
|  |         emit fail(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     connect(challengeDialog, &ChallengeDialog::finished, this, [this] { | ||||||
|  |         delete challengeDialog; | ||||||
|  |         challengeDialog = nullptr; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     challengeDialog->show(); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,46 +1,48 @@ | |||||||
| #ifndef GATEWAYAUTHENTICATOR_H | #ifndef GATEWAYAUTHENTICATOR_H | ||||||
| #define GATEWAYAUTHENTICATOR_H | #define GATEWAYAUTHENTICATOR_H | ||||||
|  |  | ||||||
| #include "portalconfigresponse.h" | #include <QtCore/QObject> | ||||||
| #include "normalloginwindow.h" |  | ||||||
|  | #include "standardloginwindow.h" | ||||||
|  | #include "challengedialog.h" | ||||||
| #include "loginparams.h" | #include "loginparams.h" | ||||||
| #include <QObject> | #include "gatewayauthenticatorparams.h" | ||||||
|  |  | ||||||
| class GatewayAuthenticator : public QObject | class GatewayAuthenticator : public QObject | ||||||
| { | { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: | public: | ||||||
|     explicit GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig); |     explicit GatewayAuthenticator(const QString &gateway, GatewayAuthenticatorParams params); | ||||||
|     ~GatewayAuthenticator(); |  | ||||||
|  |  | ||||||
|     void authenticate(); |     void authenticate(); | ||||||
|  |  | ||||||
| signals: | signals: | ||||||
|     void success(const QString& authCookie); |     void success(const QString &authCookie); | ||||||
|     void fail(const QString& msg = ""); |     void fail(const QString &msg = ""); | ||||||
|  |  | ||||||
| private slots: | private slots: | ||||||
|     void onLoginFinished(); |     void onLoginFinished(); | ||||||
|     void onPreloginFinished(); |     void onPreloginFinished(); | ||||||
|     void onPerformNormalLogin(const QString &username, const QString &password); |     void onPerformStandardLogin(const QString &username, const QString &password); | ||||||
|     void onLoginWindowRejected(); |     void onLoginWindowRejected(); | ||||||
|     void onLoginWindowFinished(); |     void onLoginWindowFinished(); | ||||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> &samlResult); |     void onSAMLLoginSuccess(const QMap<QString, QString> &samlResult); | ||||||
|     void onSAMLLoginFail(const QString msg); |     void onSAMLLoginFail(const QString &code, const QString &msg); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     QString gateway; |     QString gateway; | ||||||
|  |     GatewayAuthenticatorParams params; | ||||||
|     QString preloginUrl; |     QString preloginUrl; | ||||||
|     QString loginUrl; |     QString loginUrl; | ||||||
|  |  | ||||||
|     const PortalConfigResponse& portalConfig; |     StandardLoginWindow *standardLoginWindow { nullptr }; | ||||||
|  |     ChallengeDialog *challengeDialog { nullptr }; | ||||||
|  |  | ||||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; |     void login(const LoginParams& loginParams); | ||||||
|  |  | ||||||
|     void login(const LoginParams& params); |  | ||||||
|     void doAuth(); |     void doAuth(); | ||||||
|     void normalAuth(QString labelUsername, QString labelPassword, QString authMessage); |     void normalAuth(QString labelUsername, QString labelPassword, QString authMessage); | ||||||
|     void samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl = ""); |     void samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl = ""); | ||||||
|  |     void showChallenge(const QString &responseText); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // GATEWAYAUTHENTICATOR_H | #endif // GATEWAYAUTHENTICATOR_H | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								GPClient/gatewayauthenticatorparams.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								GPClient/gatewayauthenticatorparams.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | #include "gatewayauthenticatorparams.h" | ||||||
|  |  | ||||||
|  | GatewayAuthenticatorParams::GatewayAuthenticatorParams() | ||||||
|  | { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | GatewayAuthenticatorParams GatewayAuthenticatorParams::fromPortalConfigResponse(const PortalConfigResponse &portalConfig) | ||||||
|  | { | ||||||
|  |     GatewayAuthenticatorParams params; | ||||||
|  |     params.setUsername(portalConfig.username()); | ||||||
|  |     params.setPassword(portalConfig.password()); | ||||||
|  |     params.setUserAuthCookie(portalConfig.userAuthCookie()); | ||||||
|  |  | ||||||
|  |     return params; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString &GatewayAuthenticatorParams::username() const | ||||||
|  | { | ||||||
|  |     return m_username; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticatorParams::setUsername(const QString &newUsername) | ||||||
|  | { | ||||||
|  |     m_username = newUsername; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString &GatewayAuthenticatorParams::password() const | ||||||
|  | { | ||||||
|  |     return m_password; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticatorParams::setPassword(const QString &newPassword) | ||||||
|  | { | ||||||
|  |     m_password = newPassword; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString &GatewayAuthenticatorParams::userAuthCookie() const | ||||||
|  | { | ||||||
|  |     return m_userAuthCookie; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticatorParams::setUserAuthCookie(const QString &newUserAuthCookie) | ||||||
|  | { | ||||||
|  |     m_userAuthCookie = newUserAuthCookie; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString &GatewayAuthenticatorParams::clientos() const | ||||||
|  | { | ||||||
|  |     return m_clientos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticatorParams::setClientos(const QString &newClientos) | ||||||
|  | { | ||||||
|  |     m_clientos = newClientos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const QString &GatewayAuthenticatorParams::inputStr() const | ||||||
|  | { | ||||||
|  |     return m_inputStr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GatewayAuthenticatorParams::setInputStr(const QString &inputStr) | ||||||
|  | { | ||||||
|  |     m_inputStr = inputStr; | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								GPClient/gatewayauthenticatorparams.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								GPClient/gatewayauthenticatorparams.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | #ifndef GATEWAYAUTHENTICATORPARAMS_H | ||||||
|  | #define GATEWAYAUTHENTICATORPARAMS_H | ||||||
|  |  | ||||||
|  | #include <QtCore/QString> | ||||||
|  |  | ||||||
|  | #include "portalconfigresponse.h" | ||||||
|  |  | ||||||
|  | class GatewayAuthenticatorParams | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     GatewayAuthenticatorParams(); | ||||||
|  |  | ||||||
|  |     static GatewayAuthenticatorParams fromPortalConfigResponse(const PortalConfigResponse &portalConfig); | ||||||
|  |  | ||||||
|  |     const QString &username() const; | ||||||
|  |     void setUsername(const QString &newUsername); | ||||||
|  |  | ||||||
|  |     const QString &password() const; | ||||||
|  |     void setPassword(const QString &newPassword); | ||||||
|  |  | ||||||
|  |     const QString &userAuthCookie() const; | ||||||
|  |     void setUserAuthCookie(const QString &newUserAuthCookie); | ||||||
|  |  | ||||||
|  |     const QString &clientos() const; | ||||||
|  |     void setClientos(const QString &newClientos); | ||||||
|  |  | ||||||
|  |     const QString &inputStr() const; | ||||||
|  |     void setInputStr(const QString &inputStr); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     QString m_username; | ||||||
|  |     QString m_password; | ||||||
|  |     QString m_userAuthCookie; | ||||||
|  |     QString m_clientos; | ||||||
|  |     QString m_inputStr; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // GATEWAYAUTHENTICATORPARAMS_H | ||||||
| @@ -1,41 +1,74 @@ | |||||||
|  | #include <QtGui/QIcon> | ||||||
|  | #include <plog/Log.h> | ||||||
|  |  | ||||||
| #include "gpclient.h" | #include "gpclient.h" | ||||||
| #include "gphelper.h" | #include "gphelper.h" | ||||||
| #include "ui_gpclient.h" | #include "ui_gpclient.h" | ||||||
| #include "portalauthenticator.h" | #include "portalauthenticator.h" | ||||||
| #include "gatewayauthenticator.h" | #include "gatewayauthenticator.h" | ||||||
|  | #include "settingsdialog.h" | ||||||
| #include <plog/Log.h> | #include "gatewayauthenticatorparams.h" | ||||||
| #include <QIcon> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; | using namespace gpclient::helper; | ||||||
|  |  | ||||||
| GPClient::GPClient(QWidget *parent) | GPClient::GPClient(QWidget *parent, IVpn *vpn) | ||||||
|     : QMainWindow(parent) |     : QMainWindow(parent) | ||||||
|     , ui(new Ui::GPClient) |     , ui(new Ui::GPClient) | ||||||
|  |     , vpn(vpn) | ||||||
|  |     , settingsDialog(new SettingsDialog(this)) | ||||||
| { | { | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|  |  | ||||||
|     setWindowTitle("GlobalProtect"); |     setWindowTitle("GlobalProtect"); | ||||||
|     setFixedSize(width(), height()); |     setFixedSize(width(), height()); | ||||||
|     gpclient::helper::moveCenter(this); |     gpclient::helper::moveCenter(this); | ||||||
|  |  | ||||||
|  |     setupSettings(); | ||||||
|  |  | ||||||
|     // Restore portal from the previous settings |     // Restore portal from the previous settings | ||||||
|     ui->portalInput->setText(settings::get("portal", "").toString()); |     this->portal(settings::get("portal", "").toString()); | ||||||
|  |  | ||||||
|     // DBus service setup |     // DBus service setup | ||||||
|     vpn = new com::yuezk::qt::GPService("com.yuezk.qt.GPService", "/", QDBusConnection::systemBus(), this); |     QObject *ov = dynamic_cast<QObject*>(vpn); | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::connected, this, &GPClient::onVPNConnected); |     connect(ov, SIGNAL(connected()), this, SLOT(onVPNConnected())); | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::disconnected, this, &GPClient::onVPNDisconnected); |     connect(ov, SIGNAL(disconnected()), this, SLOT(onVPNDisconnected())); | ||||||
|     connect(vpn, &com::yuezk::qt::GPService::logAvailable, this, &GPClient::onVPNLogAvailable); |     connect(ov, SIGNAL(error(QString)), this, SLOT(onVPNError(QString))); | ||||||
|  |     connect(ov, SIGNAL(logAvailable(QString)), this, SLOT(onVPNLogAvailable(QString))); | ||||||
|  |  | ||||||
|     // Initiallize the context menu of system tray. |     // Initialize the context menu of system tray. | ||||||
|     initSystemTrayIcon(); |     initSystemTrayIcon(); | ||||||
|     initVpnStatus(); |     initVpnStatus(); | ||||||
| } | } | ||||||
|  |  | ||||||
| GPClient::~GPClient() | void GPClient::setupSettings() | ||||||
| { | { | ||||||
|     delete ui; |     settingsButton = new QPushButton(this); | ||||||
|     delete vpn; |     settingsButton->setIcon(QIcon(":/images/settings_icon.png")); | ||||||
|  |     settingsButton->setFixedSize(QSize(28, 28)); | ||||||
|  |  | ||||||
|  |     QRect rect = this->geometry(); | ||||||
|  |     settingsButton->setGeometry( | ||||||
|  |                 rect.width() - settingsButton->width() - 15, | ||||||
|  |                 15, | ||||||
|  |                 settingsButton->geometry().width(), | ||||||
|  |                 settingsButton->geometry().height() | ||||||
|  |                 ); | ||||||
|  |  | ||||||
|  |     connect(settingsButton, &QPushButton::clicked, this, &GPClient::onSettingsButtonClicked); | ||||||
|  |     connect(settingsDialog, &QDialog::accepted, this, &GPClient::onSettingsAccepted); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GPClient::onSettingsButtonClicked() | ||||||
|  | { | ||||||
|  |     settingsDialog->setClientos(settings::get("clientos", "Linux").toString()); | ||||||
|  |     settingsDialog->setOsVersion(settings::get("os-version", QSysInfo::prettyProductName()).toString()); | ||||||
|  |     settingsDialog->show(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GPClient::onSettingsAccepted() | ||||||
|  | { | ||||||
|  |     settings::save("clientos", settingsDialog->clientos()); | ||||||
|  |     settings::save("os-version", settingsDialog->osVersion()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPClient::on_connectButton_clicked() | void GPClient::on_connectButton_clicked() | ||||||
| @@ -69,11 +102,11 @@ void GPClient::initSystemTrayIcon() | |||||||
|     connect(systemTrayIcon, &QSystemTrayIcon::activated, this, &GPClient::onSystemTrayActivated); |     connect(systemTrayIcon, &QSystemTrayIcon::activated, this, &GPClient::onSystemTrayActivated); | ||||||
|     connect(gatewaySwitchMenu, &QMenu::triggered, this, &GPClient::onGatewayChanged); |     connect(gatewaySwitchMenu, &QMenu::triggered, this, &GPClient::onGatewayChanged); | ||||||
|  |  | ||||||
|     openAction = contextMenu->addAction(QIcon::fromTheme("window-new"), "Open", this, &GPClient::activiate); |     openAction = contextMenu->addAction(QIcon::fromTheme("window-new"), "Open", this, &GPClient::activate); | ||||||
|     connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect); |     connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect); | ||||||
|     contextMenu->addMenu(gatewaySwitchMenu); |     contextMenu->addMenu(gatewaySwitchMenu); | ||||||
|     contextMenu->addSeparator(); |     contextMenu->addSeparator(); | ||||||
|     clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset Settings", this, &GPClient::clearSettings); |     clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset", this, &GPClient::reset); | ||||||
|     quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit); |     quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit); | ||||||
|  |  | ||||||
|     systemTrayIcon->show(); |     systemTrayIcon->show(); | ||||||
| @@ -97,7 +130,7 @@ void GPClient::initVpnStatus() { | |||||||
|  |  | ||||||
| void GPClient::populateGatewayMenu() | void GPClient::populateGatewayMenu() | ||||||
| { | { | ||||||
|     PLOGI << "Populating the Switch Gateway menu..."; |     LOGI << "Populating the Switch Gateway menu..."; | ||||||
|  |  | ||||||
|     const QList<GPGateway> gateways = allGateways(); |     const QList<GPGateway> gateways = allGateways(); | ||||||
|     gatewaySwitchMenu->clear(); |     gatewaySwitchMenu->clear(); | ||||||
| @@ -114,7 +147,7 @@ void GPClient::populateGatewayMenu() | |||||||
|         if (g.name() == currentGatewayName) { |         if (g.name() == currentGatewayName) { | ||||||
|             iconImage = ":/images/radio_selected.png"; |             iconImage = ":/images/radio_selected.png"; | ||||||
|         } |         } | ||||||
|         gatewaySwitchMenu->addAction(QIcon(iconImage), g.name())->setData(i); |         gatewaySwitchMenu->addAction(QIcon(iconImage), QString("%1 (%2)").arg(g.name(), g.address()))->setData(i); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -167,7 +200,7 @@ void GPClient::onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason) | |||||||
|     switch (reason) { |     switch (reason) { | ||||||
|         case QSystemTrayIcon::Trigger: |         case QSystemTrayIcon::Trigger: | ||||||
|         case QSystemTrayIcon::DoubleClick: |         case QSystemTrayIcon::DoubleClick: | ||||||
|             this->activiate(); |             this->activate(); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
| @@ -182,7 +215,7 @@ void GPClient::onGatewayChanged(QAction *action) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const GPGateway g = allGateways().at(index); |     const auto g = allGateways().at(index); | ||||||
|  |  | ||||||
|     // If the selected gateway is the same as the current gateway |     // If the selected gateway is the same as the current gateway | ||||||
|     if (g.name() == currentGateway().name()) { |     if (g.name() == currentGateway().name()) { | ||||||
| @@ -202,14 +235,14 @@ void GPClient::onGatewayChanged(QAction *action) | |||||||
|  |  | ||||||
| void GPClient::doConnect() | void GPClient::doConnect() | ||||||
| { | { | ||||||
|     PLOGI << "Start connecting..."; |     LOGI << "Start connecting..."; | ||||||
|  |  | ||||||
|     const QString btnText = ui->connectButton->text(); |     const auto btnText = ui->connectButton->text(); | ||||||
|     const QString portal = this->portal(); |     const auto portal = this->portal(); | ||||||
|  |  | ||||||
|     // Display the main window if portal is empty |     // Display the main window if portal is empty | ||||||
|     if (portal.isEmpty()) { |     if (portal.isEmpty()) { | ||||||
|         activiate(); |         activate(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -218,16 +251,16 @@ void GPClient::doConnect() | |||||||
|  |  | ||||||
|         // Login to the previously saved gateway |         // Login to the previously saved gateway | ||||||
|         if (!currentGateway().name().isEmpty()) { |         if (!currentGateway().name().isEmpty()) { | ||||||
|             PLOGI << "Start gateway login using the previously saved gateway..."; |             LOGI << "Start gateway login using the previously saved gateway..."; | ||||||
|             isQuickConnect = true; |             isQuickConnect = true; | ||||||
|             gatewayLogin(); |             gatewayLogin(); | ||||||
|         } else { |         } else { | ||||||
|             // Perform the portal login |             // Perform the portal login | ||||||
|             PLOGI << "Start portal login..."; |             LOGI << "Start portal login..."; | ||||||
|             portalLogin(); |             portalLogin(); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         PLOGI << "Start disconnecting the VPN..."; |         LOGI << "Start disconnecting the VPN..."; | ||||||
|  |  | ||||||
|         ui->statusLabel->setText("Disconnecting..."); |         ui->statusLabel->setText("Disconnecting..."); | ||||||
|         updateConnectionStatus(VpnStatus::pending); |         updateConnectionStatus(VpnStatus::pending); | ||||||
| @@ -238,14 +271,26 @@ void GPClient::doConnect() | |||||||
| // Login to the portal interface to get the portal config and preferred gateway | // Login to the portal interface to get the portal config and preferred gateway | ||||||
| void GPClient::portalLogin() | void GPClient::portalLogin() | ||||||
| { | { | ||||||
|     PortalAuthenticator *portalAuth = new PortalAuthenticator(portal()); |     auto *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString()); | ||||||
|  |  | ||||||
|     connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess); |     connect(portalAuth, &PortalAuthenticator::success, [this, portalAuth](const PortalConfigResponse response, const QString region) { | ||||||
|  |         this->onPortalSuccess(response, region); | ||||||
|  |         portalAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|     // Prelogin failed on the portal interface, try to treat the portal as a gateway interface |     // 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::preloginFailed, [this, portalAuth](const QString msg) { | ||||||
|     connect(portalAuth, &PortalAuthenticator::portalConfigFailed, this, &GPClient::onPortalConfigFail); |         this->onPortalPreloginFail(msg); | ||||||
|  |         portalAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(portalAuth, &PortalAuthenticator::portalConfigFailed, [this, portalAuth](const QString msg) { | ||||||
|  |         this->onPortalConfigFail(msg); | ||||||
|  |         portalAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|     // Portal login failed |     // Portal login failed | ||||||
|     connect(portalAuth, &PortalAuthenticator::fail, this, &GPClient::onPortalFail); |     connect(portalAuth, &PortalAuthenticator::fail, [this, portalAuth](const QString &msg) { | ||||||
|  |         this->onPortalFail(msg); | ||||||
|  |         portalAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     ui->statusLabel->setText("Authenticating..."); |     ui->statusLabel->setText("Authenticating..."); | ||||||
|     updateConnectionStatus(VpnStatus::pending); |     updateConnectionStatus(VpnStatus::pending); | ||||||
| @@ -254,11 +299,11 @@ void GPClient::portalLogin() | |||||||
|  |  | ||||||
| void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region) | void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region) | ||||||
| { | { | ||||||
|     PLOGI << "Portal authentication succeeded."; |     LOGI << "Portal authentication succeeded."; | ||||||
|  |  | ||||||
|     // No gateway found in protal configuration |     // No gateway found in portal configuration | ||||||
|     if (portalConfig.allGateways().size() == 0) { |     if (portalConfig.allGateways().size() == 0) { | ||||||
|         PLOGI << "No gateway found in portal configuration, treat the portal address as a gateway."; |         LOGI << "No gateway found in portal configuration, treat the portal address as a gateway."; | ||||||
|         tryGatewayLogin(); |         tryGatewayLogin(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -273,13 +318,13 @@ void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QS | |||||||
|  |  | ||||||
| void GPClient::onPortalPreloginFail(const QString msg) | void GPClient::onPortalPreloginFail(const QString msg) | ||||||
| { | { | ||||||
|     PLOGI << "Portal prelogin failed: " << msg; |     LOGI << "Portal prelogin failed, treat the portal address as a gateway." << msg; | ||||||
|     tryGatewayLogin(); |     tryGatewayLogin(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPClient::onPortalConfigFail(const QString msg) | void GPClient::onPortalConfigFail(const QString msg) | ||||||
| { | { | ||||||
|     PLOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway."; |     LOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway."; | ||||||
|     tryGatewayLogin(); |     tryGatewayLogin(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -294,7 +339,7 @@ void GPClient::onPortalFail(const QString &msg) | |||||||
|  |  | ||||||
| void GPClient::tryGatewayLogin() | void GPClient::tryGatewayLogin() | ||||||
| { | { | ||||||
|     PLOGI << "Try to preform login on the the gateway interface..."; |     LOGI << "Try to perform login on the the gateway interface..."; | ||||||
|  |  | ||||||
|     // Treat the portal input as the gateway address |     // Treat the portal input as the gateway address | ||||||
|     GPGateway g; |     GPGateway g; | ||||||
| @@ -313,12 +358,22 @@ void GPClient::tryGatewayLogin() | |||||||
| // Login to the gateway | // Login to the gateway | ||||||
| void GPClient::gatewayLogin() | void GPClient::gatewayLogin() | ||||||
| { | { | ||||||
|     PLOGI << "Performing gateway login..."; |     LOGI << "Performing gateway login..."; | ||||||
|  |  | ||||||
|     GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), portalConfig); |     GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig); | ||||||
|  |     params.setClientos(settings::get("clientos", "Linux").toString()); | ||||||
|  |  | ||||||
|     connect(gatewayAuth, &GatewayAuthenticator::success, this, &GPClient::onGatewaySuccess); |     GatewayAuthenticator *gatewayAuth; | ||||||
|     connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail); |     gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params); | ||||||
|  |  | ||||||
|  |     connect(gatewayAuth, &GatewayAuthenticator::success, [this, gatewayAuth](const QString &authToken) { | ||||||
|  |         this->onGatewaySuccess(authToken); | ||||||
|  |         gatewayAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(gatewayAuth, &GatewayAuthenticator::fail, [this, gatewayAuth](const QString &msg) { | ||||||
|  |         this->onGatewayFail(msg); | ||||||
|  |         gatewayAuth->deleteLater(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     ui->statusLabel->setText("Authenticating..."); |     ui->statusLabel->setText("Authenticating..."); | ||||||
|     updateConnectionStatus(VpnStatus::pending); |     updateConnectionStatus(VpnStatus::pending); | ||||||
| @@ -327,10 +382,14 @@ void GPClient::gatewayLogin() | |||||||
|  |  | ||||||
| void GPClient::onGatewaySuccess(const QString &authCookie) | void GPClient::onGatewaySuccess(const QString &authCookie) | ||||||
| { | { | ||||||
|     PLOGI << "Gateway login succeeded, got the cookie " << authCookie; |     LOGI << "Gateway login succeeded, got the cookie " << authCookie; | ||||||
|  |  | ||||||
|     isQuickConnect = false; |     isQuickConnect = false; | ||||||
|     vpn->connect(currentGateway().address(), portalConfig.username(), authCookie); |     QList<QString> gatewayAddresses; | ||||||
|  |     for (GPGateway &gw : allGateways()) { | ||||||
|  |       gatewayAddresses.push_back(gw.address()); | ||||||
|  |     } | ||||||
|  |     vpn->connect(currentGateway().address(), gatewayAddresses, portalConfig.username(), authCookie); | ||||||
|     ui->statusLabel->setText("Connecting..."); |     ui->statusLabel->setText("Connecting..."); | ||||||
|     updateConnectionStatus(VpnStatus::pending); |     updateConnectionStatus(VpnStatus::pending); | ||||||
| } | } | ||||||
| @@ -340,6 +399,7 @@ void GPClient::onGatewayFail(const QString &msg) | |||||||
|     // If the quick connect on gateway failed, perform the portal login |     // If the quick connect on gateway failed, perform the portal login | ||||||
|     if (isQuickConnect && !msg.isEmpty()) { |     if (isQuickConnect && !msg.isEmpty()) { | ||||||
|         isQuickConnect = false; |         isQuickConnect = false; | ||||||
|  |         LOGI << "Quick connection failed, trying to portal login..."; | ||||||
|         portalLogin(); |         portalLogin(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -351,7 +411,7 @@ void GPClient::onGatewayFail(const QString &msg) | |||||||
|     updateConnectionStatus(VpnStatus::disconnected); |     updateConnectionStatus(VpnStatus::disconnected); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPClient::activiate() | void GPClient::activate() | ||||||
| { | { | ||||||
|     activateWindow(); |     activateWindow(); | ||||||
|     showNormal(); |     showNormal(); | ||||||
| @@ -367,6 +427,11 @@ QString GPClient::portal() const | |||||||
|     return input; |     return input; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void GPClient::portal(QString p) | ||||||
|  | { | ||||||
|  |     ui->portalInput->setText(p); | ||||||
|  | } | ||||||
|  |  | ||||||
| bool GPClient::connected() const | bool GPClient::connected() const | ||||||
| { | { | ||||||
|     const QString statusText = ui->statusLabel->text(); |     const QString statusText = ui->statusLabel->text(); | ||||||
| @@ -375,13 +440,19 @@ bool GPClient::connected() const | |||||||
|  |  | ||||||
| QList<GPGateway> GPClient::allGateways() const | QList<GPGateway> GPClient::allGateways() const | ||||||
| { | { | ||||||
|     const QString gatewaysJson = settings::get(portal() + "_gateways").toString(); |  | ||||||
|     return GPGateway::fromJson(gatewaysJson); |     QList<GPGateway> gateways; | ||||||
|  |  | ||||||
|  |     for (auto g :settings::get_all("_gateways$") ){ | ||||||
|  |  | ||||||
|  |     	gateways.append(GPGateway::fromJson(settings::get(g).toString())); | ||||||
|  |     } | ||||||
|  |     return gateways; | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPClient::setAllGateways(QList<GPGateway> gateways) | void GPClient::setAllGateways(QList<GPGateway> gateways) | ||||||
| { | { | ||||||
|     PLOGI << "Updating all the gateways..."; |     LOGI << "Updating all the gateways..."; | ||||||
|  |  | ||||||
|     settings::save(portal() + "_gateways", GPGateway::serialize(gateways)); |     settings::save(portal() + "_gateways", GPGateway::serialize(gateways)); | ||||||
|     populateGatewayMenu(); |     populateGatewayMenu(); | ||||||
| @@ -401,13 +472,14 @@ GPGateway GPClient::currentGateway() const | |||||||
|  |  | ||||||
| void GPClient::setCurrentGateway(const GPGateway gateway) | void GPClient::setCurrentGateway(const GPGateway gateway) | ||||||
| { | { | ||||||
|     PLOGI << "Updating the current gateway to " << gateway.name(); |     LOGI << "Updating the current gateway to " << gateway.name(); | ||||||
|  |  | ||||||
|     settings::save(portal() + "_selectedGateway", gateway.name()); |     settings::save(portal() + "_selectedGateway", gateway.name()); | ||||||
|  |     ui->portalInput->setText(gateway.address()); | ||||||
|     populateGatewayMenu(); |     populateGatewayMenu(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPClient::clearSettings() | void GPClient::reset() | ||||||
| { | { | ||||||
|     settings::clear(); |     settings::clear(); | ||||||
|     populateGatewayMenu(); |     populateGatewayMenu(); | ||||||
| @@ -435,7 +507,13 @@ void GPClient::onVPNDisconnected() | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void GPClient::onVPNError(QString errorMessage) | ||||||
|  | { | ||||||
|  |     updateConnectionStatus(VpnStatus::disconnected); | ||||||
|  |     openMessageBox("Failed to connect", errorMessage); | ||||||
|  | } | ||||||
|  |  | ||||||
| void GPClient::onVPNLogAvailable(QString log) | void GPClient::onVPNLogAvailable(QString log) | ||||||
| { | { | ||||||
|     PLOGI << log; |     LOGI << log; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
| #ifndef GPCLIENT_H | #ifndef GPCLIENT_H | ||||||
| #define GPCLIENT_H | #define GPCLIENT_H | ||||||
|  |  | ||||||
| #include "gpservice_interface.h" | #include <QtWidgets/QMainWindow> | ||||||
| #include "portalconfigresponse.h" | #include <QtWidgets/QSystemTrayIcon> | ||||||
|  | #include <QtWidgets/QMenu> | ||||||
|  | #include <QtWidgets/QPushButton> | ||||||
|  |  | ||||||
| #include <QMainWindow> | #include "portalconfigresponse.h" | ||||||
| #include <QSystemTrayIcon> | #include "settingsdialog.h" | ||||||
| #include <QMenu> | #include "vpn.h" | ||||||
|  | #include "gatewayauthenticator.h" | ||||||
|  |  | ||||||
| QT_BEGIN_NAMESPACE | QT_BEGIN_NAMESPACE | ||||||
| namespace Ui { class GPClient; } | namespace Ui { class GPClient; } | ||||||
| @@ -17,12 +20,24 @@ class GPClient : public QMainWindow | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
| public: | public: | ||||||
|     GPClient(QWidget *parent = nullptr); |     GPClient(QWidget *parent, IVpn *vpn); | ||||||
|     ~GPClient(); |  | ||||||
|  |  | ||||||
|     void activiate(); |     void activate(); | ||||||
|  |     void quit(); | ||||||
|  |  | ||||||
|  |     QString portal() const; | ||||||
|  |     void portal(QString); | ||||||
|  |  | ||||||
|  |     GPGateway currentGateway() const; | ||||||
|  |     void setCurrentGateway(const GPGateway gateway); | ||||||
|  |  | ||||||
|  |     void doConnect(); | ||||||
|  |     void reset(); | ||||||
|  |  | ||||||
| private slots: | private slots: | ||||||
|  |     void onSettingsButtonClicked(); | ||||||
|  |     void onSettingsAccepted(); | ||||||
|  |  | ||||||
|     void on_connectButton_clicked(); |     void on_connectButton_clicked(); | ||||||
|     void on_portalInput_returnPressed(); |     void on_portalInput_returnPressed(); | ||||||
|     void on_portalInput_editingFinished(); |     void on_portalInput_editingFinished(); | ||||||
| @@ -40,6 +55,7 @@ private slots: | |||||||
|  |  | ||||||
|     void onVPNConnected(); |     void onVPNConnected(); | ||||||
|     void onVPNDisconnected(); |     void onVPNDisconnected(); | ||||||
|  |     void onVPNError(QString errorMessage); | ||||||
|     void onVPNLogAvailable(QString log); |     void onVPNLogAvailable(QString log); | ||||||
|  |  | ||||||
| private: | private: | ||||||
| @@ -51,7 +67,7 @@ private: | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     Ui::GPClient *ui; |     Ui::GPClient *ui; | ||||||
|     com::yuezk::qt::GPService *vpn; |     IVpn *vpn; | ||||||
|  |  | ||||||
|     QSystemTrayIcon *systemTrayIcon; |     QSystemTrayIcon *systemTrayIcon; | ||||||
|     QMenu *contextMenu; |     QMenu *contextMenu; | ||||||
| @@ -62,30 +78,27 @@ private: | |||||||
|     QAction *clearAction; |     QAction *clearAction; | ||||||
|     QAction *quitAction; |     QAction *quitAction; | ||||||
|  |  | ||||||
|  |     SettingsDialog *settingsDialog; | ||||||
|  |     QPushButton *settingsButton; | ||||||
|  |  | ||||||
|     bool isQuickConnect { false }; |     bool isQuickConnect { false }; | ||||||
|     bool isSwitchingGateway { false }; |     bool isSwitchingGateway { false }; | ||||||
|     PortalConfigResponse portalConfig; |     PortalConfigResponse portalConfig; | ||||||
|  |  | ||||||
|  |     void setupSettings(); | ||||||
|  |  | ||||||
|     void initSystemTrayIcon(); |     void initSystemTrayIcon(); | ||||||
|     void initVpnStatus(); |     void initVpnStatus(); | ||||||
|     void populateGatewayMenu(); |     void populateGatewayMenu(); | ||||||
|     void updateConnectionStatus(const VpnStatus &status); |     void updateConnectionStatus(const VpnStatus &status); | ||||||
|  |  | ||||||
|     void doConnect(); |  | ||||||
|     void portalLogin(); |     void portalLogin(); | ||||||
|     void tryGatewayLogin(); |     void tryGatewayLogin(); | ||||||
|     void gatewayLogin(); |     void gatewayLogin(); | ||||||
|  |  | ||||||
|     QString portal() const; |  | ||||||
|     bool connected() const; |     bool connected() const; | ||||||
|  |  | ||||||
|     QList<GPGateway> allGateways() const; |     QList<GPGateway> allGateways() const; | ||||||
|     void setAllGateways(QList<GPGateway> gateways); |     void setAllGateways(QList<GPGateway> gateways); | ||||||
|  |  | ||||||
|     GPGateway currentGateway() const; |  | ||||||
|     void setCurrentGateway(const GPGateway gateway); |  | ||||||
|  |  | ||||||
|     void clearSettings(); |  | ||||||
|     void quit(); |  | ||||||
| }; | }; | ||||||
| #endif // GPCLIENT_H | #endif // GPCLIENT_H | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|     <x>0</x> |     <x>0</x> | ||||||
|     <y>0</y> |     <y>0</y> | ||||||
|     <width>260</width> |     <width>260</width> | ||||||
|     <height>338</height> |     <height>362</height> | ||||||
|    </rect> |    </rect> | ||||||
|   </property> |   </property> | ||||||
|   <property name="windowTitle"> |   <property name="windowTitle"> | ||||||
| @@ -36,7 +36,7 @@ | |||||||
|    <property name="layoutDirection"> |    <property name="layoutDirection"> | ||||||
|     <enum>Qt::LeftToRight</enum> |     <enum>Qt::LeftToRight</enum> | ||||||
|    </property> |    </property> | ||||||
|    <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0"> |    <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0,0"> | ||||||
|     <property name="leftMargin"> |     <property name="leftMargin"> | ||||||
|      <number>15</number> |      <number>15</number> | ||||||
|     </property> |     </property> | ||||||
| @@ -123,6 +123,16 @@ | |||||||
|       </item> |       </item> | ||||||
|      </layout> |      </layout> | ||||||
|     </item> |     </item> | ||||||
|  |     <item> | ||||||
|  |      <widget class="QLabel" name="label"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string><html><head/><body><p align="center"><a href="https://bit.ly/3g5DHqy"><span style=" text-decoration: underline; color:#4c6b8a;">Report a bug</span></a> / <a href="https://bit.ly/3jQYfEi"><span style=" text-decoration: underline; color:#4c6b8a;">Buy me a coffee</span></a></p></body></html></string> | ||||||
|  |       </property> | ||||||
|  |       <property name="openExternalLinks"> | ||||||
|  |        <bool>true</bool> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|    </layout> |    </layout> | ||||||
|   </widget> |   </widget> | ||||||
|  </widget> |  </widget> | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #include "gpgateway.h" | #include <QtCore/QJsonObject> | ||||||
|  | #include <QtCore/QJsonDocument> | ||||||
|  | #include <QtCore/QJsonArray> | ||||||
|  |  | ||||||
| #include <QJsonObject> | #include "gpgateway.h" | ||||||
| #include <QJsonDocument> |  | ||||||
| #include <QJsonArray> |  | ||||||
|  |  | ||||||
| GPGateway::GPGateway() | GPGateway::GPGateway() | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| #ifndef GPGATEWAY_H | #ifndef GPGATEWAY_H | ||||||
| #define GPGATEWAY_H | #define GPGATEWAY_H | ||||||
|  |  | ||||||
| #include <QString> | #include <QtCore/QString> | ||||||
| #include <QMap> | #include <QtCore/QMap> | ||||||
| #include <QJsonObject> | #include <QtCore/QJsonObject> | ||||||
|  |  | ||||||
| class GPGateway | class GPGateway | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,13 +1,19 @@ | |||||||
| #include "gphelper.h" | #include <QtCore/QXmlStreamReader> | ||||||
| #include <QNetworkRequest> | #include <QtWidgets/QMessageBox> | ||||||
| #include <QXmlStreamReader> | #include <QtWidgets/QDesktopWidget> | ||||||
| #include <QMessageBox> | #include <QtWidgets/QApplication> | ||||||
| #include <QDesktopWidget> | #include <QtWidgets/QWidget> | ||||||
| #include <QApplication> | #include <QtNetwork/QNetworkRequest> | ||||||
| #include <QWidget> | #include <QtNetwork/QSslConfiguration> | ||||||
| #include <QSslConfiguration> | #include <QtNetwork/QSslSocket> | ||||||
| #include <QSslSocket> |  | ||||||
| #include <plog/Log.h> | #include <plog/Log.h> | ||||||
|  | #include <QWebEngineProfile> | ||||||
|  | #include <QWebEngineCookieStore> | ||||||
|  | #include <keychain.h> | ||||||
|  |  | ||||||
|  | #include "gphelper.h" | ||||||
|  |  | ||||||
|  | using namespace QKeychain; | ||||||
|  |  | ||||||
| QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager; | QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager; | ||||||
|  |  | ||||||
| @@ -18,6 +24,7 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) | |||||||
|     // Skip the ssl verifying |     // Skip the ssl verifying | ||||||
|     QSslConfiguration conf = request.sslConfiguration(); |     QSslConfiguration conf = request.sslConfiguration(); | ||||||
|     conf.setPeerVerifyMode(QSslSocket::VerifyNone); |     conf.setPeerVerifyMode(QSslSocket::VerifyNone); | ||||||
|  |     conf.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation, false); | ||||||
|     request.setSslConfiguration(conf); |     request.setSslConfiguration(conf); | ||||||
|  |  | ||||||
|     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); |     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); | ||||||
| @@ -31,13 +38,13 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) | |||||||
|  |  | ||||||
| GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName) | GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName) | ||||||
| { | { | ||||||
|     PLOGI << gateways.size() << " gateway(s) avaiable, filter the gateways with rule: " << ruleName; |     LOGI << gateways.size() << " gateway(s) available, filter the gateways with rule: " << ruleName; | ||||||
|  |  | ||||||
|     GPGateway gateway = gateways.first(); |     GPGateway gateway = gateways.first(); | ||||||
|  |  | ||||||
|     for (GPGateway g : gateways) { |     for (GPGateway g : gateways) { | ||||||
|         if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) { |         if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) { | ||||||
|             PLOGI << "Find a preferred gateway: " << g.name(); |             LOGI << "Find a preferred gateway: " << g.name(); | ||||||
|             gateway = g; |             gateway = g; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -47,8 +54,8 @@ GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, co | |||||||
|  |  | ||||||
| QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml) | QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml) | ||||||
| { | { | ||||||
|     PLOGI << "Start parsing the gateway response..."; |     LOGI << "Start parsing the gateway response..."; | ||||||
|     PLOGI << "The gateway response is: " << xml; |     LOGI << "The gateway response is: " << xml; | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader{xml}; |     QXmlStreamReader xmlReader{xml}; | ||||||
|     QList<QString> args; |     QList<QString> args; | ||||||
| @@ -111,12 +118,61 @@ QVariant gpclient::helper::settings::get(const QString &key, const QVariant &def | |||||||
|     return _settings->value(key, defaultValue); |     return _settings->value(key, defaultValue); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QStringList gpclient::helper::settings::get_all(const QString &key, const QVariant &defaultValue) | ||||||
|  | { | ||||||
|  | 	QRegularExpression re(key); | ||||||
|  | 	return 	_settings->allKeys().filter(re); | ||||||
|  | } | ||||||
|  |  | ||||||
| void gpclient::helper::settings::save(const QString &key, const QVariant &value) | void gpclient::helper::settings::save(const QString &key, const QVariant &value) | ||||||
| { | { | ||||||
|     _settings->setValue(key, value); |     _settings->setValue(key, value); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void gpclient::helper::settings::clear() | void gpclient::helper::settings::clear() | ||||||
| { | { | ||||||
|     _settings->clear(); |     QStringList keys = _settings->allKeys(); | ||||||
|  |     for (const auto &key : qAsConst(keys)) { | ||||||
|  |         if (!reservedKeys.contains(key)) { | ||||||
|  |             _settings->remove(key); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     QWebEngineProfile::defaultProfile()->cookieStore()->deleteAllCookies(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | bool gpclient::helper::settings::secureSave(const QString &key, const QString &value) { | ||||||
|  |     WritePasswordJob job( QLatin1String("gpclient") ); | ||||||
|  |     job.setAutoDelete( false ); | ||||||
|  |     job.setKey( key ); | ||||||
|  |     job.setTextData( value ); | ||||||
|  |     QEventLoop loop; | ||||||
|  |     job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); | ||||||
|  |     job.start(); | ||||||
|  |     loop.exec(); | ||||||
|  |     if ( job.error() ) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool gpclient::helper::settings::secureGet(const QString &key, QString &value) { | ||||||
|  |     ReadPasswordJob job( QLatin1String("gpclient") ); | ||||||
|  |     job.setAutoDelete( false ); | ||||||
|  |     job.setKey( key ); | ||||||
|  |     QEventLoop loop; | ||||||
|  |     job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); | ||||||
|  |     job.start(); | ||||||
|  |     loop.exec(); | ||||||
|  |  | ||||||
|  |     const QString pw = job.textData(); | ||||||
|  |     if ( job.error() ) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     value = pw; | ||||||
|  |     return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,16 +1,16 @@ | |||||||
| #ifndef GPHELPER_H | #ifndef GPHELPER_H | ||||||
| #define GPHELPER_H | #define GPHELPER_H | ||||||
|  |  | ||||||
|  | #include <QtCore/QObject> | ||||||
|  | #include <QtCore/QUrlQuery> | ||||||
|  | #include <QtCore/QSettings> | ||||||
|  | #include <QtNetwork/QNetworkAccessManager> | ||||||
|  | #include <QtNetwork/QNetworkRequest> | ||||||
|  | #include <QtNetwork/QNetworkReply> | ||||||
|  |  | ||||||
| #include "samlloginwindow.h" | #include "samlloginwindow.h" | ||||||
| #include "gpgateway.h" | #include "gpgateway.h" | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
| #include <QNetworkAccessManager> |  | ||||||
| #include <QNetworkRequest> |  | ||||||
| #include <QNetworkReply> |  | ||||||
| #include <QUrlQuery> |  | ||||||
| #include <QSettings> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const QString UA = "PAN GlobalProtect"; | const QString UA = "PAN GlobalProtect"; | ||||||
|  |  | ||||||
| @@ -31,10 +31,15 @@ namespace gpclient { | |||||||
|         namespace settings { |         namespace settings { | ||||||
|  |  | ||||||
|             extern QSettings *_settings; |             extern QSettings *_settings; | ||||||
|  |             static const QStringList reservedKeys {"extraArgs", "clientos"}; | ||||||
|  |  | ||||||
|             QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); |             QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); | ||||||
|  |             QStringList get_all(const QString &key, const QVariant &defaultValue = QVariant()); | ||||||
|             void save(const QString &key, const QVariant &value); |             void save(const QString &key, const QVariant &value); | ||||||
|             void clear(); |             void clear(); | ||||||
|  |  | ||||||
|  |             bool secureSave(const QString &key, const QString &value); | ||||||
|  |             bool secureGet(const QString &key, QString &value); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
|  | #include <QtCore/QUrlQuery> | ||||||
|  |  | ||||||
| #include "loginparams.h" | #include "loginparams.h" | ||||||
|  | #include "gphelper.h" | ||||||
|  |  | ||||||
| #include <QUrlQuery> | using namespace gpclient::helper; | ||||||
|  |  | ||||||
| LoginParams::LoginParams() | LoginParams::LoginParams(const QString clientos) | ||||||
| { | { | ||||||
|     params.addQueryItem("prot", QUrl::toPercentEncoding("https:")); |     params.addQueryItem("prot", QUrl::toPercentEncoding("https:")); | ||||||
|     params.addQueryItem("server", ""); |     params.addQueryItem("server", ""); | ||||||
|     params.addQueryItem("inputSrc", ""); |     params.addQueryItem("inputStr", ""); | ||||||
|     params.addQueryItem("jnlpReady", "jnlpReady"); |     params.addQueryItem("jnlpReady", "jnlpReady"); | ||||||
|     params.addQueryItem("user", ""); |     params.addQueryItem("user", ""); | ||||||
|     params.addQueryItem("passwd", ""); |     params.addQueryItem("passwd", ""); | ||||||
| @@ -14,8 +17,18 @@ LoginParams::LoginParams() | |||||||
|     params.addQueryItem("ok", "Login"); |     params.addQueryItem("ok", "Login"); | ||||||
|     params.addQueryItem("direct", "yes"); |     params.addQueryItem("direct", "yes"); | ||||||
|     params.addQueryItem("clientVer", "4100"); |     params.addQueryItem("clientVer", "4100"); | ||||||
|     params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName())); |  | ||||||
|     params.addQueryItem("clientos", "Linux"); |     // add the clientos parameter if not empty | ||||||
|  |     if (!clientos.isEmpty()) { | ||||||
|  |         params.addQueryItem("clientos", clientos); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     auto osVersion = settings::get("os-version", "").toString(); | ||||||
|  |     if (osVersion.isEmpty()) { | ||||||
|  |         osVersion = QSysInfo::prettyProductName(); | ||||||
|  |     } | ||||||
|  |     params.addQueryItem("os-version", QUrl::toPercentEncoding(osVersion)); | ||||||
|  |  | ||||||
|     params.addQueryItem("portal-userauthcookie", ""); |     params.addQueryItem("portal-userauthcookie", ""); | ||||||
|     params.addQueryItem("portal-prelogonuserauthcookie", ""); |     params.addQueryItem("portal-prelogonuserauthcookie", ""); | ||||||
|     params.addQueryItem("prelogin-cookie", ""); |     params.addQueryItem("prelogin-cookie", ""); | ||||||
| @@ -56,6 +69,11 @@ void LoginParams::setPreloginCookie(const QString cookie) | |||||||
|     updateQueryItem("prelogin-cookie", cookie); |     updateQueryItem("prelogin-cookie", cookie); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void LoginParams::setInputStr(const QString inputStr) | ||||||
|  | { | ||||||
|  |     updateQueryItem("inputStr", inputStr); | ||||||
|  | } | ||||||
|  |  | ||||||
| QByteArray LoginParams::toUtf8() const | QByteArray LoginParams::toUtf8() const | ||||||
| { | { | ||||||
|     return params.toString().toUtf8(); |     return params.toString().toUtf8(); | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| #ifndef LOGINPARAMS_H | #ifndef LOGINPARAMS_H | ||||||
| #define LOGINPARAMS_H | #define LOGINPARAMS_H | ||||||
|  |  | ||||||
| #include <QUrlQuery> | #include <QtCore/QUrlQuery> | ||||||
|  |  | ||||||
| class LoginParams | class LoginParams | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     LoginParams(); |     LoginParams(const QString clientos); | ||||||
|     ~LoginParams(); |     ~LoginParams(); | ||||||
|  |  | ||||||
|     void setUser(const QString user); |     void setUser(const QString user); | ||||||
| @@ -15,6 +15,7 @@ public: | |||||||
|     void setUserAuthCookie(const QString cookie); |     void setUserAuthCookie(const QString cookie); | ||||||
|     void setPrelogonAuthCookie(const QString cookie); |     void setPrelogonAuthCookie(const QString cookie); | ||||||
|     void setPreloginCookie(const QString cookie); |     void setPreloginCookie(const QString cookie); | ||||||
|  |     void setInputStr(const QString inputStr); | ||||||
|  |  | ||||||
|     QByteArray toUtf8() const; |     QByteArray toUtf8() const; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,37 +1,96 @@ | |||||||
|  | #include <QtCore/QObject> | ||||||
|  | #include <QtCore/QString> | ||||||
|  | #include <QtCore/QStandardPaths> | ||||||
|  | #include <plog/Log.h> | ||||||
|  | #include <plog/Init.h> | ||||||
|  | #include <plog/Appenders/ColorConsoleAppender.h> | ||||||
|  | #include <plog/Formatters/TxtFormatter.h> | ||||||
|  |  | ||||||
| #include "singleapplication.h" | #include "singleapplication.h" | ||||||
| #include "gpclient.h" | #include "gpclient.h" | ||||||
|  | #include "vpn_dbus.h" | ||||||
|  | #include "vpn_json.h" | ||||||
| #include "enhancedwebview.h" | #include "enhancedwebview.h" | ||||||
|  | #include "sigwatch.h" | ||||||
|  | #include "version.h" | ||||||
|  |  | ||||||
| #include <QStandardPaths> | #define QT_AUTO_SCREEN_SCALE_FACTOR "QT_AUTO_SCREEN_SCALE_FACTOR" | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <plog/Appenders/ColorConsoleAppender.h> |  | ||||||
|  |  | ||||||
| static const QString version = "v1.2.5"; |  | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||||||
| { | { | ||||||
|     const QDir path = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/GlobalProtect-openconnect"; |     plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr); | ||||||
|     const QString logFile = path.path() + "/gpclient.log"; |     plog::init(plog::debug, &consoleAppender); | ||||||
|     if (!path.exists()) { |  | ||||||
|         path.mkpath("."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender; |     LOGI << "GlobalProtect started, version: " << VERSION; | ||||||
|     plog::init(plog::debug, logFile.toUtf8()).addAppender(&consoleAppender); |  | ||||||
|  |  | ||||||
|     PLOGI << "GlobalProtect started, version: " << version; |     auto port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); | ||||||
|  |     auto hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR)); | ||||||
|  |  | ||||||
|     QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); |     if (port.isEmpty()) { | ||||||
|  |  | ||||||
|     if (port == "") { |  | ||||||
|         qputenv(ENV_CDP_PORT, "12315"); |         qputenv(ENV_CDP_PORT, "12315"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SingleApplication app(argc, argv); |     if (hidpiSupport.isEmpty()) { | ||||||
|     GPClient w; |         qputenv(QT_AUTO_SCREEN_SCALE_FACTOR, "1"); | ||||||
|     w.show(); |     } | ||||||
|  |  | ||||||
|     QObject::connect(&app, &SingleApplication::instanceStarted, &w, &GPClient::activiate); |     SingleApplication app(argc, argv); | ||||||
|  |     app.setQuitOnLastWindowClosed(false); | ||||||
|  |  | ||||||
|  |     QCommandLineParser parser; | ||||||
|  |     parser.addHelpOption(); | ||||||
|  |     parser.addVersionOption(); | ||||||
|  |     parser.addPositionalArgument("server", "The URL of the VPN server. Optional."); | ||||||
|  |     parser.addPositionalArgument("gateway", "The URL of the specific VPN gateway. Optional."); | ||||||
|  |     parser.addOptions({ | ||||||
|  |       {"json", "Write the result of the handshake with the GlobalConnect server to stdout as JSON and terminate. Useful for scripting."}, | ||||||
|  |       {"now", "Do not show the dialog with the connect button; connect immediately instead."}, | ||||||
|  |       {"start-minimized", "Launch the client minimized."}, | ||||||
|  |       {"reset", "Reset the client's settings."}, | ||||||
|  |     }); | ||||||
|  |     parser.process(app); | ||||||
|  |  | ||||||
|  |     const auto positional = parser.positionalArguments(); | ||||||
|  |  | ||||||
|  |     auto *vpn = parser.isSet("json") // yes it leaks, but this is cleared on exit anyway | ||||||
|  |       ? static_cast<IVpn*>(new VpnJson(nullptr)) // Print to stdout and exit | ||||||
|  |       : static_cast<IVpn*>(new VpnDbus(nullptr)); // Contact GPService daemon via dbus | ||||||
|  |     GPClient w(nullptr, vpn); | ||||||
|  |  | ||||||
|  |     if (positional.size() > 0) { | ||||||
|  |       w.portal(positional.at(0)); | ||||||
|  |     } | ||||||
|  |     if (positional.size() > 1) { | ||||||
|  |       GPGateway gw; | ||||||
|  |       gw.setName(positional.at(1)); | ||||||
|  |       gw.setAddress(positional.at(1)); | ||||||
|  |       w.setCurrentGateway(gw); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     QObject::connect(&app, &SingleApplication::instanceStarted, &w, &GPClient::activate); | ||||||
|  |  | ||||||
|  |     UnixSignalWatcher sigwatch; | ||||||
|  |     sigwatch.watchForSignal(SIGINT); | ||||||
|  |     sigwatch.watchForSignal(SIGTERM); | ||||||
|  |     sigwatch.watchForSignal(SIGQUIT); | ||||||
|  |     sigwatch.watchForSignal(SIGHUP); | ||||||
|  |     QObject::connect(&sigwatch, &UnixSignalWatcher::unixSignal, &w, &GPClient::quit); | ||||||
|  |  | ||||||
|  |     if (parser.isSet("json")) { | ||||||
|  |         QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (parser.isSet("reset")) { | ||||||
|  |         w.reset(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (parser.isSet("now")) { | ||||||
|  |       w.doConnect(); | ||||||
|  |     } else if (parser.isSet("start-minimized")) { | ||||||
|  |       w.showMinimized(); | ||||||
|  |     } else { | ||||||
|  |       w.show(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return app.exec(); |     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,32 +1,38 @@ | |||||||
|  | #include <QtNetwork/QNetworkReply> | ||||||
|  | #include <plog/Log.h> | ||||||
|  |  | ||||||
| #include "portalauthenticator.h" | #include "portalauthenticator.h" | ||||||
| #include "gphelper.h" | #include "gphelper.h" | ||||||
| #include "normalloginwindow.h" | #include "standardloginwindow.h" | ||||||
| #include "samlloginwindow.h" | #include "samlloginwindow.h" | ||||||
| #include "loginparams.h" | #include "loginparams.h" | ||||||
| #include "preloginresponse.h" | #include "preloginresponse.h" | ||||||
| #include "portalconfigresponse.h" | #include "portalconfigresponse.h" | ||||||
| #include "gpgateway.h" | #include "gpgateway.h" | ||||||
|  |  | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <QNetworkReply> |  | ||||||
|  |  | ||||||
| using namespace gpclient::helper; | using namespace gpclient::helper; | ||||||
|  |  | ||||||
| PortalAuthenticator::PortalAuthenticator(const QString& portal) : QObject() | PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& clientos) : QObject() | ||||||
|   , portal(portal) |   , portal(portal) | ||||||
|   , preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") |   , clientos(clientos) | ||||||
|  |   , preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100") | ||||||
|   , configUrl("https://" + portal + "/global-protect/getconfig.esp") |   , configUrl("https://" + portal + "/global-protect/getconfig.esp") | ||||||
| { | { | ||||||
|  |     if (!clientos.isEmpty()) { | ||||||
|  |         preloginUrl = preloginUrl + "&clientos=" + clientos; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| PortalAuthenticator::~PortalAuthenticator() | PortalAuthenticator::~PortalAuthenticator() | ||||||
| { | { | ||||||
|     delete normalLoginWindow; |     delete standardLoginWindow; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalAuthenticator::authenticate() | void PortalAuthenticator::authenticate() | ||||||
| { | { | ||||||
|     PLOGI << "Preform portal prelogin at " << preloginUrl; |     attempts++; | ||||||
|  |  | ||||||
|  |     LOGI << QString("(%1/%2) attempts").arg(attempts).arg(MAX_ATTEMPTS) << ", perform portal prelogin at " << preloginUrl; | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(preloginUrl); |     QNetworkReply *reply = createRequest(preloginUrl); | ||||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished); |     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished); | ||||||
| @@ -34,20 +40,20 @@ void PortalAuthenticator::authenticate() | |||||||
|  |  | ||||||
| void PortalAuthenticator::onPreloginFinished() | void PortalAuthenticator::onPreloginFinished() | ||||||
| { | { | ||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |     auto *reply = qobject_cast<QNetworkReply*>(sender()); | ||||||
|  |  | ||||||
|     if (reply->error()) { |     if (reply->error()) { | ||||||
|         PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl).arg(reply->errorString()); |         LOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString()); | ||||||
|         emit preloginFailed("Error occurred on the portal prelogin interface."); |         emit preloginFailed("Error occurred on the portal prelogin interface."); | ||||||
|         delete reply; |         delete reply; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Portal prelogin succeeded."; |     LOGI << "Portal prelogin succeeded."; | ||||||
|  |  | ||||||
|     preloginResponse = PreloginResponse::parse(reply->readAll()); |     preloginResponse = PreloginResponse::parse(reply->readAll()); | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region(); |     LOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region(); | ||||||
|  |  | ||||||
|     if (preloginResponse.hasSamlAuthFields()) { |     if (preloginResponse.hasSamlAuthFields()) { | ||||||
|         // Do SAML authentication |         // Do SAML authentication | ||||||
| @@ -56,7 +62,7 @@ void PortalAuthenticator::onPreloginFinished() | |||||||
|         // Do normal username/password authentication |         // Do normal username/password authentication | ||||||
|         tryAutoLogin(); |         tryAutoLogin(); | ||||||
|     } else { |     } else { | ||||||
|         PLOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse())); |         LOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse())); | ||||||
|         emit preloginFailed("Unknown response for portal prelogin interface."); |         emit preloginFailed("Unknown response for portal prelogin interface."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -69,7 +75,7 @@ void PortalAuthenticator::tryAutoLogin() | |||||||
|     const QString password = settings::get("password").toString(); |     const QString password = settings::get("password").toString(); | ||||||
|  |  | ||||||
|     if (!username.isEmpty() && !password.isEmpty()) { |     if (!username.isEmpty() && !password.isEmpty()) { | ||||||
|         PLOGI << "Trying auto login using the saved credentials"; |         LOGI << "Trying auto login using the saved credentials"; | ||||||
|         isAutoLogin = true; |         isAutoLogin = true; | ||||||
|         fetchConfig(settings::get("username").toString(), settings::get("password").toString()); |         fetchConfig(settings::get("username").toString(), settings::get("password").toString()); | ||||||
|     } else { |     } else { | ||||||
| @@ -79,25 +85,21 @@ void PortalAuthenticator::tryAutoLogin() | |||||||
|  |  | ||||||
| void PortalAuthenticator::normalAuth() | void PortalAuthenticator::normalAuth() | ||||||
| { | { | ||||||
|     PLOGI << "Trying to launch the normal login window..."; |     LOGI << "Trying to launch the normal login window..."; | ||||||
|  |  | ||||||
|     normalLoginWindow = new NormalLoginWindow; |     standardLoginWindow = new StandardLoginWindow {portal, preloginResponse.labelUsername(), preloginResponse.labelPassword(), preloginResponse.authMessage() }; | ||||||
|     normalLoginWindow->setPortalAddress(portal); |  | ||||||
|     normalLoginWindow->setAuthMessage(preloginResponse.authMessage()); |  | ||||||
|     normalLoginWindow->setUsernameLabel(preloginResponse.labelUsername()); |  | ||||||
|     normalLoginWindow->setPasswordLabel(preloginResponse.labelPassword()); |  | ||||||
|  |  | ||||||
|     // Do login |     // Do login | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); |     connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); |     connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); | ||||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); |     connect(standardLoginWindow, &StandardLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); | ||||||
|  |  | ||||||
|     normalLoginWindow->show(); |     standardLoginWindow->show(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) | void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) | ||||||
| { | { | ||||||
|     normalLoginWindow->setProcessing(true); |     standardLoginWindow->setProcessing(true); | ||||||
|     fetchConfig(username, password); |     fetchConfig(username, password); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -108,19 +110,28 @@ void PortalAuthenticator::onLoginWindowRejected() | |||||||
|  |  | ||||||
| void PortalAuthenticator::onLoginWindowFinished() | void PortalAuthenticator::onLoginWindowFinished() | ||||||
| { | { | ||||||
|     delete normalLoginWindow; |     delete standardLoginWindow; | ||||||
|     normalLoginWindow = nullptr; |     standardLoginWindow = nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalAuthenticator::samlAuth() | void PortalAuthenticator::samlAuth() | ||||||
| { | { | ||||||
|     PLOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod(); |     LOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod(); | ||||||
|  |  | ||||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; |     auto *loginWindow = new SAMLLoginWindow(this->portal); | ||||||
|  |  | ||||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &PortalAuthenticator::onSAMLLoginSuccess); |     connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> samlResult) { | ||||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &PortalAuthenticator::onSAMLLoginFail); |         this->onSAMLLoginSuccess(samlResult); | ||||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString msg) { | ||||||
|  |         this->onSAMLLoginFail(code, msg); | ||||||
|  |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |     connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() { | ||||||
|  |         this->onLoginWindowRejected(); | ||||||
|  |         loginWindow->deleteLater(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl); |     loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl); | ||||||
| } | } | ||||||
| @@ -128,35 +139,40 @@ void PortalAuthenticator::samlAuth() | |||||||
| void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult) | void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult) | ||||||
| { | { | ||||||
|     if (samlResult.contains("preloginCookie")) { |     if (samlResult.contains("preloginCookie")) { | ||||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); |         LOGI << "SAML login succeeded, got the prelogin-cookie"; | ||||||
|     } else { |     } else { | ||||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); |         LOGI << "SAML login succeeded, got the portal-userauthcookie"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie")); |     fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie")); | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalAuthenticator::onSAMLLoginFail(const QString msg) | void PortalAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg) | ||||||
| { | { | ||||||
|     emitFail(msg); |     if (code == "ERR002" && attempts < MAX_ATTEMPTS) { | ||||||
|  |         LOGI << "Failed to authenticate, trying to re-authenticate..."; | ||||||
|  |         authenticate(); | ||||||
|  |     } else { | ||||||
|  |         emitFail(msg); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie) | void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie) | ||||||
| { | { | ||||||
|     LoginParams params; |     LoginParams loginParams { clientos }; | ||||||
|     params.setServer(portal); |     loginParams.setServer(portal); | ||||||
|     params.setUser(username); |     loginParams.setUser(username); | ||||||
|     params.setPassword(password); |     loginParams.setPassword(password); | ||||||
|     params.setPreloginCookie(preloginCookie); |     loginParams.setPreloginCookie(preloginCookie); | ||||||
|     params.setUserAuthCookie(userAuthCookie); |     loginParams.setUserAuthCookie(userAuthCookie); | ||||||
|  |  | ||||||
|     // Save the username and password for future use. |     // Save the username and password for future use. | ||||||
|     this->username = username; |     this->username = username; | ||||||
|     this->password = password; |     this->password = password; | ||||||
|  |  | ||||||
|     PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username; |     LOGI << "Fetching the portal config from " << configUrl; | ||||||
|  |  | ||||||
|     QNetworkReply *reply = createRequest(configUrl, params.toUtf8()); |     auto *reply = createRequest(configUrl, loginParams.toUtf8()); | ||||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished); |     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -165,11 +181,11 @@ void PortalAuthenticator::onFetchConfigFinished() | |||||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); |     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); | ||||||
|  |  | ||||||
|     if (reply->error()) { |     if (reply->error()) { | ||||||
|         PLOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString()); |         LOGE << 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 |         // Login failed, enable the fields of the normal login window | ||||||
|         if (normalLoginWindow) { |         if (standardLoginWindow) { | ||||||
|             normalLoginWindow->setProcessing(false); |             standardLoginWindow->setProcessing(false); | ||||||
|             openMessageBox("Portal login failed.", "Please check your credentials and try again."); |             openMessageBox("Portal login failed.", "Please check your credentials and try again."); | ||||||
|         } else if (isAutoLogin) { |         } else if (isAutoLogin) { | ||||||
|             isAutoLogin = false; |             isAutoLogin = false; | ||||||
| @@ -180,7 +196,7 @@ void PortalAuthenticator::onFetchConfigFinished() | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Fetch the portal config succeeded."; |     LOGI << "Fetch the portal config succeeded."; | ||||||
|     PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll()); |     PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll()); | ||||||
|  |  | ||||||
|     // Add the username & password to the response object |     // Add the username & password to the response object | ||||||
| @@ -188,13 +204,10 @@ void PortalAuthenticator::onFetchConfigFinished() | |||||||
|     response.setPassword(password); |     response.setPassword(password); | ||||||
|  |  | ||||||
|     // Close the login window |     // Close the login window | ||||||
|     if (normalLoginWindow) { |     if (standardLoginWindow) { | ||||||
|         PLOGI << "Closing the NormalLoginWindow..."; |         LOGI << "Closing the StandardLoginWindow..."; | ||||||
|  |  | ||||||
|         // Save the credentials for reuse |         standardLoginWindow->close(); | ||||||
|         settings::save("username", username); |  | ||||||
|         settings::save("password", password); |  | ||||||
|         normalLoginWindow->close(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     emit success(response, preloginResponse.region()); |     emit success(response, preloginResponse.region()); | ||||||
|   | |||||||
| @@ -1,18 +1,19 @@ | |||||||
| #ifndef PORTALAUTHENTICATOR_H | #ifndef PORTALAUTHENTICATOR_H | ||||||
| #define PORTALAUTHENTICATOR_H | #define PORTALAUTHENTICATOR_H | ||||||
|  |  | ||||||
|  | #include <QtCore/QObject> | ||||||
|  |  | ||||||
| #include "portalconfigresponse.h" | #include "portalconfigresponse.h" | ||||||
| #include "normalloginwindow.h" | #include "standardloginwindow.h" | ||||||
| #include "samlloginwindow.h" | #include "samlloginwindow.h" | ||||||
| #include "preloginresponse.h" | #include "preloginresponse.h" | ||||||
|  |  | ||||||
| #include <QObject> |  | ||||||
|  |  | ||||||
| class PortalAuthenticator : public QObject | class PortalAuthenticator : public QObject | ||||||
| { | { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| public: | public: | ||||||
|     explicit PortalAuthenticator(const QString& portal); |     explicit PortalAuthenticator(const QString& portal, const QString& clientos); | ||||||
|     ~PortalAuthenticator(); |     ~PortalAuthenticator(); | ||||||
|  |  | ||||||
|     void authenticate(); |     void authenticate(); | ||||||
| @@ -29,21 +30,25 @@ private slots: | |||||||
|     void onLoginWindowRejected(); |     void onLoginWindowRejected(); | ||||||
|     void onLoginWindowFinished(); |     void onLoginWindowFinished(); | ||||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> samlResult); |     void onSAMLLoginSuccess(const QMap<QString, QString> samlResult); | ||||||
|     void onSAMLLoginFail(const QString msg); |     void onSAMLLoginFail(const QString &code, const QString &msg); | ||||||
|     void onFetchConfigFinished(); |     void onFetchConfigFinished(); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|  |     static const auto MAX_ATTEMPTS{ 5 }; | ||||||
|  |  | ||||||
|     QString portal; |     QString portal; | ||||||
|  |     QString clientos; | ||||||
|     QString preloginUrl; |     QString preloginUrl; | ||||||
|     QString configUrl; |     QString configUrl; | ||||||
|     QString username; |     QString username; | ||||||
|     QString password; |     QString password; | ||||||
|  |  | ||||||
|  |     int attempts{ 0 }; | ||||||
|     PreloginResponse preloginResponse; |     PreloginResponse preloginResponse; | ||||||
|  |  | ||||||
|     bool isAutoLogin { false }; |     bool isAutoLogin{ false }; | ||||||
|  |  | ||||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; |     StandardLoginWindow *standardLoginWindow { nullptr }; | ||||||
|  |  | ||||||
|     void tryAutoLogin(); |     void tryAutoLogin(); | ||||||
|     void normalAuth(); |     void normalAuth(); | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #include "portalconfigresponse.h" | #include <QtCore/QXmlStreamReader> | ||||||
|  |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
| #include <plog/Log.h> | #include <plog/Log.h> | ||||||
|  |  | ||||||
|  | #include "portalconfigresponse.h" | ||||||
|  |  | ||||||
| QString PortalConfigResponse::xmlUserAuthCookie = "portal-userauthcookie"; | QString PortalConfigResponse::xmlUserAuthCookie = "portal-userauthcookie"; | ||||||
| QString PortalConfigResponse::xmlPrelogonUserAuthCookie = "portal-prelogonuserauthcookie"; | QString PortalConfigResponse::xmlPrelogonUserAuthCookie = "portal-prelogonuserauthcookie"; | ||||||
| QString PortalConfigResponse::xmlGateways = "gateways"; | QString PortalConfigResponse::xmlGateways = "gateways"; | ||||||
| @@ -17,7 +17,7 @@ PortalConfigResponse::~PortalConfigResponse() | |||||||
|  |  | ||||||
| PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) | PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) | ||||||
| { | { | ||||||
|     PLOGI << "Start parsing the portal configuration..."; |     LOGI << "Start parsing the portal configuration..."; | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader(xml); |     QXmlStreamReader xmlReader(xml); | ||||||
|     PortalConfigResponse response; |     PortalConfigResponse response; | ||||||
| @@ -29,140 +29,146 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) | |||||||
|         QString name = xmlReader.name().toString(); |         QString name = xmlReader.name().toString(); | ||||||
|  |  | ||||||
|         if (name == xmlUserAuthCookie) { |         if (name == xmlUserAuthCookie) { | ||||||
|             PLOGI << "Start reading " << name; |             LOGI << "Start reading " << name; | ||||||
|             response.setUserAuthCookie(xmlReader.readElementText()); |             response.setUserAuthCookie(xmlReader.readElementText()); | ||||||
|         } else if (name == xmlPrelogonUserAuthCookie) { |         } else if (name == xmlPrelogonUserAuthCookie) { | ||||||
|             PLOGI << "Start reading " << name; |             LOGI << "Start reading " << name; | ||||||
|             response.setPrelogonUserAuthCookie(xmlReader.readElementText()); |             response.setPrelogonUserAuthCookie(xmlReader.readElementText()); | ||||||
|         } else if (name == xmlGateways) { |         } else if (name == xmlGateways) { | ||||||
|             response.setAllGateways(parseGateways(xmlReader)); |             response.setAllGateways(parseGateways(xmlReader)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing portal configuration."; |     LOGI << "Finished parsing portal configuration."; | ||||||
|  |  | ||||||
|     return response; |     return response; | ||||||
| } | } | ||||||
|  |  | ||||||
| const QByteArray PortalConfigResponse::rawResponse() const | const QByteArray PortalConfigResponse::rawResponse() const | ||||||
| { | { | ||||||
|     return _rawResponse; |     return m_rawResponse; | ||||||
| } | } | ||||||
|  |  | ||||||
| QString PortalConfigResponse::username() const | const QString &PortalConfigResponse::username() const | ||||||
| { | { | ||||||
|     return _username; |     return m_username; | ||||||
| } | } | ||||||
|  |  | ||||||
| QString PortalConfigResponse::password() const | QString PortalConfigResponse::password() const | ||||||
| { | { | ||||||
|     return _password; |     return m_password; | ||||||
| } | } | ||||||
|  |  | ||||||
| QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) | QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) | ||||||
| { | { | ||||||
|     PLOGI << "Start parsing the gateways from portal configuration..."; |     LOGI << "Start parsing the gateways from portal configuration..."; | ||||||
|  |  | ||||||
|     QList<GPGateway> gateways; |     QList<GPGateway> gateways; | ||||||
|  |  | ||||||
|  |     while (xmlReader.name() != "external"){ | ||||||
|  |         xmlReader.readNext(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (xmlReader.name() != "list"){ | ||||||
|  |         xmlReader.readNext(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     while (xmlReader.name() != xmlGateways || !xmlReader.isEndElement()) { |     while (xmlReader.name() != xmlGateways || !xmlReader.isEndElement()) { | ||||||
|         xmlReader.readNext(); |         xmlReader.readNext(); | ||||||
|         // Parse the gateways -> external -> list -> entry |         // Parse the gateways -> external -> list -> entry | ||||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { |         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { | ||||||
|             GPGateway g; |             GPGateway g; | ||||||
|             QString address = xmlReader.attributes().value("name").toString(); |             parseGateway(xmlReader, g); | ||||||
|             g.setAddress(address); |  | ||||||
|             g.setPriorityRules(parsePriorityRules(xmlReader)); |  | ||||||
|             g.setName(parseGatewayName(xmlReader)); |  | ||||||
|             gateways.append(g); |             gateways.append(g); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the gateways."; |     LOGI << "Finished parsing the gateways."; | ||||||
|  |  | ||||||
|     return gateways; |     return gateways; | ||||||
| } | } | ||||||
|  |  | ||||||
| QMap<QString, int> PortalConfigResponse::parsePriorityRules(QXmlStreamReader &xmlReader) | void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) { | ||||||
| { |     LOGI << "Start parsing gateway..."; | ||||||
|     PLOGI << "Start parsing the priority rules..."; |  | ||||||
|  |  | ||||||
|     QMap<QString, int> priorityRules; |     auto finished = false; | ||||||
|  |     while (!finished) { | ||||||
|     while (xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) { |         if (reader.name() == "entry" && reader.isStartElement()) { | ||||||
|         xmlReader.readNext(); |             auto address = reader.attributes().value("name").toString(); | ||||||
|  |             gateway.setAddress(address); | ||||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { |         } else if (reader.name() == "description" && reader.isStartElement()) { // gateway name | ||||||
|             QString ruleName = xmlReader.attributes().value("name").toString(); |             gateway.setName(reader.readElementText()); | ||||||
|             // Read the priority tag |         } else if (reader.name() == "priority-rule" && reader.isStartElement()) { // priority rules | ||||||
|             xmlReader.readNextStartElement(); |             parsePriorityRule(reader, gateway); | ||||||
|             int ruleValue = xmlReader.readElementText().toUInt(); |  | ||||||
|             priorityRules.insert(ruleName, ruleValue); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         auto result = reader.readNext(); | ||||||
|  |         finished = result == QXmlStreamReader::Invalid || (reader.name() == "entry" && reader.isEndElement()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGI << "Finished parsing the priority rules."; |  | ||||||
|  |  | ||||||
|     return priorityRules; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader) | void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) { | ||||||
| { |     LOGI << "Start parsing priority rule..."; | ||||||
|     PLOGI << "Start parsing the gateway name..."; |  | ||||||
|  |  | ||||||
|     while (xmlReader.name() != "description" || !xmlReader.isEndElement()) { |     QMap<QString, int> priorityRules; | ||||||
|         xmlReader.readNext(); |     auto finished = false; | ||||||
|         if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) { |  | ||||||
|             PLOGI << "Finished parsing the gateway name"; |     while (!finished) { | ||||||
|             return xmlReader.readElementText(); |         // Parse the priority-rule -> entry | ||||||
|  |         if (reader.name() == "entry" && reader.isStartElement()) { | ||||||
|  |             auto ruleName = reader.attributes().value("name").toString(); | ||||||
|  |             // move to the priority value | ||||||
|  |             while (reader.readNextStartElement()) { | ||||||
|  |                 if (reader.name() == "priority") { | ||||||
|  |                     auto priority = reader.readElementText().toInt(); | ||||||
|  |                     priorityRules.insert(ruleName, priority); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |         auto result = reader.readNext(); | ||||||
|  |         finished = result == QXmlStreamReader::Invalid || (reader.name() == "priority-rule" && reader.isEndElement()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     PLOGE << "Error: <description> tag not found"; |     gateway.setPriorityRules(priorityRules); | ||||||
|     return ""; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| QString PortalConfigResponse::userAuthCookie() const | QString PortalConfigResponse::userAuthCookie() const | ||||||
| { | { | ||||||
|     return _userAuthCookie; |     return m_userAuthCookie; | ||||||
| } |  | ||||||
|  |  | ||||||
| QString PortalConfigResponse::prelogonUserAuthCookie() const |  | ||||||
| { |  | ||||||
|     return _prelogonAuthCookie; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| QList<GPGateway> PortalConfigResponse::allGateways() const | QList<GPGateway> PortalConfigResponse::allGateways() const | ||||||
| { | { | ||||||
|     return _gateways; |     return m_gateways; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setAllGateways(QList<GPGateway> gateways) | void PortalConfigResponse::setAllGateways(QList<GPGateway> gateways) | ||||||
| { | { | ||||||
|     _gateways = gateways; |     m_gateways = gateways; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setRawResponse(const QByteArray response) | void PortalConfigResponse::setRawResponse(const QByteArray response) | ||||||
| { | { | ||||||
|     _rawResponse = response; |     m_rawResponse = response; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setUsername(const QString username) | void PortalConfigResponse::setUsername(const QString username) | ||||||
| { | { | ||||||
|     _username = username; |     m_username = username; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setPassword(const QString password) | void PortalConfigResponse::setPassword(const QString password) | ||||||
| { | { | ||||||
|     _password = password; |     m_password = password; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setUserAuthCookie(const QString cookie) | void PortalConfigResponse::setUserAuthCookie(const QString cookie) | ||||||
| { | { | ||||||
|     _userAuthCookie = cookie; |     m_userAuthCookie = cookie; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie) | void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie) | ||||||
| { | { | ||||||
|     _prelogonAuthCookie = cookie; |     m_prelogonAuthCookie = cookie; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| #ifndef PORTALCONFIGRESPONSE_H | #ifndef PORTALCONFIGRESPONSE_H | ||||||
| #define PORTALCONFIGRESPONSE_H | #define PORTALCONFIGRESPONSE_H | ||||||
|  |  | ||||||
| #include "gpgateway.h" | #include <QtCore/QString> | ||||||
|  | #include <QtCore/QList> | ||||||
|  | #include <QtCore/QXmlStreamReader> | ||||||
|  |  | ||||||
| #include <QString> | #include "gpgateway.h" | ||||||
| #include <QList> |  | ||||||
| #include <QXmlStreamReader> |  | ||||||
|  |  | ||||||
| class PortalConfigResponse | class PortalConfigResponse | ||||||
| { | { | ||||||
| @@ -16,10 +16,9 @@ public: | |||||||
|     static PortalConfigResponse parse(const QByteArray xml); |     static PortalConfigResponse parse(const QByteArray xml); | ||||||
|  |  | ||||||
|     const QByteArray rawResponse() const; |     const QByteArray rawResponse() const; | ||||||
|     QString username() const; |     const QString &username() const; | ||||||
|     QString password() const; |     QString password() const; | ||||||
|     QString userAuthCookie() const; |     QString userAuthCookie() const; | ||||||
|     QString prelogonUserAuthCookie() const; |  | ||||||
|     QList<GPGateway> allGateways() const; |     QList<GPGateway> allGateways() const; | ||||||
|     void setAllGateways(QList<GPGateway> gateways); |     void setAllGateways(QList<GPGateway> gateways); | ||||||
|  |  | ||||||
| @@ -31,21 +30,22 @@ private: | |||||||
|     static QString xmlPrelogonUserAuthCookie; |     static QString xmlPrelogonUserAuthCookie; | ||||||
|     static QString xmlGateways; |     static QString xmlGateways; | ||||||
|  |  | ||||||
|     QByteArray _rawResponse; |     QByteArray m_rawResponse; | ||||||
|     QString _username; |     QString m_username; | ||||||
|     QString _password; |     QString m_password; | ||||||
|     QString _userAuthCookie; |     QString m_userAuthCookie; | ||||||
|     QString _prelogonAuthCookie; |     QString m_prelogonAuthCookie; | ||||||
|  |  | ||||||
|     QList<GPGateway> _gateways; |     QList<GPGateway> m_gateways; | ||||||
|  |  | ||||||
|     void setRawResponse(const QByteArray response); |     void setRawResponse(const QByteArray response); | ||||||
|     void setUserAuthCookie(const QString cookie); |     void setUserAuthCookie(const QString cookie); | ||||||
|     void setPrelogonUserAuthCookie(const QString cookie); |     void setPrelogonUserAuthCookie(const QString cookie); | ||||||
|  |  | ||||||
|     static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader); |     static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader); | ||||||
|     static QMap<QString, int> parsePriorityRules(QXmlStreamReader &xmlReader); |     static void parseGateway(QXmlStreamReader &reader, GPGateway &gateway); | ||||||
|     static QString parseGatewayName(QXmlStreamReader &xmlReader); |     static void parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway); | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // PORTALCONFIGRESPONSE_H | #endif // PORTALCONFIGRESPONSE_H | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| #include "preloginresponse.h" | #include <QtCore/QXmlStreamReader> | ||||||
|  | #include <QtCore/QMap> | ||||||
| #include <QXmlStreamReader> |  | ||||||
| #include <QMap> |  | ||||||
| #include <plog/Log.h> | #include <plog/Log.h> | ||||||
|  |  | ||||||
|  | #include "preloginresponse.h" | ||||||
|  |  | ||||||
| QString PreloginResponse::xmlAuthMessage = "authentication-message"; | QString PreloginResponse::xmlAuthMessage = "authentication-message"; | ||||||
| QString PreloginResponse::xmlLabelUsername = "username-label"; | QString PreloginResponse::xmlLabelUsername = "username-label"; | ||||||
| QString PreloginResponse::xmlLabelPassword = "password-label"; | QString PreloginResponse::xmlLabelPassword = "password-label"; | ||||||
| @@ -23,7 +23,7 @@ PreloginResponse::PreloginResponse() | |||||||
|  |  | ||||||
| PreloginResponse PreloginResponse::parse(const QByteArray& xml) | PreloginResponse PreloginResponse::parse(const QByteArray& xml) | ||||||
| { | { | ||||||
|     PLOGI << "Start parsing the prelogin response..."; |     LOGI << "Start parsing the prelogin response..."; | ||||||
|  |  | ||||||
|     QXmlStreamReader xmlReader(xml); |     QXmlStreamReader xmlReader(xml); | ||||||
|     PreloginResponse response; |     PreloginResponse response; | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #ifndef PRELOGINRESPONSE_H | #ifndef PRELOGINRESPONSE_H | ||||||
| #define PRELOGINRESPONSE_H | #define PRELOGINRESPONSE_H | ||||||
|  |  | ||||||
| #include <QString> | #include <QtCore/QString> | ||||||
| #include <QMap> | #include <QtCore/QMap> | ||||||
|  |  | ||||||
| class PreloginResponse | class PreloginResponse | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| <RCC> | <RCC> | ||||||
|     <qresource prefix="/images"> |     <qresource prefix="/images"> | ||||||
|         <file alias="logo.svg">com.yuezk.qt.GPClient.svg</file> |         <file alias="logo.svg">com.yuezk.qt.gpclient.svg</file> | ||||||
|         <file>connected.png</file> |         <file>connected.png</file> | ||||||
|         <file>pending.png</file> |         <file>pending.png</file> | ||||||
|         <file>not_connected.png</file> |         <file>not_connected.png</file> | ||||||
|         <file>radio_unselected.png</file> |         <file>radio_unselected.png</file> | ||||||
|         <file>radio_selected.png</file> |         <file>radio_selected.png</file> | ||||||
|  |         <file>settings_icon.png</file> | ||||||
|     </qresource> |     </qresource> | ||||||
| </RCC> | </RCC> | ||||||
|   | |||||||
| @@ -1,31 +1,40 @@ | |||||||
|  | #include <QtWidgets/QVBoxLayout> | ||||||
|  | #include <QtWebEngineWidgets/QWebEngineProfile> | ||||||
|  | #include <QtWebEngineWidgets/QWebEngineView> | ||||||
|  | #include <QWebEngineCookieStore> | ||||||
|  | #include <plog/Log.h> | ||||||
|  |  | ||||||
|  | #include "INIReader.h" | ||||||
| #include "samlloginwindow.h" | #include "samlloginwindow.h" | ||||||
|  |  | ||||||
| #include <QVBoxLayout> | SAMLLoginWindow::SAMLLoginWindow(QString portal, QWidget *parent) | ||||||
| #include <plog/Log.h> |  | ||||||
| #include <QWebEngineProfile> |  | ||||||
| #include <QWebEngineView> |  | ||||||
|  |  | ||||||
| SAMLLoginWindow::SAMLLoginWindow(QWidget *parent) |  | ||||||
|     : QDialog(parent) |     : QDialog(parent) | ||||||
|     , webView(new EnhancedWebView(this)) |     , webView(new EnhancedWebView(this)) | ||||||
| { | { | ||||||
|     setWindowTitle("GlobalProtect SAML Login"); |     setWindowTitle("GlobalProtect Login"); | ||||||
|     setModal(true); |     setModal(true); | ||||||
|     resize(700, 550); |     resize(700, 550); | ||||||
|  |  | ||||||
|     QVBoxLayout *verticalLayout = new QVBoxLayout(this); |     QVBoxLayout *verticalLayout = new QVBoxLayout(this); | ||||||
|     webView->setUrl(QUrl("about:blank")); |     webView->setUrl(QUrl("about:blank")); | ||||||
|     // webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); |     webView->setAttribute(Qt::WA_DeleteOnClose); | ||||||
|     verticalLayout->addWidget(webView); |     verticalLayout->addWidget(webView); | ||||||
|  |  | ||||||
|     webView->initialize(); |     webView->initialize(); | ||||||
|     connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived); |     connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived); | ||||||
|     connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished); |     connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished); | ||||||
| } |  | ||||||
|  |  | ||||||
| SAMLLoginWindow::~SAMLLoginWindow() |     // Portal | ||||||
| { |     this->portal = portal; | ||||||
|     delete webView; |  | ||||||
|  |     // Show the login window automatically when exceeds the MAX_WAIT_TIME | ||||||
|  |     QTimer::singleShot(MAX_WAIT_TIME, this, [this]() { | ||||||
|  |         if (failed) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         LOGI << "MAX_WAIT_TIME exceeded, display the login window."; | ||||||
|  |         this->show(); | ||||||
|  |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SAMLLoginWindow::closeEvent(QCloseEvent *event) | void SAMLLoginWindow::closeEvent(QCloseEvent *event) | ||||||
| @@ -34,47 +43,55 @@ void SAMLLoginWindow::closeEvent(QCloseEvent *event) | |||||||
|     reject(); |     reject(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloingUrl) | void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloginUrl) | ||||||
| { | { | ||||||
|  |     webView->page()->profile()->cookieStore()->deleteSessionCookies(); | ||||||
|  |  | ||||||
|     if (samlMethod == "POST") { |     if (samlMethod == "POST") { | ||||||
|         webView->setHtml(samlRequest, preloingUrl); |         webView->setHtml(samlRequest, preloginUrl); | ||||||
|     } else if (samlMethod == "REDIRECT") { |     } else if (samlMethod == "REDIRECT") { | ||||||
|  |         LOGI << "Redirect to " << samlRequest; | ||||||
|         webView->load(samlRequest); |         webView->load(samlRequest); | ||||||
|     } else { |     } else { | ||||||
|         PLOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; |         LOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; | ||||||
|         emit fail("Unknown saml-auth-method, got " + samlMethod); |         failed = true; | ||||||
|  |         emit fail("ERR001", "Unknown saml-auth-method, got " + samlMethod); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void SAMLLoginWindow::onResponseReceived(QJsonObject params) | void SAMLLoginWindow::onResponseReceived(QJsonObject params) | ||||||
| { | { | ||||||
|     QString type = params.value("type").toString(); |     const auto type = params.value("type").toString(); | ||||||
|     // Skip non-document response |     // Skip non-document response | ||||||
|     if (type != "Document") { |     if (type != "Document") { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QJsonObject response = params.value("response").toObject(); |     auto response = params.value("response").toObject(); | ||||||
|     QJsonObject headers = response.value("headers").toObject(); |     auto headers = response.value("headers").toObject(); | ||||||
|  |  | ||||||
|     const QString username = headers.value("saml-username").toString(); |     LOGI << "Trying to receive authentication cookie from " << response.value("url").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(); |     const auto username = headers.value("saml-username").toString(); | ||||||
|  |     const auto preloginCookie = headers.value("prelogin-cookie").toString(); | ||||||
|  |     const auto userAuthCookie = headers.value("portal-userauthcookie").toString(); | ||||||
|  |  | ||||||
|  |     this->checkSamlResult(username, preloginCookie, userAuthCookie); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SAMLLoginWindow::checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie) | ||||||
|  | { | ||||||
|  |     LOGI << "Checking the authentication result..."; | ||||||
|  |  | ||||||
|     if (!username.isEmpty()) { |     if (!username.isEmpty()) { | ||||||
|         LOGI << "Got username from SAML response headers " << username; |  | ||||||
|         samlResult.insert("username", username); |         samlResult.insert("username", username); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!preloginCookie.isEmpty()) { |     if (!preloginCookie.isEmpty()) { | ||||||
|         LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie; |  | ||||||
|         samlResult.insert("preloginCookie", preloginCookie); |         samlResult.insert("preloginCookie", preloginCookie); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!userAuthCookie.isEmpty()) { |     if (!userAuthCookie.isEmpty()) { | ||||||
|         LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie; |  | ||||||
|         samlResult.insert("userAuthCookie", userAuthCookie); |         samlResult.insert("userAuthCookie", userAuthCookie); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -88,12 +105,61 @@ void SAMLLoginWindow::onResponseReceived(QJsonObject params) | |||||||
|  |  | ||||||
|         emit success(samlResult); |         emit success(samlResult); | ||||||
|         accept(); |         accept(); | ||||||
|     } else { |  | ||||||
|         this->show(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void SAMLLoginWindow::onLoadFinished() | void SAMLLoginWindow::onLoadFinished() | ||||||
| { | { | ||||||
|      LOGI << "Load finished " << this->webView->page()->url().toString(); |      LOGI << "Load finished " << webView->page()->url().toString(); | ||||||
|  |      webView->page()->toHtml([this] (const QString &html) { this->handleHtml(html); }); | ||||||
|  |      QMap<QString, QString> credentials = this->loadCredentials(); | ||||||
|  |      webView->page()->runJavaScript("document.getElementById('username').value='" + credentials["username"] + "';"); | ||||||
|  |      webView->page()->runJavaScript("document.getElementById('password').value='" + credentials["password"] + "';"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void SAMLLoginWindow::handleHtml(const QString &html) | ||||||
|  | { | ||||||
|  |     // try to check the html body and extract from there | ||||||
|  |     const auto samlAuthStatus = parseTag("saml-auth-status", html); | ||||||
|  |  | ||||||
|  |     if (samlAuthStatus == "1") { | ||||||
|  |         const auto preloginCookie = parseTag("prelogin-cookie", html); | ||||||
|  |         const auto username = parseTag("saml-username", html); | ||||||
|  |         const auto userAuthCookie = parseTag("portal-userauthcookie", html); | ||||||
|  |  | ||||||
|  |         checkSamlResult(username, preloginCookie, userAuthCookie); | ||||||
|  |     } else if (samlAuthStatus == "-1") { | ||||||
|  |         LOGI << "SAML authentication failed..."; | ||||||
|  |         failed = true; | ||||||
|  |         emit fail("ERR002", "Authentication failed, please try again."); | ||||||
|  |     } else { | ||||||
|  |         show(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QString SAMLLoginWindow::parseTag(const QString &tag, const QString &html) { | ||||||
|  |     const QRegularExpression expression(QString("<%1>(.*)</%1>").arg(tag)); | ||||||
|  |     return expression.match(html).captured(1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QMap<QString, QString> SAMLLoginWindow::loadCredentials() | ||||||
|  | { | ||||||
|  |     std::string home = getenv("HOME"); | ||||||
|  |     std::string iniFile = home + "/.gpclient-credentials"; | ||||||
|  |     INIReader reader(iniFile); | ||||||
|  |  | ||||||
|  |     QMap<QString, QString> credentials; | ||||||
|  |     if (reader.ParseError() < 0) { | ||||||
|  |         LOGE << "File '" << iniFile << "' not found."; | ||||||
|  |         return credentials; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (reader.HasSection(this->portal.toStdString())) { | ||||||
|  |         credentials.insert(QString("username"), QString::fromStdString(reader.Get(this->portal.toStdString(), "username", ""))); | ||||||
|  |         credentials.insert(QString("password"), QString::fromStdString(reader.Get(this->portal.toStdString(), "password", ""))); | ||||||
|  |     } else { | ||||||
|  |         LOGE << "No credentials found for '" << this->portal.toStdString() << "' in '" << iniFile << "'"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return credentials; | ||||||
|  | } | ||||||
| @@ -1,35 +1,43 @@ | |||||||
| #ifndef SAMLLOGINWINDOW_H | #ifndef SAMLLOGINWINDOW_H | ||||||
| #define SAMLLOGINWINDOW_H | #define SAMLLOGINWINDOW_H | ||||||
|  |  | ||||||
| #include "enhancedwebview.h" | #include <QtCore/QMap> | ||||||
|  | #include <QtGui/QCloseEvent> | ||||||
|  | #include <QtWidgets/QDialog> | ||||||
|  |  | ||||||
| #include <QDialog> | #include "enhancedwebview.h" | ||||||
| #include <QMap> |  | ||||||
| #include <QCloseEvent> |  | ||||||
|  |  | ||||||
| class SAMLLoginWindow : public QDialog | class SAMLLoginWindow : public QDialog | ||||||
| { | { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
| public: | public: | ||||||
|     explicit SAMLLoginWindow(QWidget *parent = nullptr); |     explicit SAMLLoginWindow(QString portal, QWidget *parent = nullptr); | ||||||
|     ~SAMLLoginWindow(); |  | ||||||
|  |  | ||||||
|     void login(const QString samlMethod, const QString samlRequest, const QString preloingUrl); |     void login(const QString samlMethod, const QString samlRequest, const QString preloginUrl); | ||||||
|  |     QMap<QString, QString> loadCredentials(); | ||||||
|  |  | ||||||
| signals: | signals: | ||||||
|     void success(QMap<QString, QString> samlResult); |     void success(QMap<QString, QString> samlResult); | ||||||
|     void fail(const QString msg); |     void fail(const QString code, const QString msg); | ||||||
|  |  | ||||||
| private slots: | private slots: | ||||||
|     void onResponseReceived(QJsonObject params); |     void onResponseReceived(QJsonObject params); | ||||||
|     void onLoadFinished(); |     void onLoadFinished(); | ||||||
|  |     void checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     EnhancedWebView *webView; |     static const auto MAX_WAIT_TIME { 10 * 1000 }; | ||||||
|  |  | ||||||
|  |     bool failed { false }; | ||||||
|  |     EnhancedWebView *webView { nullptr }; | ||||||
|     QMap<QString, QString> samlResult; |     QMap<QString, QString> samlResult; | ||||||
|  |     QString portal; | ||||||
|  |  | ||||||
|     void closeEvent(QCloseEvent *event); |     void closeEvent(QCloseEvent *event); | ||||||
|  |     void handleHtml(const QString &html); | ||||||
|  |  | ||||||
|  |     static QString parseTag(const QString &tag, const QString &html); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // SAMLLOGINWINDOW_H | #endif // SAMLLOGINWINDOW_H | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								GPClient/settings_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								GPClient/settings_icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										42
									
								
								GPClient/settingsdialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								GPClient/settingsdialog.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #include "settingsdialog.h" | ||||||
|  | #include "ui_settingsdialog.h" | ||||||
|  |  | ||||||
|  | SettingsDialog::SettingsDialog(QWidget *parent) : | ||||||
|  |     QDialog(parent), | ||||||
|  |     ui(new Ui::SettingsDialog) | ||||||
|  | { | ||||||
|  |     ui->setupUi(this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SettingsDialog::~SettingsDialog() | ||||||
|  | { | ||||||
|  |     delete ui; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SettingsDialog::setExtraArgs(QString extraArgs) | ||||||
|  | { | ||||||
|  |     ui->extraArgsInput->setPlainText(extraArgs); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QString SettingsDialog::extraArgs() | ||||||
|  | { | ||||||
|  |     return ui->extraArgsInput->toPlainText().trimmed(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SettingsDialog::setClientos(QString clientos) | ||||||
|  | { | ||||||
|  |     ui->clientosInput->setText(clientos); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QString SettingsDialog::clientos() | ||||||
|  | { | ||||||
|  |     return ui->clientosInput->text(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SettingsDialog::setOsVersion(QString osVersion) { | ||||||
|  |     ui->osVersionInput->setText(osVersion); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | QString SettingsDialog::osVersion() { | ||||||
|  |     return ui->osVersionInput->text(); | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								GPClient/settingsdialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								GPClient/settingsdialog.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | #ifndef SETTINGSDIALOG_H | ||||||
|  | #define SETTINGSDIALOG_H | ||||||
|  |  | ||||||
|  | #include <QtWidgets/QDialog> | ||||||
|  |  | ||||||
|  | namespace Ui { | ||||||
|  | class SettingsDialog; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SettingsDialog : public QDialog | ||||||
|  | { | ||||||
|  |     Q_OBJECT | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit SettingsDialog(QWidget *parent = nullptr); | ||||||
|  |     ~SettingsDialog(); | ||||||
|  |  | ||||||
|  |     void setExtraArgs(QString extraArgs); | ||||||
|  |     QString extraArgs(); | ||||||
|  |  | ||||||
|  |     void setClientos(QString clientos); | ||||||
|  |     QString clientos(); | ||||||
|  |  | ||||||
|  |     void setOsVersion(QString osVersion); | ||||||
|  |     QString osVersion(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Ui::SettingsDialog *ui; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // SETTINGSDIALOG_H | ||||||
							
								
								
									
										117
									
								
								GPClient/settingsdialog.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								GPClient/settingsdialog.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>SettingsDialog</class> | ||||||
|  |  <widget class="QDialog" name="SettingsDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>488</width> | ||||||
|  |     <height>220</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="sizePolicy"> | ||||||
|  |    <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||||
|  |     <horstretch>0</horstretch> | ||||||
|  |     <verstretch>0</verstretch> | ||||||
|  |    </sizepolicy> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>Settings</string> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowIcon"> | ||||||
|  |    <iconset resource="resources.qrc"> | ||||||
|  |     <normaloff>:/images/connected.png</normaloff>:/images/connected.png</iconset> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QFormLayout" name="formLayout_3"> | ||||||
|  |    <item row="0" column="0"> | ||||||
|  |     <widget class="QLabel" name="label"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Custom Parameters:</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="0" column="1"> | ||||||
|  |     <widget class="QPlainTextEdit" name="extraArgsInput"> | ||||||
|  |      <property name="readOnly"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="placeholderText"> | ||||||
|  |       <string>The configuration has been moved to "/etc/gpservice/gp.conf"</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="0"> | ||||||
|  |     <widget class="QLabel" name="label_2"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>clientos:</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="1"> | ||||||
|  |     <widget class="QLineEdit" name="clientosInput"> | ||||||
|  |      <property name="placeholderText"> | ||||||
|  |       <string>e.g., Windows</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="1"> | ||||||
|  |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Horizontal</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="standardButtons"> | ||||||
|  |       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="1"> | ||||||
|  |     <widget class="QLineEdit" name="osVersionInput"/> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="0"> | ||||||
|  |     <widget class="QLabel" name="label_3"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>os-version:</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources> | ||||||
|  |   <include location="resources.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>SettingsDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>248</x> | ||||||
|  |      <y>254</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>157</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>SettingsDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>316</x> | ||||||
|  |      <y>260</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>286</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
							
								
								
									
										60
									
								
								GPClient/standardloginwindow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								GPClient/standardloginwindow.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | #include <QtGui/QCloseEvent> | ||||||
|  |  | ||||||
|  | #include "standardloginwindow.h" | ||||||
|  | #include "ui_standardloginwindow.h" | ||||||
|  | #include "gphelper.h" | ||||||
|  |  | ||||||
|  | using namespace gpclient::helper; | ||||||
|  |  | ||||||
|  | StandardLoginWindow::StandardLoginWindow(const QString &portalAddress, const QString &labelUsername, | ||||||
|  |                                          const QString &labelPassword, const QString &authMessage) : | ||||||
|  |         QDialog(nullptr), | ||||||
|  |         ui(new Ui::StandardLoginWindow) { | ||||||
|  |     ui->setupUi(this); | ||||||
|  |     ui->portalAddress->setText(portalAddress); | ||||||
|  |     ui->username->setPlaceholderText(labelUsername); | ||||||
|  |     ui->password->setPlaceholderText(labelPassword); | ||||||
|  |     ui->authMessage->setText(authMessage); | ||||||
|  |  | ||||||
|  |     autocomplete(); | ||||||
|  |  | ||||||
|  |     setWindowTitle("GlobalProtect Login"); | ||||||
|  |     setFixedSize(width(), height()); | ||||||
|  |     setModal(true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StandardLoginWindow::autocomplete() { | ||||||
|  |     QString username, password; | ||||||
|  |     settings::secureGet("username", username); | ||||||
|  |     settings::secureGet("password", password); | ||||||
|  |  | ||||||
|  |     if (!username.isEmpty() && !password.isEmpty()) { | ||||||
|  |         ui->username->setText(username); | ||||||
|  |         ui->password->setText(password); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StandardLoginWindow::setProcessing(bool isProcessing) { | ||||||
|  |     ui->username->setReadOnly(isProcessing); | ||||||
|  |     ui->password->setReadOnly(isProcessing); | ||||||
|  |     ui->loginButton->setDisabled(isProcessing); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StandardLoginWindow::on_loginButton_clicked() { | ||||||
|  |     const QString username = ui->username->text().trimmed(); | ||||||
|  |     const QString password = ui->password->text().trimmed(); | ||||||
|  |  | ||||||
|  |     if (username.isEmpty() || password.isEmpty()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     settings::secureSave("username", username); | ||||||
|  |     settings::secureSave("password", password); | ||||||
|  |  | ||||||
|  |     emit performLogin(username, password); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StandardLoginWindow::closeEvent(QCloseEvent *event) { | ||||||
|  |     event->accept(); | ||||||
|  |     reject(); | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								GPClient/standardloginwindow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								GPClient/standardloginwindow.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | #ifndef STANDARDLOGINWINDOW_H | ||||||
|  | #define STANDARDLOGINWINDOW_H | ||||||
|  |  | ||||||
|  | #include <QtWidgets/QDialog> | ||||||
|  |  | ||||||
|  | namespace Ui { | ||||||
|  |     class StandardLoginWindow; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class StandardLoginWindow : public QDialog { | ||||||
|  | Q_OBJECT | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit StandardLoginWindow(const QString &portalAddress, const QString &labelUsername, | ||||||
|  |                                  const QString &labelPassword, const QString &authMessage); | ||||||
|  |  | ||||||
|  |     void setProcessing(bool isProcessing); | ||||||
|  |  | ||||||
|  | private slots: | ||||||
|  |  | ||||||
|  |     void on_loginButton_clicked(); | ||||||
|  |  | ||||||
|  | signals: | ||||||
|  |  | ||||||
|  |     void performLogin(QString username, QString password); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Ui::StandardLoginWindow *ui; | ||||||
|  |  | ||||||
|  |     void closeEvent(QCloseEvent *event); | ||||||
|  |     void autocomplete(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // STANDARDLOGINWINDOW_H | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <ui version="4.0"> | <ui version="4.0"> | ||||||
|  <class>NormalLoginWindow</class> |  <class>StandardLoginWindow</class> | ||||||
|  <widget class="QDialog" name="NormalLoginWindow"> |  <widget class="QDialog" name="StandardLoginWindow"> | ||||||
|   <property name="geometry"> |   <property name="geometry"> | ||||||
|    <rect> |    <rect> | ||||||
|     <x>0</x> |     <x>0</x> | ||||||
							
								
								
									
										24
									
								
								GPClient/vpn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								GPClient/vpn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | #ifndef VPN_H | ||||||
|  | #define VPN_H | ||||||
|  | #include <QtCore/QObject> | ||||||
|  | #include <QtCore/QString> | ||||||
|  |  | ||||||
|  | class IVpn | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     virtual ~IVpn() = default; | ||||||
|  |  | ||||||
|  |     virtual void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) = 0; | ||||||
|  |     virtual void disconnect() = 0; | ||||||
|  |     virtual int status() = 0; | ||||||
|  |  | ||||||
|  | // signals: // SIGNALS | ||||||
|  | //     virtual void connected(); | ||||||
|  | //     virtual void disconnected(); | ||||||
|  | //     virtual void error(const QString &errorMessage); | ||||||
|  | //     virtual void logAvailable(const QString &log); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Q_DECLARE_INTERFACE(IVpn, "IVpn") // define this out of namespace scope | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										13
									
								
								GPClient/vpn_dbus.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								GPClient/vpn_dbus.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #include "vpn_dbus.h" | ||||||
|  |  | ||||||
|  | void VpnDbus::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) { | ||||||
|  |     inner->connect(preferredServer, username, passwd); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VpnDbus::disconnect() { | ||||||
|  |     inner->disconnect(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int VpnDbus::status() { | ||||||
|  |     return inner->status(); | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								GPClient/vpn_dbus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								GPClient/vpn_dbus.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | #ifndef VPN_DBUS_H | ||||||
|  | #define VPN_DBUS_H | ||||||
|  | #include "vpn.h" | ||||||
|  | #include "gpserviceinterface.h" | ||||||
|  |  | ||||||
|  | class VpnDbus : public QObject, public IVpn | ||||||
|  | { | ||||||
|  |   Q_OBJECT | ||||||
|  |   Q_INTERFACES(IVpn) | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |   com::yuezk::qt::GPService *inner; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |   VpnDbus(QObject *parent) : QObject(parent) { | ||||||
|  |     inner = new com::yuezk::qt::GPService("com.yuezk.qt.GPService", "/", QDBusConnection::systemBus(), this); | ||||||
|  |     QObject::connect(inner, &com::yuezk::qt::GPService::connected, this, &VpnDbus::connected); | ||||||
|  |     QObject::connect(inner, &com::yuezk::qt::GPService::disconnected, this, &VpnDbus::disconnected); | ||||||
|  |     QObject::connect(inner, &com::yuezk::qt::GPService::error, this, &VpnDbus::error); | ||||||
|  |     QObject::connect(inner, &com::yuezk::qt::GPService::logAvailable, this, &VpnDbus::logAvailable); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd); | ||||||
|  |   void disconnect(); | ||||||
|  |   int status(); | ||||||
|  |  | ||||||
|  | signals: // SIGNALS | ||||||
|  |   void connected(); | ||||||
|  |   void disconnected(); | ||||||
|  |   void error(QString errorMessage); | ||||||
|  |   void logAvailable(QString log); | ||||||
|  | }; | ||||||
|  | #endif | ||||||
							
								
								
									
										24
									
								
								GPClient/vpn_json.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								GPClient/vpn_json.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | #include "vpn_json.h" | ||||||
|  | #include <QTextStream> | ||||||
|  | #include <QJsonDocument> | ||||||
|  | #include <QJsonObject> | ||||||
|  | #include <QJsonArray> | ||||||
|  |  | ||||||
|  | void VpnJson::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) { | ||||||
|  |     QJsonArray sl; | ||||||
|  |     for (const QString &srv : servers) { | ||||||
|  |       sl.push_back(QJsonValue(srv)); | ||||||
|  |     } | ||||||
|  |     QJsonObject j; | ||||||
|  |     j["server"] = preferredServer; | ||||||
|  |     j["availableServers"] = sl; | ||||||
|  |     j["cookie"] = passwd; | ||||||
|  |     QTextStream(stdout) << QJsonDocument(j).toJson(QJsonDocument::Compact) << "\n"; | ||||||
|  |     emit connected(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VpnJson::disconnect() { /* nop */ } | ||||||
|  |  | ||||||
|  | int VpnJson::status() { | ||||||
|  |     return 4; // disconnected | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								GPClient/vpn_json.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								GPClient/vpn_json.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | #ifndef VPN_JSON_H | ||||||
|  | #define VPN_JSON_H | ||||||
|  | #include "vpn.h" | ||||||
|  |  | ||||||
|  | class VpnJson : public QObject, public IVpn | ||||||
|  | { | ||||||
|  |   Q_OBJECT | ||||||
|  |   Q_INTERFACES(IVpn) | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |   VpnJson(QObject *parent) : QObject(parent) {} | ||||||
|  |  | ||||||
|  |   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd); | ||||||
|  |   void disconnect(); | ||||||
|  |   int status(); | ||||||
|  |  | ||||||
|  | signals: // SIGNALS | ||||||
|  |   void connected(); | ||||||
|  |   void disconnected(); | ||||||
|  |   void error(const QString &errorMessage); | ||||||
|  |   void logAvailable(const QString &log); | ||||||
|  | }; | ||||||
|  | #endif | ||||||
							
								
								
									
										83
									
								
								GPService/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								GPService/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | include("${CMAKE_SOURCE_DIR}/cmake/Add3rdParty.cmake") | ||||||
|  |  | ||||||
|  | project(GPService) | ||||||
|  |  | ||||||
|  | set(gpservice_GENERATED_SOURCES) | ||||||
|  |  | ||||||
|  | execute_process(COMMAND logname OUTPUT_VARIABLE CMAKE_LOGNAME) | ||||||
|  | string(STRIP "${CMAKE_LOGNAME}" CMAKE_LOGNAME) | ||||||
|  |  | ||||||
|  | message(STATUS "CMAKE_LOGNAME: ${CMAKE_LOGNAME}") | ||||||
|  |  | ||||||
|  | configure_file(dbus/com.yuezk.qt.GPService.conf.in dbus/com.yuezk.qt.GPService.conf) | ||||||
|  | configure_file(dbus/com.yuezk.qt.GPService.service.in dbus/com.yuezk.qt.GPService.service) | ||||||
|  | configure_file(systemd/gpservice.service.in systemd/gpservice.service) | ||||||
|  |  | ||||||
|  | # generate the dbus xml definition | ||||||
|  | qt5_generate_dbus_interface( | ||||||
|  |     gpservice.h | ||||||
|  |     ${CMAKE_BINARY_DIR}/com.yuezk.qt.GPService.xml | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | # generate dbus adaptor | ||||||
|  | qt5_add_dbus_adaptor( | ||||||
|  |     gpservice_GENERATED_SOURCES | ||||||
|  |     ${CMAKE_BINARY_DIR}/com.yuezk.qt.GPService.xml | ||||||
|  |     gpservice.h | ||||||
|  |     GPService | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_executable(gpservice | ||||||
|  |     gpservice.h | ||||||
|  |     gpservice.cpp | ||||||
|  |     main.cpp | ||||||
|  |     ${gpservice_GENERATED_SOURCES} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_3rdparty( | ||||||
|  |     SingleApplication | ||||||
|  |     GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication.git | ||||||
|  |     GIT_TAG v3.3.0 | ||||||
|  |     CMAKE_ARGS | ||||||
|  |         -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} | ||||||
|  |         -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} | ||||||
|  |         -DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} | ||||||
|  |         -DCMAKE_PREFIX_PATH=$ENV{CMAKE_PREFIX_PATH} | ||||||
|  |         -DQAPPLICATION_CLASS=QCoreApplication | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | ExternalProject_Get_Property(SingleApplication-${PROJECT_NAME} SOURCE_DIR BINARY_DIR) | ||||||
|  |  | ||||||
|  | set(SingleApplication_INCLUDE_DIR ${SOURCE_DIR}) | ||||||
|  | set(SingleApplication_LIBRARY ${BINARY_DIR}/libSingleApplication.a) | ||||||
|  |  | ||||||
|  | add_dependencies(gpservice SingleApplication-${PROJECT_NAME}) | ||||||
|  |  | ||||||
|  | target_include_directories(gpservice PRIVATE | ||||||
|  |     ${CMAKE_CURRENT_SOURCE_DIR} | ||||||
|  |     ${CMAKE_CURRENT_BINARY_DIR} | ||||||
|  |     ${SingleApplication_INCLUDE_DIR} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | target_link_libraries(gpservice | ||||||
|  |     ${SingleApplication_LIBRARY} | ||||||
|  |     Qt5::Core | ||||||
|  |     Qt5::Network | ||||||
|  |     Qt5::DBus | ||||||
|  |     QtSignals | ||||||
|  |     inih | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | target_compile_definitions(gpservice PUBLIC QAPPLICATION_CLASS=QCoreApplication) | ||||||
|  |  | ||||||
|  | install(TARGETS gpservice DESTINATION bin) | ||||||
|  | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.conf" DESTINATION share/dbus-1/system.d ) | ||||||
|  | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.service" DESTINATION share/dbus-1/system-services) | ||||||
|  | install(FILES "gp.conf" DESTINATION /etc/gpservice) | ||||||
|  |  | ||||||
|  | if("$ENV{DEBIAN_PACKAGE}") | ||||||
|  |     # Install the systemd unit files to /lib/systemd/system for debian package | ||||||
|  |     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/systemd/gpservice.service" DESTINATION /lib/systemd/system) | ||||||
|  | else() | ||||||
|  |     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/systemd/gpservice.service" DESTINATION lib/systemd/system) | ||||||
|  | endif() | ||||||
| @@ -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,5 +1,5 @@ | |||||||
| [D-BUS Service] | [D-BUS Service] | ||||||
| Name=com.yuezk.qt.GPService | Name=com.yuezk.qt.GPService | ||||||
| Exec=/usr/bin/gpservice | Exec=@CMAKE_INSTALL_PREFIX@/bin/gpservice | ||||||
| User=root | User=root | ||||||
| SystemdService=gpservice.service | SystemdService=gpservice.service | ||||||
							
								
								
									
										17
									
								
								GPService/gp.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								GPService/gp.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | # Configuration file for GlobalProtect-openconnect | ||||||
|  | # | ||||||
|  | # Description: | ||||||
|  | # | ||||||
|  | # Each section is a VPN gateway address, and [*] is a special section that defines the default configuration. | ||||||
|  | # See https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration for more details. | ||||||
|  | # | ||||||
|  | # Example: | ||||||
|  | # | ||||||
|  | # [*] | ||||||
|  | # openconnect-args=<value> | ||||||
|  | # | ||||||
|  | # [vpn1.company.com] | ||||||
|  | # openconnect-args=--script=/path/to/vpnc-script | ||||||
|  |  | ||||||
|  | [*] | ||||||
|  | openconnect-args= | ||||||
| @@ -1,10 +1,13 @@ | |||||||
| #include "gpservice.h" | #include <QtCore/QFileInfo> | ||||||
| #include "gpservice_adaptor.h" | #include <QtCore/QDateTime> | ||||||
|  | #include <QtCore/QVariant> | ||||||
|  | #include <QtCore/QRegularExpression> | ||||||
|  | #include <QtCore/QRegularExpressionMatch> | ||||||
|  | #include <QtDBus/QtDBus> | ||||||
|  |  | ||||||
| #include <QFileInfo> | #include "INIReader.h" | ||||||
| #include <QtDBus> | #include "gpservice.h" | ||||||
| #include <QDateTime> | #include "gpserviceadaptor.h" | ||||||
| #include <QVariant> |  | ||||||
|  |  | ||||||
| GPService::GPService(QObject *parent) | GPService::GPService(QObject *parent) | ||||||
|     : QObject(parent) |     : QObject(parent) | ||||||
| @@ -31,14 +34,69 @@ GPService::~GPService() | |||||||
|  |  | ||||||
| QString GPService::findBinary() | QString GPService::findBinary() | ||||||
| { | { | ||||||
|     for (int i = 0; i < binaryPaths->length(); i++) { |     for (auto& binaryPath : binaryPaths) { | ||||||
|         if (QFileInfo::exists(binaryPaths[i])) { |         if (QFileInfo::exists(binaryPath)) { | ||||||
|             return binaryPaths[i]; |             return binaryPath; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QString GPService::extraOpenconnectArgs(const QString &gateway) | ||||||
|  | { | ||||||
|  |     INIReader reader("/etc/gpservice/gp.conf"); | ||||||
|  |  | ||||||
|  |     if (reader.ParseError() < 0) { | ||||||
|  |         return ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::string defaultArgs = reader.Get("*", "openconnect-args", ""); | ||||||
|  |     std::string extraArgs = reader.Get(gateway.toStdString(), "openconnect-args", defaultArgs); | ||||||
|  |  | ||||||
|  |     return QString::fromStdString(extraArgs); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Port from https://github.com/qt/qtbase/blob/11d1dcc6e263c5059f34b44d531c9ccdf7c0b1d6/src/corelib/io/qprocess.cpp#L2115 */ | ||||||
|  | QStringList GPService::splitCommand(const QString &command) | ||||||
|  | { | ||||||
|  |     QStringList args; | ||||||
|  |     QString tmp; | ||||||
|  |     int quoteCount = 0; | ||||||
|  |     bool inQuote = false; | ||||||
|  |  | ||||||
|  |     // handle quoting. tokens can be surrounded by double quotes | ||||||
|  |     // "hello world". three consecutive double quotes represent | ||||||
|  |     // the quote character itself. | ||||||
|  |     for (int i = 0; i < command.size(); ++i) { | ||||||
|  |         if (command.at(i) == QLatin1Char('"')) { | ||||||
|  |             ++quoteCount; | ||||||
|  |             if (quoteCount == 3) { | ||||||
|  |                 // third consecutive quote | ||||||
|  |                 quoteCount = 0; | ||||||
|  |                 tmp += command.at(i); | ||||||
|  |             } | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (quoteCount) { | ||||||
|  |             if (quoteCount == 1) | ||||||
|  |                 inQuote = !inQuote; | ||||||
|  |             quoteCount = 0; | ||||||
|  |         } | ||||||
|  |         if (!inQuote && command.at(i).isSpace()) { | ||||||
|  |             if (!tmp.isEmpty()) { | ||||||
|  |                 args += tmp; | ||||||
|  |                 tmp.clear(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             tmp += command.at(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (!tmp.isEmpty()) | ||||||
|  |         args += tmp; | ||||||
|  |  | ||||||
|  |     return args; | ||||||
|  | } | ||||||
|  |  | ||||||
| void GPService::quit() | void GPService::quit() | ||||||
| { | { | ||||||
|     if (openconnect->state() == QProcess::NotRunning) { |     if (openconnect->state() == QProcess::NotRunning) { | ||||||
| @@ -58,18 +116,56 @@ void GPService::connect(QString server, QString username, QString passwd) | |||||||
|  |  | ||||||
|     QString bin = findBinary(); |     QString bin = findBinary(); | ||||||
|     if (bin == nullptr) { |     if (bin == nullptr) { | ||||||
|         log("Could not found openconnect binary, make sure openconnect is installed, exiting."); |         log("Could not find openconnect binary, make sure openconnect is installed, exiting."); | ||||||
|  |         emit error("The OpenConect CLI was not found, make sure it has been installed!"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!isValidVersion(bin)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const QString extraArgs = extraOpenconnectArgs(server); | ||||||
|  |     log(QString("Got extra OpenConnect args for server: %1, %2").arg(server, extraArgs.isEmpty() ? "<empty>" : extraArgs)); | ||||||
|  |  | ||||||
|     QStringList args; |     QStringList args; | ||||||
|     args << QCoreApplication::arguments().mid(1) |     args << QCoreApplication::arguments().mid(1) | ||||||
|      << "--protocol=gp" |          << "--protocol=gp" | ||||||
|      << "-u" << username |          << splitCommand(extraArgs) | ||||||
|      << "-C" << passwd |          << "-u" << username | ||||||
|      << server; |          << "--cookie-on-stdin" | ||||||
|  |          << server; | ||||||
|  |  | ||||||
|  |     log("Start process with arguments: " + args.join(", ")); | ||||||
|  |  | ||||||
|     openconnect->start(bin, args); |     openconnect->start(bin, args); | ||||||
|  |     openconnect->write((passwd + "\n").toUtf8()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool GPService::isValidVersion(QString &bin) { | ||||||
|  |     QProcess p; | ||||||
|  |     p.start(bin, QStringList("--version")); | ||||||
|  |     p.waitForFinished(); | ||||||
|  |     QString output = p.readAllStandardError() + p.readAllStandardOutput(); | ||||||
|  |  | ||||||
|  |     QRegularExpression re("v(\\d+).*?(\\s|\\n)"); | ||||||
|  |     QRegularExpressionMatch match = re.match(output); | ||||||
|  |  | ||||||
|  |     if (match.hasMatch()) { | ||||||
|  |         log("Output of `openconnect --version`: " + output); | ||||||
|  |  | ||||||
|  |         QString fullVersion = match.captured(0); | ||||||
|  |         QString majorVersion = match.captured(1); | ||||||
|  |  | ||||||
|  |         if (majorVersion.toInt() < 8) { | ||||||
|  |             emit error("The OpenConnect version must greater than v8.0.0, got " + fullVersion); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         log("Failed to parse the OpenConnect version from " + output); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void GPService::disconnect() | void GPService::disconnect() | ||||||
| @@ -103,7 +199,9 @@ void GPService::onProcessStdout() | |||||||
|     QString output = openconnect->readAllStandardOutput(); |     QString output = openconnect->readAllStandardOutput(); | ||||||
|  |  | ||||||
|     log(output); |     log(output); | ||||||
|     if (output.indexOf("Connected as") >= 0) { |     if (output.indexOf("Connected as") >= 0 || | ||||||
|  |         output.indexOf("Configured as") >= 0 || | ||||||
|  |         output.indexOf("Configurado como") >= 0) { | ||||||
|         vpnStatus = GPService::VpnConnected; |         vpnStatus = GPService::VpnConnected; | ||||||
|         emit connected(); |         emit connected(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,17 +1,16 @@ | |||||||
| #ifndef GLOBALPROTECTSERVICE_H | #ifndef GLOBALPROTECTSERVICE_H | ||||||
| #define GLOBALPROTECTSERVICE_H | #define GLOBALPROTECTSERVICE_H | ||||||
|  |  | ||||||
| #include <QObject> | #include <QtCore/QObject> | ||||||
| #include <QProcess> | #include <QtCore/QProcess> | ||||||
|  |  | ||||||
| static const QString binaryPaths[] { | static QList<QString> binaryPaths = QList<QString>() << | ||||||
|     "/usr/local/bin/openconnect", |     "/usr/local/bin/openconnect" << | ||||||
|     "/usr/local/sbin/openconnect", |      "/usr/local/sbin/openconnect" << | ||||||
|     "/usr/bin/openconnect", |      "/usr/bin/openconnect" << | ||||||
|     "/usr/sbin/openconnect", |      "/usr/sbin/openconnect" << | ||||||
|     "/opt/bin/openconnect", |      "/opt/bin/openconnect" << | ||||||
|     "/opt/sbin/openconnect" |      "/opt/sbin/openconnect"; | ||||||
| }; |  | ||||||
|  |  | ||||||
| class GPService : public QObject | class GPService : public QObject | ||||||
| { | { | ||||||
| @@ -21,6 +20,8 @@ public: | |||||||
|     explicit GPService(QObject *parent = nullptr); |     explicit GPService(QObject *parent = nullptr); | ||||||
|     ~GPService(); |     ~GPService(); | ||||||
|  |  | ||||||
|  |     void quit(); | ||||||
|  |  | ||||||
|     enum VpnStatus { |     enum VpnStatus { | ||||||
|         VpnNotConnected, |         VpnNotConnected, | ||||||
|         VpnConnecting, |         VpnConnecting, | ||||||
| @@ -31,13 +32,13 @@ public: | |||||||
| signals: | signals: | ||||||
|     void connected(); |     void connected(); | ||||||
|     void disconnected(); |     void disconnected(); | ||||||
|  |     void error(QString errorMessage); | ||||||
|     void logAvailable(QString log); |     void logAvailable(QString log); | ||||||
|  |  | ||||||
| public slots: | public slots: | ||||||
|     void connect(QString server, QString username, QString passwd); |     void connect(QString server, QString username, QString passwd); | ||||||
|     void disconnect(); |     void disconnect(); | ||||||
|     int status(); |     int status(); | ||||||
|     void quit(); |  | ||||||
|  |  | ||||||
| private slots: | private slots: | ||||||
|     void onProcessStarted(); |     void onProcessStarted(); | ||||||
| @@ -52,7 +53,10 @@ private: | |||||||
|     int vpnStatus = GPService::VpnNotConnected; |     int vpnStatus = GPService::VpnNotConnected; | ||||||
|  |  | ||||||
|     void log(QString msg); |     void log(QString msg); | ||||||
|  |     bool isValidVersion(QString &bin); | ||||||
|     static QString findBinary(); |     static QString findBinary(); | ||||||
|  |     static QString extraOpenconnectArgs(const QString &gateway); | ||||||
|  |     static QStringList splitCommand(const QString &command); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // GLOBALPROTECTSERVICE_H | #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,4 +1,5 @@ | |||||||
| #include <QtDBus> | #include <QtDBus/QtDBus> | ||||||
|  |  | ||||||
| #include "gpservice.h" | #include "gpservice.h" | ||||||
| #include "singleapplication.h" | #include "singleapplication.h" | ||||||
| #include "sigwatch.h" | #include "sigwatch.h" | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ Description=GlobalProtect openconnect DBus service | |||||||
| Environment="LANG=en_US.utf8" | Environment="LANG=en_US.utf8" | ||||||
| Type=dbus | Type=dbus | ||||||
| BusName=com.yuezk.qt.GPService | BusName=com.yuezk.qt.GPService | ||||||
| ExecStart=/usr/bin/gpservice | ExecStart=@CMAKE_INSTALL_PREFIX@/bin/gpservice | ||||||
| 
 | 
 | ||||||
| [Install] | [Install] | ||||||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| TEMPLATE = subdirs |  | ||||||
|  |  | ||||||
| SUBDIRS += \ |  | ||||||
|     GPClient \ |  | ||||||
|     GPService |  | ||||||
							
								
								
									
										207
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,9 +2,14 @@ | |||||||
| 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). | 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 src="https://user-images.githubusercontent.com/3297602/133869036-5c02b0d9-c2d9-4f87-8c81-e44f68cfd6ac.png"> | ||||||
| </p> | </p> | ||||||
|  |  | ||||||
|  | <a href="https://paypal.me/zongkun" target="_blank"><img src="https://cdn.jsdelivr.net/gh/everdrone/coolbadge@5ea5937cabca5ecbfc45d6b30592bd81f219bc8d/badges/Paypal/Coffee/Blue/Small.png" alt="Buy me a coffee via Paypal" style="height: 32px; width: 268px;" ></a> | ||||||
|  | <a href="https://ko-fi.com/M4M75PYKZ" target="_blank"><img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support me on Ko-fi" style="height: 32px; width: 238px;"></a> | ||||||
|  | <a href="https://www.buymeacoffee.com/yuezk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 32px; width: 114px;" ></a> | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|  |  | ||||||
| - Similar user experience as the official client in macOS. | - Similar user experience as the official client in macOS. | ||||||
| @@ -12,80 +17,178 @@ A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Q | |||||||
| - Supports automatically selecting the preferred gateway from the multiple gateways. | - Supports automatically selecting the preferred gateway from the multiple gateways. | ||||||
| - Supports switching gateway from the system tray menu manually. | - Supports switching gateway from the system tray menu manually. | ||||||
|  |  | ||||||
| ## Prerequisites |  | ||||||
|  |  | ||||||
| - Openconnect v8.x |  | ||||||
| - Qt5, qt5-webengine, qt5-websockets |  | ||||||
|  |  | ||||||
| ### Ubuntu |  | ||||||
| 1. Install openconnect v8.x |  | ||||||
|  |  | ||||||
|    For Ubuntu 18.04 you might need to [build the latest openconnect from source code](https://gist.github.com/yuezk/ab9a4b87a9fa0182bdb2df41fab5f613). |  | ||||||
|     |  | ||||||
| 2. Install the Qt dependencies |  | ||||||
|     ```sh |  | ||||||
|     sudo apt install qt5-default libqt5websockets5-dev qtwebengine5-dev |  | ||||||
|     ``` |  | ||||||
| ### OpenSUSE |  | ||||||
| Install the Qt dependencies |  | ||||||
|  |  | ||||||
| ```sh |  | ||||||
| sudo zypper install libqt5-qtbase-devel libqt5-qtwebsockets-devel libqt5-qtwebengine-devel |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Install | ## Install | ||||||
|  |  | ||||||
| ### Install from AUR (Arch/Manjaro) | |OS|Stable version | Development version| | ||||||
|  | |---|--------------|--------------------| | ||||||
|  | |Linux Mint, Ubuntu 18.04 or later|[ppa:yuezk/globalprotect-openconnect](https://launchpad.net/~yuezk/+archive/ubuntu/globalprotect-openconnect)|[ppa:yuezk/globalprotect-openconnect-snapshot](https://launchpad.net/~yuezk/+archive/ubuntu/globalprotect-openconnect-snapshot)| | ||||||
|  | |Arch, Manjaro|[globalprotect-openconnect](https://archlinux.org/packages/community/x86_64/globalprotect-openconnect/)|[AUR: globalprotect-openconnect-git](https://aur.archlinux.org/packages/globalprotect-openconnect-git/)| | ||||||
|  | |Fedora|[copr: yuezk/globalprotect-openconnect](https://copr.fedorainfracloud.org/coprs/yuezk/globalprotect-openconnect/)|[copr: yuezk/globalprotect-openconnect](https://copr.fedorainfracloud.org/coprs/yuezk/globalprotect-openconnect/)| | ||||||
|  | |openSUSE, CentOS 8|[OBS: globalprotect-openconnect](https://build.opensuse.org/package/show/home:yuezk/globalprotect-openconnect)|[OBS: globalprotect-openconnect-snapshot](https://build.opensuse.org/package/show/home:yuezk/globalprotect-openconnect-snapshot)| | ||||||
|  |  | ||||||
| Install [globalprotect-openconnect](https://aur.archlinux.org/packages/globalprotect-openconnect/). | Add the repository in the above table and install it with your favorite package manager tool. | ||||||
|  |  | ||||||
| ### Build from source code | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  | [](https://repology.org/project/globalprotect-openconnect/versions) | ||||||
|  |  | ||||||
|  | ### Linux Mint, Ubuntu 18.04 or later | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | sudo add-apt-repository ppa:yuezk/globalprotect-openconnect | ||||||
|  | sudo apt-get update | ||||||
|  | sudo apt-get install globalprotect-openconnect | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | > For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`. | ||||||
|  |  | ||||||
|  | ### Arch Linux / Manjaro | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | sudo pacman -S globalprotect-openconnect | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### AUR snapshot version | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | yay -S globalprotect-openconnect-git | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Fedora | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | sudo dnf copr enable yuezk/globalprotect-openconnect | ||||||
|  | sudo dnf install globalprotect-openconnect | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### openSUSE | ||||||
|  |  | ||||||
|  | - openSUSE Tumbleweed | ||||||
|  |   ```sh | ||||||
|  |   sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/openSUSE_Tumbleweed/home:yuezk.repo | ||||||
|  |   sudo zypper ref | ||||||
|  |   sudo zypper install globalprotect-openconnect | ||||||
|  |   ``` | ||||||
|  |  | ||||||
|  | - openSUSE Leap | ||||||
|  |  | ||||||
|  |   ```sh   | ||||||
|  |   sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/15.4/home:yuezk.repo | ||||||
|  |    | ||||||
|  |   sudo zypper ref | ||||||
|  |   sudo zypper install globalprotect-openconnect | ||||||
|  |   ``` | ||||||
|  | ### CentOS 8 | ||||||
|  |  | ||||||
|  | 1. Add the repository: `https://download.opensuse.org/repositories/home:/yuezk/CentOS_8/home:yuezk.repo` | ||||||
|  | 1. Install `globalprotect-openconnect` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Build & Install from source code | ||||||
|  |  | ||||||
|  | Clone this repo with: | ||||||
|  |  | ||||||
| ```sh | ```sh | ||||||
| git clone https://github.com/yuezk/GlobalProtect-openconnect.git | git clone https://github.com/yuezk/GlobalProtect-openconnect.git | ||||||
| cd GlobalProtect-openconnect | 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 | ### MX Linux | ||||||
|  | The following instructions are for **MX-21.2.1_x64 KDE**. | ||||||
|  |  | ||||||
| Relatively manual process for now: | ```sh | ||||||
|  | sudo apt install qttools5-dev libsecret-1-dev libqt5keychain1 | ||||||
|  | ./scripts/install-debian.sh | ||||||
|  | ``` | ||||||
|  |  | ||||||
| * Clone the source tree | ### Ubuntu/Mint | ||||||
|  |  | ||||||
|  | > **⚠️ REQUIRED for Ubuntu 18.04 ⚠️** | ||||||
|  | > | ||||||
|  | > Add this [dwmw2/openconnect](https://launchpad.net/~dwmw2/+archive/ubuntu/openconnect) PPA first to install the latest openconnect. | ||||||
|  | > | ||||||
|  | > ```sh | ||||||
|  | > sudo add-apt-repository ppa:dwmw2/openconnect | ||||||
|  | > sudo apt-get update | ||||||
|  | > ``` | ||||||
|  |  | ||||||
|  | Build and install with: | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | ./scripts/install-ubuntu.sh | ||||||
|  | ``` | ||||||
|  | ### openSUSE | ||||||
|  |  | ||||||
|  | Build and install with: | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | ./scripts/install-opensuse.sh | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Fedora | ||||||
|  |  | ||||||
|  | Build and install with: | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | ./scripts/install-fedora.sh | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Other Linux | ||||||
|  |  | ||||||
|  | Install the Qt5 dependencies and OpenConnect: | ||||||
|  |  | ||||||
|  | - QtCore | ||||||
|  | - QtWebEngine | ||||||
|  | - QtWebSockets | ||||||
|  | - QtDBus | ||||||
|  | - openconnect v8.x | ||||||
|  | - qtkeychain | ||||||
|  |  | ||||||
|  | ...then build and install with: | ||||||
|  |  | ||||||
|  | ```sh | ||||||
|  | ./scripts/install.sh | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### NixOS | ||||||
|  |   In `configuration.nix`: | ||||||
|  |  | ||||||
|   ``` |   ``` | ||||||
|   git clone https://github.com/yuezk/GlobalProtect-openconnect.git |   services.globalprotect = { | ||||||
|   cd GlobalProtect-openconnect |     enable = true; | ||||||
|  |     # if you need a Host Integrity Protection report | ||||||
|  |     csdWrapper = "${pkgs.openconnect}/libexec/openconnect/hipreport.sh"; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   environment.systemPackages = [ globalprotect-openconnect ]; | ||||||
|   ``` |   ``` | ||||||
|  |  | ||||||
| * Install git-archive-all using the pip. Remember to adjust the version numbers etc. | ## Run | ||||||
|  |  | ||||||
|   ``` | Once the software is installed, you can run `gpclient` to start the UI. | ||||||
|   pip install git-archive-all |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| * Next create an upstream source tree using git archive. | ## Passing the Custom Parameters to `OpenConnect` CLI | ||||||
|  |  | ||||||
|   ``` | See [Configuration](https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration) | ||||||
|   git-archive-all --force-submodules --prefix=globalprotect-openconnect-1.3.0/ ../globalprotect-openconnect_1.3.0.orig.tar.gz |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| * Finally extract the source tree and build the debian package. | ## Display the system tray icon on Gnome 40 | ||||||
|  |  | ||||||
|   ``` | Install the [AppIndicator and KStatusNotifierItem Support](https://extensions.gnome.org/extension/615/appindicator-support/) extension and you will see the system try icon (Restart the system after the installation). | ||||||
|   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 |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
|    | <p align="center"> | ||||||
|  |   <img src="https://user-images.githubusercontent.com/3297602/130831022-b93492fd-46dd-4a8e-94a4-13b5747120b7.png" /> | ||||||
|  | <p> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Troubleshooting | ||||||
|  |  | ||||||
|  | Run `gpclient` in the Terminal and collect the logs. | ||||||
|  |  | ||||||
| ## [License](./LICENSE) | ## [License](./LICENSE) | ||||||
| GPLv3 | GPLv3 | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								cmake/Add3rdParty.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								cmake/Add3rdParty.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | include(ExternalProject) | ||||||
|  |  | ||||||
|  | function(add_3rdparty NAME) | ||||||
|  |     set(oneValueArgs GIT_REPOSITORY GIT_TAG) | ||||||
|  |     cmake_parse_arguments(add_3rdparty_args "" "${oneValueArgs}" "" ${ARGN}) | ||||||
|  |  | ||||||
|  |     if(EXISTS "${CMAKE_SOURCE_DIR}/3rdparty/${NAME}/CMakeLists.txt") | ||||||
|  |         message(STATUS "Found third party locally for ${NAME}") | ||||||
|  |  | ||||||
|  |         ExternalProject_Add( | ||||||
|  |             ${NAME}-${PROJECT_NAME} | ||||||
|  |             PREFIX ${CMAKE_CURRENT_BINARY_DIR}/${NAME} | ||||||
|  |             SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/${NAME}" | ||||||
|  |             INSTALL_COMMAND "" | ||||||
|  |             "${add_3rdparty_args_UNPARSED_ARGUMENTS}" | ||||||
|  |         ) | ||||||
|  |         return() | ||||||
|  |     endif() | ||||||
|  |  | ||||||
|  |     message(STATUS "Using ExternalProject to download ${NAME}")  | ||||||
|  |     ExternalProject_Add( | ||||||
|  |         ${NAME}-${PROJECT_NAME} | ||||||
|  |         PREFIX ${CMAKE_CURRENT_BINARY_DIR}/${NAME} | ||||||
|  |         INSTALL_COMMAND "" | ||||||
|  |         "${ARGN}" | ||||||
|  |     ) | ||||||
|  | endfunction() | ||||||
							
								
								
									
										59
									
								
								cmake/FindNetworkManager.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								cmake/FindNetworkManager.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | # - Try to find NetworkManager | ||||||
|  | # Once done this will define | ||||||
|  | # | ||||||
|  | #  NETWORKMANAGER_FOUND - system has NetworkManager | ||||||
|  | #  NETWORKMANAGER_INCLUDE_DIRS - the NetworkManager include directories | ||||||
|  | #  NETWORKMANAGER_LIBRARIES - the libraries needed to use NetworkManager | ||||||
|  | #  NETWORKMANAGER_CFLAGS - Compiler switches required for using NetworkManager | ||||||
|  | #  NETWORKMANAGER_VERSION - version number of NetworkManager | ||||||
|  |  | ||||||
|  | # Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org> | ||||||
|  | # Copyright (c) 2007, Will Stephenson, <wstephenson@kde.org> | ||||||
|  | # Copyright (c) 2015-2018, Jan Grulich, <jgrulich@redhat.com> | ||||||
|  |  | ||||||
|  | # Redistribution and use in source and binary forms, with or without | ||||||
|  | # modification, are permitted provided that the following conditions | ||||||
|  | # are met: | ||||||
|  | # 1. Redistributions of source code must retain the above copyright | ||||||
|  | #    notice, this list of conditions and the following disclaimer. | ||||||
|  | # 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  | #    notice, this list of conditions and the following disclaimer in the | ||||||
|  | #    documentation and/or other materials provided with the distribution. | ||||||
|  | # 3. Neither the name of the University nor the names of its contributors | ||||||
|  | #    may be used to endorse or promote products derived from this software | ||||||
|  | #    without specific prior written permission. | ||||||
|  | # | ||||||
|  | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  | # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||||
|  | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  | # SUCH DAMAGE. | ||||||
|  |  | ||||||
|  | IF (NETWORKMANAGER_INCLUDE_DIRS) | ||||||
|  |     # in cache already | ||||||
|  |     SET(NetworkManager_FIND_QUIETLY TRUE) | ||||||
|  | ENDIF (NETWORKMANAGER_INCLUDE_DIRS) | ||||||
|  |  | ||||||
|  | IF (NOT WIN32) | ||||||
|  |     find_package(PkgConfig) | ||||||
|  |     PKG_SEARCH_MODULE(NETWORKMANAGER libnm) | ||||||
|  |     IF (NETWORKMANAGER_FOUND) | ||||||
|  |         IF (NetworkManager_FIND_VERSION AND ("${NETWORKMANAGER_VERSION}" VERSION_LESS "${NetworkManager_FIND_VERSION}")) | ||||||
|  |             MESSAGE(FATAL_ERROR "NetworkManager ${NETWORKMANAGER_VERSION} is too old, need at least ${NetworkManager_FIND_VERSION}") | ||||||
|  |         ELSE () | ||||||
|  |             IF (NOT NetworkManager_FIND_QUIETLY) | ||||||
|  |                 MESSAGE(STATUS "Found NetworkManager: ${NETWORKMANAGER_LIBRARY_DIRS}") | ||||||
|  |             ENDIF () | ||||||
|  |         ENDIF () | ||||||
|  |     ELSE () | ||||||
|  |         MESSAGE(FATAL_ERROR "Could NOT find NetworkManager, check FindPkgConfig output above!") | ||||||
|  |     ENDIF () | ||||||
|  | ENDIF (NOT WIN32) | ||||||
|  |  | ||||||
|  | MARK_AS_ADVANCED(NETWORKMANAGER_INCLUDE_DIRS) | ||||||
							
								
								
									
										102
									
								
								cmakew
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										102
									
								
								cmakew
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | #!/bin/bash | ||||||
|  |  | ||||||
|  | cmake_version="3.21.2" | ||||||
|  |  | ||||||
|  | arr_cmake_v=(${cmake_version//./ }) | ||||||
|  | cmake_version_major=(${arr_cmake_v[0]}) | ||||||
|  | cmake_version_minor=(${arr_cmake_v[1]}) | ||||||
|  | cmake_version_patch=(${arr_cmake_v[2]}) | ||||||
|  |  | ||||||
|  | # OS specific support (must be 'true' or 'false'). | ||||||
|  | cygwin=false | ||||||
|  | msys=false | ||||||
|  | darwin=false | ||||||
|  | nonstop=false | ||||||
|  |  | ||||||
|  | case "`uname`" in | ||||||
|  |     CYGWIN* ) | ||||||
|  |         cygwin=true | ||||||
|  |         ;; | ||||||
|  |     Darwin* ) | ||||||
|  |         darwin=true | ||||||
|  |         ;; | ||||||
|  |     MINGW* ) | ||||||
|  |         msys=true | ||||||
|  |         ;; | ||||||
|  |     NONSTOP* ) | ||||||
|  |         nonstop=true | ||||||
|  |         ;; | ||||||
|  | esac | ||||||
|  |  | ||||||
|  | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong | ||||||
|  | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then | ||||||
|  |     cd "$(dirname "$0")" | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | cmake_base="./.cmake" | ||||||
|  | cmake_bin="${cmake_base}/cmake-$cmake_version/bin/cmake" | ||||||
|  |  | ||||||
|  | # download cmake if necessary | ||||||
|  | if [ ! -f "$cmake_bin" ]; then | ||||||
|  |     download_link="" | ||||||
|  |  | ||||||
|  |     if [ "$darwin" = true ]; then  | ||||||
|  |         download_link="https://cmake.org/files/v$cmake_version_major.$cmake_version_minor/cmake-$cmake_version-Darwin-x86_64.tar.gz"         | ||||||
|  |     else | ||||||
|  |         download_link="https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-linux-x86_64.tar.gz" | ||||||
|  |     fi  | ||||||
|  |  | ||||||
|  |     wget -nv --show-progress "$download_link" -O "/tmp/cmake-$cmake_version.tar.gz"   | ||||||
|  |     mkdir -p "${cmake_base}/cmake-$cmake_version"       | ||||||
|  |     tar -xzf "/tmp/cmake-$cmake_version.tar.gz" -C "${cmake_base}/cmake-$cmake_version" --strip-components=1 | ||||||
|  |     rm "/tmp/cmake-$cmake_version.tar.gz"  | ||||||
|  | fi  | ||||||
|  |  | ||||||
|  | # We build the pattern for arguments to be converted via cygpath | ||||||
|  | if [ "$cygwin" = true ]; then | ||||||
|  |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | ||||||
|  |     SEP="" | ||||||
|  |  | ||||||
|  |     for dir in $ROOTDIRSRAW ; do | ||||||
|  |         ROOTDIRS="$ROOTDIRS$SEP$dir" | ||||||
|  |         SEP="|" | ||||||
|  |     done | ||||||
|  |  | ||||||
|  |     OURCYGPATTERN="(^($ROOTDIRS))" | ||||||
|  |  | ||||||
|  |     # Add a user-defined pattern to the cygpath arguments | ||||||
|  |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then | ||||||
|  |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | ||||||
|  |     fi | ||||||
|  |  | ||||||
|  |     # Now convert the arguments - kludge to limit ourselves to /bin/sh | ||||||
|  |     i=0 | ||||||
|  |     for arg in "$@" ; do | ||||||
|  |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | ||||||
|  |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option | ||||||
|  |  | ||||||
|  |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition | ||||||
|  |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | ||||||
|  |         else | ||||||
|  |             eval `echo args$i`="\"$arg\"" | ||||||
|  |         fi | ||||||
|  |  | ||||||
|  |         i=$((i+1)) | ||||||
|  |     done | ||||||
|  |  | ||||||
|  |     case $i in | ||||||
|  |         (0) set -- ;; | ||||||
|  |         (1) set -- "$args0" ;; | ||||||
|  |         (2) set -- "$args0" "$args1" ;; | ||||||
|  |         (3) set -- "$args0" "$args1" "$args2" ;; | ||||||
|  |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;; | ||||||
|  |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | ||||||
|  |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | ||||||
|  |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | ||||||
|  |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | ||||||
|  |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | ||||||
|  |     esac | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # run cmake | ||||||
|  | exec "$cmake_bin" "$@" | ||||||
							
								
								
									
										193
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										193
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,47 +1,154 @@ | |||||||
| globalprotect-openconnect (1.3.0-1ppa1) bionic; urgency=medium | globalprotect-openconnect (1.4.9-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.8 –> 1.4.9 | ||||||
|  |   * fix: update cmake version | ||||||
|  |   * fix: correct the package name | ||||||
|  |   * fix: use the dev package | ||||||
|  |   * fix: use qtkeychain package | ||||||
|  |   * fix: add qt5-tools | ||||||
|  |   * fix: add libsecret-1-dev | ||||||
|  |   * fix: add pkg-config | ||||||
|  |   * fix: use cmake 3.16 | ||||||
|  |   * fix: add missing build dependency | ||||||
|  |   * ci: fix CI | ||||||
|  |   * Merge branch 'master' into develop | ||||||
|  |   * feat: expose os-version to settings | ||||||
|  |   * Add two missing dependencies for building on debian (#198) | ||||||
|  |   * ci: assert no library missing | ||||||
|  |   * fix: update qtkeychain | ||||||
|  |   * ci: run gpclient after build | ||||||
|  |   * fix: add qtkeychain | ||||||
|  |   * chore: update CMake file | ||||||
|  |   * Added install instructions for MX Linux. (#190) | ||||||
|  |   * Credentials autocompleting (secure version) (#179) | ||||||
|  |   * Read all saved Gateways (for selecting in Systray) (#181) | ||||||
|  |   * copy install script for debian (#180) | ||||||
|  |   * add es and pt support to shange status when connected to vpn (#162) | ||||||
|  |   * fix: improve the cli support | ||||||
|  |   * feat: add --reset option to gpclient | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 08 Jan 2023 20:58:32 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.8-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.7 –> 1.4.8 | ||||||
|  |   * fix: fix compile error | ||||||
|  |   * refactor: simplify the code | ||||||
|  |   * chore: use auto to declare variables | ||||||
|  |   * chore: use c++ 17 | ||||||
|  |   * fix: clear cookies when click the Reset button | ||||||
|  |   * fix: refine the authentication workflow | ||||||
|  |   * chore: PLOG -> LOG | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 12 Jun 2022 20:28:58 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.7-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.6 –> 1.4.7 | ||||||
|  |   * fix: release resources when properly | ||||||
|  |   * fix: add support for parsing tokens from HTML | ||||||
|  |   * handle html comment for saml result with okta 2fa (#156) | ||||||
|  |   * chore: use auto to declare variable | ||||||
|  |   * chore: simplify readme | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Tue, 07 Jun 2022 21:46:04 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.6-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.5 –> 1.4.6 | ||||||
|  |   * feat: display address in gateway menu item | ||||||
|  |   * fix: fix bug of parsing the portal response | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Wed, 01 Jun 2022 23:55:50 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.5-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.4 –> 1.4.5 | ||||||
|  |   * chore: refine vscode settings | ||||||
|  |   * fix: rollback dbus configuration | ||||||
|  |   * feat: add option to start minimized | ||||||
|  |   * packaging: fix postinst for debian | ||||||
|  |   * packaging: add postinst for debian | ||||||
|  |   * test: test debian packaging | ||||||
|  |   * ci: fix the folder path | ||||||
|  |   * chore: apt -> apt-get | ||||||
|  |   * ci: verify debian package | ||||||
|  |   * Revert "Revert "fix: improve the dbus security"" | ||||||
|  |   * fix: improve the portal config parsing | ||||||
|  |   * Revert "fix: improve the dbus security" | ||||||
|  |   * fix: improve the dbus security | ||||||
|  |   * fix: free resources in slots | ||||||
|  |   * chore: refine cmake files | ||||||
|  |   * fix: support high DPI screen | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 29 May 2022 21:15:40 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.4-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.3 –> 1.4.4 | ||||||
|  |   * fix: support the HighDPI displays | ||||||
|  |   * [misc] update the build script | ||||||
|  |   * [ci] Enable build job for master branch | ||||||
|  |   * [ci] Add ubuntu 22.04 | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Sat, 14 May 2022 19:21:14 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.3-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.2 –> 1.4.3 | ||||||
|  |   * refine AUR packaging | ||||||
|  |   * Prepare release 1.4.3 (#149) | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Mon, 09 May 2022 22:20:54 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.2-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.1 –> 1.4.2 | ||||||
|  |   * Clear SSL_OP_LEGACY_SERVER_CONNECT (#146) | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Fri, 06 May 2022 22:18:19 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.1-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.4.0 –> 1.4.1 | ||||||
|  |   * print the gpservice logs | ||||||
|  |   * update AUR packaging | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Thu, 03 Mar 2022 21:58:59 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.4.0-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.3.4 –> 1.4.0 | ||||||
|  |   * Fix gpservice after openconnect v8.20 (#124) | ||||||
|  |   * Add 2FA support (#112) | ||||||
|  |   * Add a scripting mode to GPClient (#110) | ||||||
|  |   * Stop saving credentials (#111) | ||||||
|  |   * update CI | ||||||
|  |   * add editorconfig | ||||||
|  |   * Update README.md | ||||||
|  |   * Add a run entry (#108) | ||||||
|  |   * update the installation instruction of Arch Linux | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Wed, 02 Mar 2022 21:34:19 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.3.4-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * Updated VERSION, Bumped 1.3.3 –> 1.3.4 | ||||||
|  |   * update packaging (#100) | ||||||
|  |   * shorten the sponsor links | ||||||
|  |   * Update README.md | ||||||
|  |   * add sponsor links | ||||||
|  |   * Adding application logs location in the README (#95) | ||||||
|  |   * improve the doc | ||||||
|  |   * Add snap packaging (#93) | ||||||
|  |   * update doc | ||||||
|  |   * Migrate to cmake and refine the code structure (#92) | ||||||
|  |   * QStringView -> QString | ||||||
|  |  | ||||||
|  |  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 24 Oct 2021 12:13:24 +0800 | ||||||
|  |  | ||||||
|  | globalprotect-openconnect (1.3.0-1) unstable; urgency=medium | ||||||
|  |  | ||||||
|   * Bump version to 1.3.0 |   * Bump version to 1.3.0 | ||||||
|  |  | ||||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Thu, 09 Jul 2020 10:13:46 +0800 |  -- 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 |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -2,12 +2,12 @@ Source: globalprotect-openconnect | |||||||
| Section: net | Section: net | ||||||
| Priority: optional | Priority: optional | ||||||
| Maintainer: Kevin Yue <k3vinyue@gmail.com> | Maintainer: Kevin Yue <k3vinyue@gmail.com> | ||||||
| Build-Depends: debhelper (>=11~), qt5-default (>=5.9), qttools5-dev (>=5.9), libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9) | Build-Depends: cmake (>=3.10), pkg-config, debhelper (>=11~), qtbase5-dev, qttools5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9), qt5keychain-dev | ||||||
| Standards-Version: 4.1.4 | Standards-Version: 4.1.4 | ||||||
| Homepage: https://github.com/yuezk/GlobalProtect-openconnect | Homepage: https://github.com/yuezk/GlobalProtect-openconnect | ||||||
|  |  | ||||||
| Package: globalprotect-openconnect | Package: globalprotect-openconnect | ||||||
| Architecture: any | Architecture: any | ||||||
| Multi-Arch: foreign | Multi-Arch: foreign | ||||||
| Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9) | Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9), libqt5keychain1 | ||||||
| Description: A GlobalProtect VPN client (GUI) based on OpenConnect. | Description: A GlobalProtect VPN client (GUI) based on OpenConnect. | ||||||
|   | |||||||
							
								
								
									
										989
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										989
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -1,982 +1,15 @@ | |||||||
| Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | Files: * | ||||||
| Upstream-Name: globalprotect-openconnect | Copyright: 1975-present Kevin Yue | ||||||
| Source: https://github.com/yuezk/GlobalProtect-openconnect | License: GPL-3+ | ||||||
| # |  | ||||||
| # Please double check copyright with the licensecheck(1) command. |  | ||||||
|  |  | ||||||
| Files:     .gitignore | Files: 3rdparty/plog | ||||||
|            .gitmodules | Copyright: 2016 Sergey Podobry (sergey.podobry at gmail.com) | ||||||
|            GPClient/GPClient.pro | License: MPL-2.0 | ||||||
|            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 | Files: 3rdparty/qt-unix-signals | ||||||
|            GPService/sigwatch.h |  | ||||||
|            singleapplication/singleapplication.cpp |  | ||||||
|            singleapplication/singleapplication.h |  | ||||||
| Copyright: 2014 Simon Knopp | Copyright: 2014 Simon Knopp | ||||||
|            2015-2018 Itay Grudev | License: MIT | ||||||
| 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 | Files: 3rdparty/SingleApplication | ||||||
|            singleapplication/singleapplication_p.h | Copyright: Itay Grudev 2015 - 2020 | ||||||
| Copyright: 2015-2018 Itay Grudev | License: MIT | ||||||
| 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>. |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -5,11 +5,9 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all | |||||||
| export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic | export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic | ||||||
| export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed | ||||||
|  |  | ||||||
|  | export DEBIAN_PACKAGE=1 | ||||||
|  |  | ||||||
| %: | %: | ||||||
| 	dh $@   | 	dh $@ | ||||||
|  | override_dh_installsystemd: | ||||||
| override_dh_auto_install: | 	dh_installsystemd gpservice.service | ||||||
| 	dh_auto_install -- prefix=/usr |  | ||||||
|  |  | ||||||
| #override_dh_install: |  | ||||||
| #	dh_install --list-missing -X.pyc -X.pyo |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | |||||||
| 3.0 (quilt) | 3.0 (native) | ||||||
							
								
								
									
										4
									
								
								debian/watch
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/watch
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,3 @@ | |||||||
| version=4 | version=4 | ||||||
| opts="mode=git" https://github.com/yuezk/GlobalProtect-openconnect.git \ | opts="mode=git,pgpmode=gittag" \ | ||||||
|    refs/tags/v([\d\.]+) debian uupdate | https://github.com/yuezk/GlobalProtect-openconnect.git refs/tags/v([\d\.]+) | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								packaging/aur/PKGBUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packaging/aur/PKGBUILD
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | # Maintainer: Keinv Yue <yuezk001@gmail.com> | ||||||
|  |  | ||||||
|  | _pkgver="1.4.9" | ||||||
|  | _commit="acf184134a2ff19e4a39528bd6a7fbbafa4cf017" | ||||||
|  | pkgname=globalprotect-openconnect-git | ||||||
|  | pkgver=${_pkgver} | ||||||
|  | pkgrel=1 | ||||||
|  | pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode. (development version)" | ||||||
|  | arch=(x86_64 aarch64) | ||||||
|  | url="https://github.com/yuezk/GlobalProtect-openconnect" | ||||||
|  | license=('GPL3') | ||||||
|  | backup=( | ||||||
|  |     etc/gpservice/gp.conf | ||||||
|  | ) | ||||||
|  | install=gp.install | ||||||
|  | depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5) | ||||||
|  | makedepends=(git cmake) | ||||||
|  | conflicts=('globalprotect-openconnect') | ||||||
|  | provides=('globalprotect-openconnect' 'gpclient' 'gpservice') | ||||||
|  |  | ||||||
|  | source=(git+https://github.com/yuezk/GlobalProtect-openconnect#commit=${_commit}) | ||||||
|  | sha256sums=('SKIP') | ||||||
|  |  | ||||||
|  | prepare() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     echo "${_pkgver}" > VERSION | ||||||
|  | } | ||||||
|  |  | ||||||
|  | build() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     cmake -B build \ | ||||||
|  |         -DCMAKE_BUILD_TYPE=Release \ | ||||||
|  |         -DCMAKE_CXX_FLAGS_RELEASE=-s | ||||||
|  |     make -j$(nproc) -C build | ||||||
|  | } | ||||||
|  |  | ||||||
|  | package() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     make DESTDIR="$pkgdir/" install -C build | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								packaging/aur/PKGBUILD.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packaging/aur/PKGBUILD.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | # Maintainer: Keinv Yue <yuezk001@gmail.com> | ||||||
|  |  | ||||||
|  | _pkgver="{VERSION}" | ||||||
|  | _commit="{COMMIT}" | ||||||
|  | pkgname=globalprotect-openconnect-git | ||||||
|  | pkgver=${_pkgver} | ||||||
|  | pkgrel=1 | ||||||
|  | pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode. (development version)" | ||||||
|  | arch=(x86_64 aarch64) | ||||||
|  | url="https://github.com/yuezk/GlobalProtect-openconnect" | ||||||
|  | license=('GPL3') | ||||||
|  | backup=( | ||||||
|  |     etc/gpservice/gp.conf | ||||||
|  | ) | ||||||
|  | install=gp.install | ||||||
|  | depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5) | ||||||
|  | makedepends=(git cmake) | ||||||
|  | conflicts=('globalprotect-openconnect') | ||||||
|  | provides=('globalprotect-openconnect' 'gpclient' 'gpservice') | ||||||
|  |  | ||||||
|  | source=(git+https://github.com/yuezk/GlobalProtect-openconnect#commit=${_commit}) | ||||||
|  | sha256sums=('SKIP') | ||||||
|  |  | ||||||
|  | prepare() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     echo "${_pkgver}" > VERSION | ||||||
|  | } | ||||||
|  |  | ||||||
|  | build() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     cmake -B build \ | ||||||
|  |         -DCMAKE_BUILD_TYPE=Release \ | ||||||
|  |         -DCMAKE_CXX_FLAGS_RELEASE=-s | ||||||
|  |     make -j$(nproc) -C build | ||||||
|  | } | ||||||
|  |  | ||||||
|  | package() { | ||||||
|  |     cd GlobalProtect-openconnect | ||||||
|  |     make DESTDIR="$pkgdir/" install -C build | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								packaging/aur/gp.install
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								packaging/aur/gp.install
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | post_install() { | ||||||
|  |     systemctl enable gpservice.service | ||||||
|  |     systemctl restart gpservice.service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | post_upgrade() { | ||||||
|  |     post_install | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user