Compare commits

..

125 Commits

Author SHA1 Message Date
Kevin Yue
c2f7576d10 Release 1.4.4 2022-05-14 19:21:14 +08:00
Kevin Yue
4327235093 Updated VERSION, Bumped 1.4.3 –> 1.4.4 2022-05-14 19:21:03 +08:00
Kevin Yue
0699878b92 fix: support the HighDPI displays
Refs: #115
2022-05-14 19:12:07 +08:00
Kevin Yue
e3aba11506 [misc] update the build script 2022-05-09 22:40:00 +08:00
Kevin Yue
ff58258d5c [ci] Enable build job for master branch 2022-05-09 22:26:22 +08:00
Kevin Yue
991cf25a7b [ci] Add ubuntu 22.04 2022-05-09 22:23:08 +08:00
Kevin Yue
02c70150ba Release 1.4.3 2022-05-09 22:20:54 +08:00
Kevin Yue
28d8321958 Updated VERSION, Bumped 1.4.2 –> 1.4.3 2022-05-09 22:20:46 +08:00
Kevin Yue
e1c9180cae refine AUR packaging 2022-05-09 22:09:27 +08:00
Kevin Yue
57df34fd1e Prepare release 1.4.3 (#149)
* add inih

* add configuration file for gpservice

* Disable the UI configuration for extra args

* remove VERSION_SUFFIX

* remove ppa-publish.sh

* Use Git repo as the source for PKGBUILD

* remove VERSION_SUFFIX

* Use Git repo as the source for PKGBUILD

* add .install for PKGBUILD

* add configuration file

* Fix cmake

* Fix cmake

* Disable snap job

* update AUR packaging

* Disable the UI configuration for extra args

* improve packaging script

* update README.md

* restart gpservice after package upgrading
2022-05-09 21:58:58 +08:00
Kevin Yue
04d180e11a Release 1.4.2 2022-05-06 22:18:19 +08:00
Kevin Yue
6d3b127569 Updated VERSION, Bumped 1.4.1 –> 1.4.2 2022-05-06 22:17:49 +08:00
Erik Lindblad
e72b25e415 Clear SSL_OP_LEGACY_SERVER_CONNECT (#146)
Co-authored-by: Erik Lindblad <erili@spotify.com>
2022-05-06 21:26:27 +08:00
Kevin Yue
37a511c24d Release 1.4.1 2022-03-03 21:58:59 +08:00
Kevin Yue
ad7db36c92 Updated VERSION, Bumped 1.4.0 –> 1.4.1 2022-03-03 21:58:27 +08:00
Kevin Yue
11dc5920ef print the gpservice logs 2022-03-03 21:30:33 +08:00
Kevin Yue
e6383916c7 update AUR packaging 2022-03-02 22:11:47 +08:00
Kevin Yue
1d9d928b26 update AUR packaging 2022-03-02 22:06:26 +08:00
Kevin Yue
c02ad5d46d Release 1.4.0 2022-03-02 21:34:19 +08:00
Kevin Yue
2319c7c49c Updated VERSION, Bumped 1.3.4 –> 1.4.0 2022-03-02 21:28:02 +08:00
David Cohen
e0c2c14dc3 Fix gpservice after openconnect v8.20 (#124) 2022-03-01 15:41:29 +08:00
Kevin Yue
8f27c92e7b Add 2FA support (#112) 2021-12-20 22:20:02 +08:00
Karolin Varner
9d6ec84c14 Add a scripting mode to GPClient (#110) 2021-12-20 18:46:16 +08:00
Kevin Yue
dd81ed9519 Stop saving credentials (#111) 2021-12-20 18:43:37 +08:00
Kevin Yue
32bd713965 update CI 2021-12-20 18:32:18 +08:00
Kevin Yue
ba92517141 add editorconfig 2021-12-20 18:31:56 +08:00
Kevin Yue
0e4e082594 Update README.md 2021-11-30 16:42:04 +08:00
Kevin Yue
3e590cab7b Update README.md 2021-11-30 10:44:38 +08:00
Aloïs de Gouvello
3e0e4cff12 Add a run entry (#108)
Fix #107
2021-11-30 10:43:56 +08:00
Kevin Yue
692df2f2c5 update the installation instruction of Arch Linux 2021-11-01 17:12:15 +08:00
Kevin Yue
f2b9ffddde Update README.md 2021-10-30 09:41:32 +08:00
Kevin Yue
ca38925066 Update README.md 2021-10-24 17:26:02 +08:00
Kevin Yue
8591dd7e81 Update README.md 2021-10-24 16:43:01 +08:00
Kevin Yue
b07880930e update AUR packaging 2021-10-24 13:09:44 +08:00
Kevin Yue
fceb80e10e update CI scripts 2021-10-24 12:28:18 +08:00
Kevin Yue
d802c56d8f Release 1.3.4 2021-10-24 12:13:24 +08:00
Kevin Yue
386f08d0e8 Updated VERSION, Bumped 1.3.3 –> 1.3.4 2021-10-24 12:13:15 +08:00
Kevin Yue
9e7fb17bd3 update packaging (#100) 2021-10-24 12:11:54 +08:00
Kevin Yue
36d9753008 shorten the sponsor links 2021-10-15 19:21:35 +08:00
Kevin Yue
e5b3df9cda Update README.md 2021-10-14 19:17:47 +08:00
Kevin Yue
0dd705d0c0 add sponsor links 2021-10-14 19:09:39 +08:00
Antoine Allard
ce2360be61 Adding application logs location in the README (#95)
Co-authored-by: ALLARD Antoine <Antoine.ALLARD@murex.com>
2021-09-24 17:33:36 +08:00
Kevin Yue
b5b7033eee Update README.md 2021-09-22 23:34:52 +08:00
Kevin Yue
9e7db4eb86 improve the doc 2021-09-22 11:17:37 +08:00
Kevin Yue
bc07e3d496 Add snap packaging (#93)
* snapcraft init

* update packaging

* update packaging

* update packaging

* update packaging

* update packaging

* update packaging

* snap worked

* fix locale warning

* polish code

* update metainfo

* update icon

* update icon

* update message
2021-09-20 20:48:24 +08:00
Kevin Yue
452fe2f189 update doc 2021-09-19 15:40:20 +08:00
Kevin Yue
8a65099ca7 Migrate to cmake and refine the code structure (#92)
* migrate to cmake

* move the 3rd party libs

* organize 3rdparty

* update the 3rd party version

* refine the CMakeLists.txt

* update install command

* update install command

* update install command

* update install command

* update dependency

* update the dependency

* update the dependency

* remove CPM.cmake

* remove QtCreator project file

* update cmake file

* improve cmake file

* add cmakew

* use wget

* remove echo

* update the doc

* remove the screenshot

* update the doc

* update the install steps

* check the openconnect version

* update the doc

* update install scripts

* fix install scripts

* improve message

* improve message

* improve install scripts

* improve the version check

* improve the version check

* improve install script

* add version

* organize includes

* add version bump

* update CI

* update CI

* add the release flag

* update message
2021-09-19 14:32:12 +08:00
Kevin Yue
5c97b2df7a QStringView -> QString 2021-09-14 00:32:05 +08:00
Kevin Yue
0d4485d754 Update README.md 2021-09-10 22:49:54 +08:00
Kevin Yue
98e641e99d release 1.3.3 2021-09-04 19:03:01 +08:00
Kevin Yue
6fa77cdbd2 Fix the clientos param (#87)
* fix the clientos param

* fix the clientos param
2021-09-04 18:56:17 +08:00
Kevin Yue
64e6487e7e release 1.3.2 2021-09-02 21:11:47 +08:00
Kevin Yue
e8b2c1606f Add default value to client os (#86)
* add default value for clientos

* update CI

* update icon format

* change the icon format
2021-09-02 21:08:56 +08:00
Kevin Yue
84f1480653 release 1.3.1 2021-08-31 20:54:04 +08:00
Kevin Yue
3175855122 add rpm packaging (#83) 2021-08-31 20:52:08 +08:00
Kevin Yue
fa8b5c1528 Update CI scripts 2021-08-29 20:06:13 +08:00
Kevin Yue
7b9942c7e6 Update README.md 2021-08-26 00:42:25 +08:00
Kevin Yue
011a1a0dec Update README.md 2021-08-26 00:39:13 +08:00
Kevin Yue
4a53033023 [ci] use action-automatic-releases 2021-08-23 08:53:41 +08:00
Kevin Yue
9c6ea1c4b5 [ci] replace artifacts 2021-08-23 08:32:12 +08:00
Kevin Yue
3369ad4c1d [ci] update release action 2021-08-23 08:13:01 +08:00
Kevin Yue
25c9f2291a Update pre-release.yml 2021-08-23 01:35:12 +08:00
Kevin Yue
bba3bc7e4f [ci] improve action script 2021-08-23 01:04:17 +08:00
Kevin Yue
b12b692090 [ci] update action script 2021-08-23 00:30:01 +08:00
Kevin Yue
1300a0cc43 [ci] install qt 2021-08-22 23:56:05 +08:00
Kevin Yue
165080b476 [ci] build debian package 2021-08-22 23:46:20 +08:00
Kevin Yue
d6af8a1598 [ci] Update the changlog 2021-08-22 22:41:47 +08:00
Kevin Yue
eef92b1d31 Update action script 2021-08-22 21:07:52 +08:00
Kevin Yue
946ead24a4 Bump the changelog 2021-08-22 20:05:59 +08:00
Kevin Yue
39e57c8598 Add version suffix 2021-08-22 19:30:34 +08:00
Kevin Yue
4e2e423c27 Update the branch 2021-08-22 18:39:31 +08:00
Kevin Yue
732a62f1ee Add pre-release action 2021-08-22 18:34:56 +08:00
Kevin Yue
9f9444a72b Display error when OpenConnect was not found (#81) 2021-08-21 19:32:13 +08:00
Kevin Yue
6352e1fb2b Make the clientos configurable and improve Reset Settings (#80)
* Set the gateway

* Make clientos configurable

* Update readme.md

* Update README.md
2021-08-21 18:44:16 +08:00
Kevin Yue
42cae3ff26 Port the splitCommand method (#79) 2021-08-19 19:10:05 +08:00
Kevin Yue
53c8572cf6 Update main.yml 2021-08-19 18:42:26 +08:00
Kevin Yue
3f6467321f Update main.yml 2021-08-19 18:33:01 +08:00
Kevin Yue
563ec48c8c Update main.yml 2021-08-19 18:26:05 +08:00
Kevin Yue
3787ae164c Update main.yml 2021-08-19 18:24:30 +08:00
Kevin Yue
04a24c34e8 Update future plan 2021-08-18 16:16:52 +08:00
Kevin Yue
fe68248b1f Add future plan 2021-08-18 16:03:08 +08:00
Kevin Yue
47013033ec Release 1.3.0 2021-08-15 20:44:18 +08:00
Kevin Yue
05fb9a26bd Update links 2021-08-15 20:40:13 +08:00
Kevin Yue
96962f957c Add links (#77) 2021-08-15 17:36:51 +08:00
Kevin Yue
b4f9cfae67 Support custom parameters (#76)
* Add the setting icon

* Add support for custom parameters

* Ignore auto generated files

* Update README.md
2021-08-15 12:47:02 +08:00
Jan Vlug
c8942984a8 Added missing dependency for Fedora 34 (#75)
* Added missing dependency for Fedora 34.

* Removed architecture specification.

* Whitespace.
2021-08-08 19:17:15 +08:00
Darío Cutillas
3907827d0e Add pre-requisites for Fedora (#73) 2021-08-03 22:25:09 +08:00
Kevin Yue
f089996cdc Release 1.2.9 2021-08-03 22:20:36 +08:00
Tom Almeida
260b557238 Properly handle gateway responses that return Javascript errors (#74)
This was previously causing a segmentation fault, as the gateway
response would not be valid XML to be parsed.

Closes: #38, #71
2021-08-03 22:17:50 +08:00
Robert M Flight
3495dbfe18 Remove qt5 default (#68)
* removing qt5-default

as of ubuntu 21.04 it doesn't exist anymore

* update readme

based on ubuntu 21, and actually installing the deb for ubuntu

* missed the other package
2021-07-04 18:31:28 +08:00
Matt McHenry
cdf193024c README.md: add section for NixOS (#65) 2021-06-18 21:35:28 +08:00
Kevin Yue
76de070d78 Update publish.yml 2021-05-07 13:42:33 +08:00
Kevin Yue
420ae27888 Update publish.yml 2021-05-07 13:34:22 +08:00
Tobias Sarnowski
6a347746cc Mark for inclusion in aarch64 archlinux repository (#58)
This version of the software was tested on current Manjaro as of this
commit date on a Pinebook Pro with the official aarch64 KDE image.
2021-05-06 15:19:07 +08:00
Kevin Yue
624babb380 Update publish.yml 2021-04-25 21:24:08 +08:00
Kevin Yue
511b20fdcd Update publish.yml 2021-04-25 10:36:44 +08:00
Kevin Yue
abe33c7407 Update publish.yml 2021-04-25 10:29:03 +08:00
Kevin Yue
99a82c8641 Update publish.yml 2021-04-24 23:08:10 +08:00
Kevin Yue
e5d0acad3c Update publish.yml 2021-04-24 22:44:24 +08:00
Kevin Yue
38a1eded19 Release 1.2.8 2021-04-24 22:21:29 +08:00
Kevin Yue
3e23e7eaae Update publish action 2021-04-24 22:20:33 +08:00
Kevin Yue
cf46848e63 Merge branch 'add-github-actions' 2021-04-24 22:15:42 +08:00
Kevin Yue
2e826201d2 Create publish.yml 2021-04-24 22:13:48 +08:00
Kevin Yue
adba408dc3 Add PKGBUILD.template 2021-04-24 21:13:33 +08:00
Kevin Yue
5d613369ee Create main.yml 2021-04-24 19:02:07 +08:00
hakasapl
ebd3de6f63 added startupwmclass to desktop file (#51) 2021-04-15 16:15:13 +08:00
Michaël Arnauts
266ab65892 Update README.md (#45)
Add qttools5-dev as required package for Ubuntu.
2021-03-03 14:31:23 +08:00
Kevin Yue
ccaf93ec31 Release 1.2.7 2020-12-29 21:32:04 +08:00
Mike Gelfand
e08d7d7c4d Don't quit the app when closing the main window (#40)
Fixes: #15
2020-12-24 10:40:35 +08:00
Raphael Sant'Anna
c14a6ad1d2 Fix parsing of Gateways and Priority Rules (#35)
* Fix gateways and priority rules parsing

* Removing comment with dead code

Co-authored-by: Raphael Sant'Anna <raphael.santanna@exame.com>
2020-11-01 09:55:27 +08:00
Kevin Yue
d91fad089f Release 1.2.5 2020-07-19 18:32:34 +08:00
Kevin Yue
2c1036ff10 Add log entry for the gateway response 2020-07-19 18:26:08 +08:00
Kevin Yue
d5f9283b93 Skip the ssl certificate verifying (#26) 2020-07-19 17:26:54 +08:00
Kevin Yue
fe7b96ce9b Update changelog 2020-07-09 10:22:38 +08:00
Kevin Yue
790865c060 Update debian scripts 2020-06-09 22:20:35 +08:00
Kevin Yue
7f056c98ce Remove auto generated files 2020-06-07 22:05:01 +08:00
Amit Joshi
70816a9600 Add debian packaging instructions and code (#19)
* First cut of creating a debian package

* Update version

* Add notes to build debian package

Co-authored-by: amit.joshi@markit.com <amit.joshi@markit.com>
2020-06-01 13:36:43 +08:00
Kevin Yue
337a94efcd Release 1.2.4 2020-05-31 12:15:06 +08:00
Kevin Yue
cf34f9f70f Improve the authentication workflow (#18) 2020-05-31 12:14:56 +08:00
Kevin Yue
3a790cdc63 Release v1.2.3 2020-05-30 23:00:38 +08:00
Kevin Yue
73925fd1e2 Add more logs to debug the portal login (#16) 2020-05-30 22:58:58 +08:00
Kevin Yue
e12613d9a4 Try to fix saml hang (#14)
* Try to fix saml login hang

* Add more logs

* Add version number
2020-05-30 11:22:05 +08:00
Kevin Yue
86ad51b0ad Add extra parameters to prelogin request 2020-05-29 23:44:02 +08:00
Kevin Yue
1e2322b938 Fix saml login for portal-userauthcookie (#12) 2020-05-29 23:38:51 +08:00
Kevin Yue
4313b9d0e7 Update README.md 2020-05-25 10:05:11 +08:00
120 changed files with 4151 additions and 586 deletions

13
.editorconfig Normal file
View 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
View File

@@ -0,0 +1,2 @@
ko_fi: yuezk
custom: ["https://buymeacoffee.com/yuezk", "https://paypal.me/zongkun"]

265
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,265 @@
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
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
- 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
- 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

31
.github/workflows/pr.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
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

13
.gitignore vendored
View File

@@ -1,11 +1,20 @@
# Binaries
gpclient
gpservice
*.rpm
*.gz
*.snap
.DS_Store
build-debian
build
artifacts
.cmake
# Auto generated DBus files
*_adaptor.cpp
*_adaptor.h
gpservice_interface.*
# C++ objects and libs
*.slo
*.lo

4
.gitmodules vendored
View File

@@ -1,7 +1,7 @@
[submodule "singleapplication"]
path = singleapplication
path = 3rdparty/SingleApplication
url = https://github.com/itay-grudev/SingleApplication.git
[submodule "plog"]
path = plog
path = 3rdparty/plog
url = https://github.com/SergiusTheBest/plog.git

26
.vscode/settings.json vendored Normal file
View 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

11
3rdparty/inih/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.10.0)
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
View 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
View 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
View 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
View 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
View 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

Submodule 3rdparty/plog added at 914e799d2b

14
3rdparty/qt-unix-signals/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.1.0)
project(QtSignals LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
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
View 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.

37
CMakeLists.txt Normal file
View File

@@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
set(CMAKE_CXX_STANDARD 11)
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
)
add_subdirectory(3rdparty/qt-unix-signals)
add_subdirectory(3rdparty/inih)
add_subdirectory(GPService)
add_subdirectory(GPClient)
add_dependencies(gpclient gpservice)

104
GPClient/CMakeLists.txt Normal file
View File

@@ -0,0 +1,104 @@
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
gatewayauthenticator.cpp
gatewayauthenticatorparams.cpp
gpgateway.cpp
gphelper.cpp
loginparams.cpp
main.cpp
normalloginwindow.cpp
portalauthenticator.cpp
portalconfigresponse.cpp
preloginresponse.cpp
samlloginwindow.cpp
gpclient.cpp
settingsdialog.cpp
gpclient.ui
normalloginwindow.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}
)
target_link_libraries(gpclient
${SingleApplication_LIBRARY}
Qt5::Widgets
Qt5::Network
Qt5::WebSockets
Qt5::WebEngine
Qt5::WebEngineWidgets
Qt5::DBus
QtSignals
)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)
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)

View File

@@ -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

View File

@@ -1,8 +1,8 @@
#include "cdpcommand.h"
#include <QtCore/QVariantMap>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QJsonObject>
#include "cdpcommand.h"
CDPCommand::CDPCommand(QObject *parent) : QObject(parent)
{

View File

@@ -1,7 +1,7 @@
#ifndef CDPCOMMAND_H
#define CDPCOMMAND_H
#include <QObject>
#include <QtCore/QObject>
class CDPCommand : public QObject
{

View File

@@ -1,7 +1,8 @@
#include "cdpcommandmanager.h"
#include <QVariantMap>
#include <QtCore/QVariantMap>
#include <plog/Log.h>
#include "cdpcommandmanager.h"
CDPCommandManager::CDPCommandManager(QObject *parent)
: QObject(parent)
, networkManager(new QNetworkAccessManager)

View File

@@ -1,11 +1,12 @@
#ifndef CDPCOMMANDMANAGER_H
#define CDPCOMMANDMANAGER_H
#include <QtCore/QObject>
#include <QtCore/QHash>
#include <QtWebSockets/QtWebSockets>
#include <QtNetwork/QNetworkAccessManager>
#include "cdpcommand.h"
#include <QObject>
#include <QHash>
#include <QtWebSockets>
#include <QNetworkAccessManager>
class CDPCommandManager : public QObject
{

View 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);
}
}

View 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
View 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>

View File

@@ -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;

View 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=@CMAKE_INSTALL_PREFIX@/bin/gpclient
Icon=com.yuezk.qt.gpclient
Keywords=GlobalProtect;Openconnect;SAML;connection;VPN;
StartupWMClass=gpclient

View 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>

View File

@@ -16,7 +16,7 @@
viewBox="0 0 96 96"
style="enable-background:new 0 0 96 96;"
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
id="metadata14"><rdf:RDF><cc:Work
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

View File

@@ -1,9 +1,9 @@
#include <QtCore/QProcessEnvironment>
#include <QtWebEngineWidgets/QWebEngineView>
#include "enhancedwebview.h"
#include "cdpcommandmanager.h"
#include <QtWebEngineWidgets/QWebEngineView>
#include <QProcessEnvironment>
EnhancedWebView::EnhancedWebView(QWidget *parent)
: QWebEngineView(parent)
, cdp(new CDPCommandManager)

View File

@@ -1,9 +1,10 @@
#ifndef ENHANCEDWEBVIEW_H
#define ENHANCEDWEBVIEW_H
#include "cdpcommandmanager.h"
#include <QtWebEngineWidgets/QWebEngineView>
#include "cdpcommandmanager.h"
#define ENV_CDP_PORT "QTWEBENGINE_REMOTE_DEBUGGING"
class EnhancedWebView : public QWebEngineView

View File

@@ -1,19 +1,26 @@
#include <QtNetwork/QNetworkReply>
#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
#include <plog/Log.h>
#include "gatewayauthenticator.h"
#include "gphelper.h"
#include "loginparams.h"
#include "preloginresponse.h"
#include <QNetworkReply>
#include <plog/Log.h>
#include "challengedialog.h"
using namespace gpclient::helper;
GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig)
GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params)
: QObject()
, preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp")
, 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")
, portalConfig(portalConfig)
{
if (!params.clientos().isEmpty()) {
preloginUrl = preloginUrl + "&clientos=" + params.clientos();
}
}
GatewayAuthenticator::~GatewayAuthenticator()
@@ -23,28 +30,32 @@ GatewayAuthenticator::~GatewayAuthenticator()
void GatewayAuthenticator::authenticate()
{
LoginParams params;
params.setUser(portalConfig.username());
params.setPassword(portalConfig.password());
params.setUserAuthCookie(portalConfig.userAuthCookie());
PLOGI << "Start gateway authentication...";
login(params);
LoginParams loginParams { params.clientos() };
loginParams.setUser(params.username());
loginParams.setPassword(params.password());
loginParams.setUserAuthCookie(params.userAuthCookie());
loginParams.setInputStr(params.inputStr());
login(loginParams);
}
void GatewayAuthenticator::login(const LoginParams &params)
void GatewayAuthenticator::login(const LoginParams &loginParams)
{
PLOGI << "Trying to login the gateway at " << loginUrl << " with " << params.toUtf8();
PLOGI << "Trying to login the gateway at " << loginUrl << " with " << loginParams.toUtf8();
QNetworkReply *reply = createRequest(loginUrl, params.toUtf8());
QNetworkReply *reply = createRequest(loginUrl, loginParams.toUtf8());
connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished);
}
void GatewayAuthenticator::onLoginFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QByteArray response = reply->readAll();
if (reply->error()) {
PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl).arg(reply->errorString());
if (reply->error() || response.contains("Authentication failure")) {
PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString());
if (normalLoginWindow) {
normalLoginWindow->setProcessing(false);
@@ -55,11 +66,18 @@ void GatewayAuthenticator::onLoginFinished()
return;
}
// 2FA
if (response.contains("Challenge")) {
PLOGI << "The server need input the challenge...";
showChallenge(response);
return;
}
if (normalLoginWindow) {
normalLoginWindow->close();
}
const QUrlQuery params = gpclient::helper::parseGatewayResponse(reply->readAll());
const QUrlQuery params = gpclient::helper::parseGatewayResponse(response);
emit success(params.toString());
}
@@ -76,7 +94,7 @@ void GatewayAuthenticator::onPreloginFinished()
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) {
PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl).arg(reply->errorString());
PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString());
emit fail("Error occurred on the gateway prelogin interface.");
return;
@@ -91,7 +109,7 @@ void GatewayAuthenticator::onPreloginFinished()
} else if (response.hasNormalAuthFields()) {
normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage());
} else {
PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl).arg(QString::fromUtf8(response.rawResponse()));
PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse()));
emit fail("Unknown response for gateway prelogin interface.");
}
@@ -100,7 +118,7 @@ void GatewayAuthenticator::onPreloginFinished()
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);
PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword);
normalLoginWindow = new NormalLoginWindow;
normalLoginWindow->setPortalAddress(gateway);
@@ -118,11 +136,13 @@ void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPasswo
void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const QString &password)
{
PLOGI << "Start to perform normal login...";
normalLoginWindow->setProcessing(true);
LoginParams params;
params.setUser(username);
params.setUsername(username);
params.setPassword(password);
login(params);
authenticate();
}
void GatewayAuthenticator::onLoginWindowRejected()
@@ -151,16 +171,56 @@ void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QSt
void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult)
{
PLOGI << "SAML login succeeded, got the prelogin cookie " << samlResult.value("preloginCookie");
if (samlResult.contains("preloginCookie")) {
PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie");
} else {
PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
}
LoginParams params;
params.setUser(samlResult.value("username"));
params.setPreloginCookie(samlResult.value("preloginCookie"));
LoginParams loginParams { params.clientos() };
loginParams.setUser(samlResult.value("username"));
loginParams.setPreloginCookie(samlResult.value("preloginCookie"));
loginParams.setUserAuthCookie(samlResult.value("userAuthCookie"));
login(params);
login(loginParams);
}
void GatewayAuthenticator::onSAMLLoginFail(const QString 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());
PLOGI << "Challenge submitted, try to re-authenticate...";
authenticate();
});
connect(challengeDialog, &ChallengeDialog::rejected, this, [this] {
if (normalLoginWindow) {
normalLoginWindow->close();
}
emit fail();
});
connect(challengeDialog, &ChallengeDialog::finished, this, [this] {
delete challengeDialog;
challengeDialog = nullptr;
});
challengeDialog->show();
}

View File

@@ -1,16 +1,18 @@
#ifndef GATEWAYAUTHENTICATOR_H
#define GATEWAYAUTHENTICATOR_H
#include "portalconfigresponse.h"
#include <QtCore/QObject>
#include "normalloginwindow.h"
#include "challengedialog.h"
#include "loginparams.h"
#include <QObject>
#include "gatewayauthenticatorparams.h"
class GatewayAuthenticator : public QObject
{
Q_OBJECT
public:
explicit GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig);
explicit GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params);
~GatewayAuthenticator();
void authenticate();
@@ -30,17 +32,18 @@ private slots:
private:
QString gateway;
GatewayAuthenticatorParams params;
QString preloginUrl;
QString loginUrl;
const PortalConfigResponse& portalConfig;
NormalLoginWindow *normalLoginWindow{ nullptr };
ChallengeDialog *challengeDialog{ nullptr };
void login(const LoginParams& params);
void login(const LoginParams& loginParams);
void doAuth();
void normalAuth(QString labelUsername, QString labelPassword, QString authMessage);
void samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl = "");
void showChallenge(const QString &responseText);
};
#endif // GATEWAYAUTHENTICATOR_H

View File

@@ -0,0 +1,67 @@
#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;
}

View 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

View File

@@ -1,31 +1,39 @@
#include <QtGui/QIcon>
#include <plog/Log.h>
#include "gpclient.h"
#include "gphelper.h"
#include "ui_gpclient.h"
#include "portalauthenticator.h"
#include "gatewayauthenticator.h"
#include <plog/Log.h>
#include <QIcon>
#include "settingsdialog.h"
#include "gatewayauthenticatorparams.h"
using namespace gpclient::helper;
GPClient::GPClient(QWidget *parent)
GPClient::GPClient(QWidget *parent, IVpn *vpn)
: QMainWindow(parent)
, ui(new Ui::GPClient)
, vpn(vpn)
, settingsDialog(new SettingsDialog(this))
{
ui->setupUi(this);
setWindowTitle("GlobalProtect");
setFixedSize(width(), height());
gpclient::helper::moveCenter(this);
setupSettings();
// Restore portal from the previous settings
ui->portalInput->setText(settings::get("portal", "").toString());
this->portal(settings::get("portal", "").toString());
// DBus service setup
vpn = new com::yuezk::qt::GPService("com.yuezk.qt.GPService", "/", QDBusConnection::systemBus(), this);
connect(vpn, &com::yuezk::qt::GPService::connected, this, &GPClient::onVPNConnected);
connect(vpn, &com::yuezk::qt::GPService::disconnected, this, &GPClient::onVPNDisconnected);
connect(vpn, &com::yuezk::qt::GPService::logAvailable, this, &GPClient::onVPNLogAvailable);
QObject *ov = dynamic_cast<QObject*>(vpn);
connect(ov, SIGNAL(connected()), this, SLOT(onVPNConnected()));
connect(ov, SIGNAL(disconnected()), this, SLOT(onVPNDisconnected()));
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.
initSystemTrayIcon();
@@ -36,6 +44,37 @@ GPClient::~GPClient()
{
delete ui;
delete vpn;
delete settingsDialog;
delete settingsButton;
}
void GPClient::setupSettings()
{
settingsButton = new QPushButton(this);
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->show();
}
void GPClient::onSettingsAccepted()
{
settings::save("clientos", settingsDialog->clientos());
}
void GPClient::on_connectButton_clicked()
@@ -69,7 +108,7 @@ void GPClient::initSystemTrayIcon()
connect(systemTrayIcon, &QSystemTrayIcon::activated, this, &GPClient::onSystemTrayActivated);
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);
contextMenu->addMenu(gatewaySwitchMenu);
contextMenu->addSeparator();
@@ -97,6 +136,8 @@ void GPClient::initVpnStatus() {
void GPClient::populateGatewayMenu()
{
PLOGI << "Populating the Switch Gateway menu...";
const QList<GPGateway> gateways = allGateways();
gatewaySwitchMenu->clear();
@@ -165,7 +206,7 @@ void GPClient::onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason)
switch (reason) {
case QSystemTrayIcon::Trigger:
case QSystemTrayIcon::DoubleClick:
this->activiate();
this->activate();
break;
default:
break;
@@ -200,12 +241,14 @@ void GPClient::onGatewayChanged(QAction *action)
void GPClient::doConnect()
{
PLOGI << "Start connecting...";
const QString btnText = ui->connectButton->text();
const QString portal = this->portal();
// Display the main window if portal is empty
if (portal.isEmpty()) {
activiate();
activate();
return;
}
@@ -214,16 +257,19 @@ void GPClient::doConnect()
// Login to the previously saved gateway
if (!currentGateway().name().isEmpty()) {
PLOGI << "Start gateway login using the previously saved gateway...";
isQuickConnect = true;
gatewayLogin();
} else {
// Perform the portal login
PLOGI << "Start portal login...";
portalLogin();
}
} else {
PLOGI << "Start disconnecting the VPN...";
ui->statusLabel->setText("Disconnecting...");
updateConnectionStatus(VpnStatus::pending);
vpn->disconnect();
}
}
@@ -231,11 +277,12 @@ void GPClient::doConnect()
// Login to the portal interface to get the portal config and preferred gateway
void GPClient::portalLogin()
{
PortalAuthenticator *portalAuth = new PortalAuthenticator(portal());
PortalAuthenticator *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString());
connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess);
// Prelogin failed on the portal interface, try to treat the portal as a gateway interface
connect(portalAuth, &PortalAuthenticator::preloginFailed, this, &GPClient::onPortalPreloginFail);
connect(portalAuth, &PortalAuthenticator::portalConfigFailed, this, &GPClient::onPortalConfigFail);
// Portal login failed
connect(portalAuth, &PortalAuthenticator::fail, this, &GPClient::onPortalFail);
@@ -244,18 +291,49 @@ void GPClient::portalLogin()
portalAuth->authenticate();
}
void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const GPGateway gateway, QList<GPGateway> allGateways)
void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region)
{
this->portalConfig = portalConfig;
setAllGateways(allGateways);
PLOGI << "Portal authentication succeeded.";
// No gateway found in protal configuration
if (portalConfig.allGateways().size() == 0) {
PLOGI << "No gateway found in portal configuration, treat the portal address as a gateway.";
tryGatewayLogin();
return;
}
GPGateway gateway = filterPreferredGateway(portalConfig.allGateways(), region);
setAllGateways(portalConfig.allGateways());
setCurrentGateway(gateway);
this->portalConfig = portalConfig;
gatewayLogin();
}
void GPClient::onPortalPreloginFail()
void GPClient::onPortalPreloginFail(const QString msg)
{
PLOGI << "Portal prelogin failed, try to preform login on the the gateway interface...";
PLOGI << "Portal prelogin failed: " << msg;
tryGatewayLogin();
}
void GPClient::onPortalConfigFail(const QString msg)
{
PLOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway.";
tryGatewayLogin();
}
void GPClient::onPortalFail(const QString &msg)
{
if (!msg.isEmpty()) {
openMessageBox("Portal authentication failed.", msg);
}
updateConnectionStatus(VpnStatus::disconnected);
}
void GPClient::tryGatewayLogin()
{
PLOGI << "Try to preform login on the the gateway interface...";
// Treat the portal input as the gateway address
GPGateway g;
@@ -271,19 +349,15 @@ void GPClient::onPortalPreloginFail()
gatewayLogin();
}
void GPClient::onPortalFail(const QString &msg)
{
if (!msg.isEmpty()) {
openMessageBox("Portal authentication failed.", msg);
}
updateConnectionStatus(VpnStatus::disconnected);
}
// Login to the gateway
void GPClient::gatewayLogin()
{
GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), portalConfig);
PLOGI << "Performing gateway login...";
GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig);
params.setClientos(settings::get("clientos", "Linux").toString());
GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params);
connect(gatewayAuth, &GatewayAuthenticator::success, this, &GPClient::onGatewaySuccess);
connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail);
@@ -298,7 +372,11 @@ void GPClient::onGatewaySuccess(const QString &authCookie)
PLOGI << "Gateway login succeeded, got the cookie " << authCookie;
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...");
updateConnectionStatus(VpnStatus::pending);
}
@@ -319,7 +397,7 @@ void GPClient::onGatewayFail(const QString &msg)
updateConnectionStatus(VpnStatus::disconnected);
}
void GPClient::activiate()
void GPClient::activate()
{
activateWindow();
showNormal();
@@ -335,13 +413,17 @@ QString GPClient::portal() const
return input;
}
void GPClient::portal(QString p)
{
ui->portalInput->setText(p);
}
bool GPClient::connected() const
{
const QString statusText = ui->statusLabel->text();
return statusText.contains("Connected") && !statusText.contains("Not");
}
QList<GPGateway> GPClient::allGateways() const
{
const QString gatewaysJson = settings::get(portal() + "_gateways").toString();
@@ -350,6 +432,8 @@ QList<GPGateway> GPClient::allGateways() const
void GPClient::setAllGateways(QList<GPGateway> gateways)
{
PLOGI << "Updating all the gateways...";
settings::save(portal() + "_gateways", GPGateway::serialize(gateways));
populateGatewayMenu();
}
@@ -368,6 +452,8 @@ GPGateway GPClient::currentGateway() const
void GPClient::setCurrentGateway(const GPGateway gateway)
{
PLOGI << "Updating the current gateway to " << gateway.name();
settings::save(portal() + "_selectedGateway", gateway.name());
populateGatewayMenu();
}
@@ -400,6 +486,12 @@ void GPClient::onVPNDisconnected()
}
}
void GPClient::onVPNError(QString errorMessage)
{
updateConnectionStatus(VpnStatus::disconnected);
openMessageBox("Failed to connect", errorMessage);
}
void GPClient::onVPNLogAvailable(QString log)
{
PLOGI << log;

View File

@@ -1,12 +1,14 @@
#ifndef GPCLIENT_H
#define GPCLIENT_H
#include "gpservice_interface.h"
#include "portalconfigresponse.h"
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QSystemTrayIcon>
#include <QtWidgets/QMenu>
#include <QtWidgets/QPushButton>
#include <QMainWindow>
#include <QSystemTrayIcon>
#include <QMenu>
#include "portalconfigresponse.h"
#include "settingsdialog.h"
#include "vpn.h"
QT_BEGIN_NAMESPACE
namespace Ui { class GPClient; }
@@ -17,12 +19,24 @@ class GPClient : public QMainWindow
Q_OBJECT
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();
private slots:
void onSettingsButtonClicked();
void onSettingsAccepted();
void on_connectButton_clicked();
void on_portalInput_returnPressed();
void on_portalInput_editingFinished();
@@ -30,8 +44,9 @@ private slots:
void onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason);
void onGatewayChanged(QAction *action);
void onPortalSuccess(const PortalConfigResponse portalConfig, const GPGateway gateway, QList<GPGateway> allGateways);
void onPortalPreloginFail();
void onPortalSuccess(const PortalConfigResponse portalConfig, const QString region);
void onPortalPreloginFail(const QString msg);
void onPortalConfigFail(const QString msg);
void onPortalFail(const QString &msg);
void onGatewaySuccess(const QString &authCookie);
@@ -39,6 +54,7 @@ private slots:
void onVPNConnected();
void onVPNDisconnected();
void onVPNError(QString errorMessage);
void onVPNLogAvailable(QString log);
private:
@@ -50,7 +66,7 @@ private:
};
Ui::GPClient *ui;
com::yuezk::qt::GPService *vpn;
IVpn *vpn;
QSystemTrayIcon *systemTrayIcon;
QMenu *contextMenu;
@@ -61,29 +77,29 @@ private:
QAction *clearAction;
QAction *quitAction;
SettingsDialog *settingsDialog;
QPushButton *settingsButton;
bool isQuickConnect { false };
bool isSwitchingGateway { false };
PortalConfigResponse portalConfig;
void setupSettings();
void initSystemTrayIcon();
void initVpnStatus();
void populateGatewayMenu();
void updateConnectionStatus(const VpnStatus &status);
void doConnect();
void portalLogin();
void tryGatewayLogin();
void gatewayLogin();
QString portal() const;
bool connected() const;
QList<GPGateway> allGateways() const;
void setAllGateways(QList<GPGateway> gateways);
GPGateway currentGateway() const;
void setCurrentGateway(const GPGateway gateway);
void clearSettings();
void quit();
};
#endif // GPCLIENT_H

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>260</width>
<height>338</height>
<height>362</height>
</rect>
</property>
<property name="windowTitle">
@@ -36,7 +36,7 @@
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0">
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0,0">
<property name="leftMargin">
<number>15</number>
</property>
@@ -123,6 +123,16 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;https://bit.ly/3g5DHqy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#4c6b8a;&quot;&gt;Report a bug&lt;/span&gt;&lt;/a&gt; / &lt;a href=&quot;https://bit.ly/3jQYfEi&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#4c6b8a;&quot;&gt;Buy me a coffee&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@@ -1,8 +1,8 @@
#include "gpgateway.h"
#include <QtCore/QJsonObject>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include "gpgateway.h"
GPGateway::GPGateway()
{

View File

@@ -1,9 +1,9 @@
#ifndef GPGATEWAY_H
#define GPGATEWAY_H
#include <QString>
#include <QMap>
#include <QJsonObject>
#include <QtCore/QString>
#include <QtCore/QMap>
#include <QtCore/QJsonObject>
class GPGateway
{

View File

@@ -1,17 +1,27 @@
#include "gphelper.h"
#include <QNetworkRequest>
#include <QXmlStreamReader>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QApplication>
#include <QWidget>
#include <QtCore/QXmlStreamReader>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslSocket>
#include <plog/Log.h>
#include "gphelper.h"
QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager;
QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params)
{
QNetworkRequest request(url);
// Skip the ssl verifying
QSslConfiguration conf = request.sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
conf.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation, false);
request.setSslConfiguration(conf);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setHeader(QNetworkRequest::UserAgentHeader, UA);
@@ -23,10 +33,13 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params)
GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName)
{
PLOGI << gateways.size() << " gateway(s) avaiable, filter the gateways with rule: " << ruleName;
GPGateway gateway = gateways.first();
for (GPGateway g : gateways) {
if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) {
PLOGI << "Find a preferred gateway: " << g.name();
gateway = g;
}
}
@@ -36,6 +49,9 @@ GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, co
QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml)
{
PLOGI << "Start parsing the gateway response...";
PLOGI << "The gateway response is: " << xml;
QXmlStreamReader xmlReader{xml};
QList<QString> args;
@@ -102,7 +118,13 @@ void gpclient::helper::settings::save(const QString &key, const QVariant &value)
_settings->setValue(key, value);
}
void gpclient::helper::settings::clear()
{
_settings->clear();
QStringList keys = _settings->allKeys();
for (const auto &key : qAsConst(keys)) {
if (!reservedKeys.contains(key)) {
_settings->remove(key);
}
}
}

View File

@@ -1,16 +1,16 @@
#ifndef 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 "gpgateway.h"
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QSettings>
const QString UA = "PAN GlobalProtect";
@@ -31,6 +31,7 @@ namespace gpclient {
namespace settings {
extern QSettings *_settings;
static const QStringList reservedKeys {"extraArgs", "clientos"};
QVariant get(const QString &key, const QVariant &defaultValue = QVariant());
void save(const QString &key, const QVariant &value);

View File

@@ -1,25 +0,0 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -i gpservice_interface.h -p :gpservice_interface.cpp ../GPService/gpservice.xml
*
* qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "gpservice_interface.h"
/*
* Implementation of interface class ComYuezkQtGPServiceInterface
*/
ComYuezkQtGPServiceInterface::ComYuezkQtGPServiceInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
}
ComYuezkQtGPServiceInterface::~ComYuezkQtGPServiceInterface()
{
}

View File

@@ -1,71 +0,0 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp -p gpservice_interface.h: ../GPService/gpservice.xml
*
* qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef GPSERVICE_INTERFACE_H
#define GPSERVICE_INTERFACE_H
#include <QtCore/QObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
/*
* Proxy class for interface com.yuezk.qt.GPService
*/
class ComYuezkQtGPServiceInterface: public QDBusAbstractInterface
{
Q_OBJECT
public:
static inline const char *staticInterfaceName()
{ return "com.yuezk.qt.GPService"; }
public:
ComYuezkQtGPServiceInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);
~ComYuezkQtGPServiceInterface();
public Q_SLOTS: // METHODS
inline QDBusPendingReply<> connect(const QString &server, const QString &username, const QString &passwd)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(server) << QVariant::fromValue(username) << QVariant::fromValue(passwd);
return asyncCallWithArgumentList(QStringLiteral("connect"), argumentList);
}
inline QDBusPendingReply<> disconnect()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("disconnect"), argumentList);
}
inline QDBusPendingReply<int> status()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QStringLiteral("status"), argumentList);
}
Q_SIGNALS: // SIGNALS
void connected();
void disconnected();
void logAvailable(const QString &log);
};
namespace com {
namespace yuezk {
namespace qt {
typedef ::ComYuezkQtGPServiceInterface GPService;
}
}
}
#endif

View File

@@ -1,12 +1,12 @@
#include <QtCore/QUrlQuery>
#include "loginparams.h"
#include <QUrlQuery>
LoginParams::LoginParams()
LoginParams::LoginParams(const QString clientos)
{
params.addQueryItem("prot", QUrl::toPercentEncoding("https:"));
params.addQueryItem("server", "");
params.addQueryItem("inputSrc", "");
params.addQueryItem("inputStr", "");
params.addQueryItem("jnlpReady", "jnlpReady");
params.addQueryItem("user", "");
params.addQueryItem("passwd", "");
@@ -15,7 +15,12 @@ LoginParams::LoginParams()
params.addQueryItem("direct", "yes");
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);
}
params.addQueryItem("portal-userauthcookie", "");
params.addQueryItem("portal-prelogonuserauthcookie", "");
params.addQueryItem("prelogin-cookie", "");
@@ -26,42 +31,47 @@ LoginParams::~LoginParams()
{
}
void LoginParams::setUser(const QString &user)
void LoginParams::setUser(const QString user)
{
updateQueryItem("user", user);
}
void LoginParams::setServer(const QString &server)
void LoginParams::setServer(const QString server)
{
updateQueryItem("server", server);
}
void LoginParams::setPassword(const QString &password)
void LoginParams::setPassword(const QString password)
{
updateQueryItem("passwd", password);
}
void LoginParams::setUserAuthCookie(const QString &cookie)
void LoginParams::setUserAuthCookie(const QString cookie)
{
updateQueryItem("portal-userauthcookie", cookie);
}
void LoginParams::setPrelogonAuthCookie(const QString &cookie)
void LoginParams::setPrelogonAuthCookie(const QString cookie)
{
updateQueryItem("portal-prelogonuserauthcookie", cookie);
}
void LoginParams::setPreloginCookie(const QString &cookie)
void LoginParams::setPreloginCookie(const QString cookie)
{
updateQueryItem("prelogin-cookie", cookie);
}
void LoginParams::setInputStr(const QString inputStr)
{
updateQueryItem("inputStr", inputStr);
}
QByteArray LoginParams::toUtf8() const
{
return params.toString().toUtf8();
}
void LoginParams::updateQueryItem(const QString &key, const QString &value)
void LoginParams::updateQueryItem(const QString key, const QString value)
{
if (params.hasQueryItem(key)) {
params.removeQueryItem(key);

View File

@@ -1,27 +1,28 @@
#ifndef LOGINPARAMS_H
#define LOGINPARAMS_H
#include <QUrlQuery>
#include <QtCore/QUrlQuery>
class LoginParams
{
public:
LoginParams();
LoginParams(const QString clientos);
~LoginParams();
void setUser(const QString &user);
void setServer(const QString &server);
void setPassword(const QString &password);
void setUserAuthCookie(const QString &cookie);
void setPrelogonAuthCookie(const QString &cookie);
void setPreloginCookie(const QString &cookie);
void setUser(const QString user);
void setServer(const QString server);
void setPassword(const QString password);
void setUserAuthCookie(const QString cookie);
void setPrelogonAuthCookie(const QString cookie);
void setPreloginCookie(const QString cookie);
void setInputStr(const QString inputStr);
QByteArray toUtf8() const;
private:
QUrlQuery params;
void updateQueryItem(const QString &key, const QString &value);
void updateQueryItem(const QString key, const QString value);
};
#endif // LOGINPARAMS_H

View File

@@ -1,33 +1,87 @@
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QDir>
#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 "gpclient.h"
#include "vpn_dbus.h"
#include "vpn_json.h"
#include "enhancedwebview.h"
#include "sigwatch.h"
#include "version.h"
#include <QStandardPaths>
#include <plog/Log.h>
#include <plog/Appenders/ColorConsoleAppender.h>
#define QT_AUTO_SCREEN_SCALE_FACTOR "QT_AUTO_SCREEN_SCALE_FACTOR"
int main(int argc, char *argv[])
{
const QDir path = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/GlobalProtect-openconnect";
const QString logFile = path.path() + "/gpclient.log";
if (!path.exists()) {
path.mkpath(".");
}
plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr);
plog::init(plog::debug, &consoleAppender);
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::debug, logFile.toUtf8()).addAppender(&consoleAppender);
PLOGI << "GlobalProtect started, version: " << VERSION;
QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT));
QString hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR));
if (port == "") {
if (port.isEmpty()) {
qputenv(ENV_CDP_PORT, "12315");
}
if (hidpiSupport.isEmpty()) {
qputenv(QT_AUTO_SCREEN_SCALE_FACTOR, "true");
}
SingleApplication app(argc, argv);
GPClient w;
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."},
});
parser.process(app);
const QStringList positional = parser.positionalArguments();
IVpn *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);
w.show();
QObject::connect(&app, &SingleApplication::instanceStarted, &w, &GPClient::activiate);
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("now")) {
w.doConnect();
}
if (parser.isSet("json")) {
QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit);
}
return app.exec();
}

View File

@@ -1,8 +1,8 @@
#include <QtGui/QCloseEvent>
#include "normalloginwindow.h"
#include "ui_normalloginwindow.h"
#include <QCloseEvent>
NormalLoginWindow::NormalLoginWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::NormalLoginWindow)

View File

@@ -1,7 +1,7 @@
#ifndef PORTALAUTHWINDOW_H
#define PORTALAUTHWINDOW_H
#include <QDialog>
#include <QtWidgets/QDialog>
namespace Ui {
class NormalLoginWindow;

View File

@@ -1,3 +1,6 @@
#include <QtNetwork/QNetworkReply>
#include <plog/Log.h>
#include "portalauthenticator.h"
#include "gphelper.h"
#include "normalloginwindow.h"
@@ -7,16 +10,17 @@
#include "portalconfigresponse.h"
#include "gpgateway.h"
#include <plog/Log.h>
#include <QNetworkReply>
using namespace gpclient::helper;
PortalAuthenticator::PortalAuthenticator(const QString& portal) : QObject()
PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& clientos) : QObject()
, portal(portal)
, preloginUrl("https://" + portal + "/global-protect/prelogin.esp")
, 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")
{
if (!clientos.isEmpty()) {
preloginUrl = preloginUrl + "&clientos=" + clientos;
}
}
PortalAuthenticator::~PortalAuthenticator()
@@ -37,7 +41,7 @@ void PortalAuthenticator::onPreloginFinished()
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) {
PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl).arg(reply->errorString());
PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString());
emit preloginFailed("Error occurred on the portal prelogin interface.");
delete reply;
return;
@@ -46,6 +50,9 @@ void PortalAuthenticator::onPreloginFinished()
PLOGI << "Portal prelogin succeeded.";
preloginResponse = PreloginResponse::parse(reply->readAll());
PLOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region();
if (preloginResponse.hasSamlAuthFields()) {
// Do SAML authentication
samlAuth();
@@ -54,7 +61,7 @@ void PortalAuthenticator::onPreloginFinished()
tryAutoLogin();
} else {
PLOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse()));
emitFail("Unknown response for portal prelogin interface.");
emit preloginFailed("Unknown response for portal prelogin interface.");
}
delete reply;
@@ -124,9 +131,13 @@ void PortalAuthenticator::samlAuth()
void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult)
{
PLOGI << "SAML login succeeded, got the prelogin cookie " << samlResult.value("preloginCookie");
if (samlResult.contains("preloginCookie")) {
PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie");
} else {
PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
}
fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"));
fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie"));
}
void PortalAuthenticator::onSAMLLoginFail(const QString msg)
@@ -134,13 +145,14 @@ void PortalAuthenticator::onSAMLLoginFail(const QString msg)
emitFail(msg);
}
void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie)
void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie)
{
LoginParams params;
params.setServer(portal);
params.setUser(username);
params.setPassword(password);
params.setPreloginCookie(preloginCookie);
LoginParams loginParams { clientos };
loginParams.setServer(portal);
loginParams.setUser(username);
loginParams.setPassword(password);
loginParams.setPreloginCookie(preloginCookie);
loginParams.setUserAuthCookie(userAuthCookie);
// Save the username and password for future use.
this->username = username;
@@ -148,7 +160,7 @@ void PortalAuthenticator::fetchConfig(QString username, QString password, QStrin
PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username;
QNetworkReply *reply = createRequest(configUrl, params.toUtf8());
QNetworkReply *reply = createRequest(configUrl, loginParams.toUtf8());
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished);
}
@@ -167,27 +179,26 @@ void PortalAuthenticator::onFetchConfigFinished()
isAutoLogin = false;
normalAuth();
} else {
emitFail("Failed to fetch the portal config.");
emit portalConfigFailed("Failed to fetch the portal config.");
}
return;
}
PLOGI << "Fetch the portal config succeeded.";
PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll());
// Add the username & password to the response object
response.setUsername(username);
response.setPassword(password);
// Close the login window
if (normalLoginWindow) {
// Save the credentials for reuse
settings::save("username", username);
settings::save("password", password);
PLOGI << "Closing the NormalLoginWindow...";
normalLoginWindow->close();
}
emit success(response, filterPreferredGateway(response.allGateways(), preloginResponse.region()), response.allGateways());
emit success(response, preloginResponse.region());
}
void PortalAuthenticator::emitFail(const QString& msg)

View File

@@ -1,26 +1,28 @@
#ifndef PORTALAUTHENTICATOR_H
#define PORTALAUTHENTICATOR_H
#include <QtCore/QObject>
#include "portalconfigresponse.h"
#include "normalloginwindow.h"
#include "samlloginwindow.h"
#include "preloginresponse.h"
#include <QObject>
class PortalAuthenticator : public QObject
{
Q_OBJECT
public:
explicit PortalAuthenticator(const QString& portal);
explicit PortalAuthenticator(const QString& portal, const QString& clientos);
~PortalAuthenticator();
void authenticate();
signals:
void success(const PortalConfigResponse, const GPGateway, QList<GPGateway> allGateways);
void success(const PortalConfigResponse response, const QString region);
void fail(const QString& msg);
void preloginFailed(const QString& msg);
void portalConfigFailed(const QString msg);
private slots:
void onPreloginFinished();
@@ -33,6 +35,7 @@ private slots:
private:
QString portal;
QString clientos;
QString preloginUrl;
QString configUrl;
QString username;
@@ -47,7 +50,7 @@ private:
void tryAutoLogin();
void normalAuth();
void samlAuth();
void fetchConfig(QString username, QString password, QString preloginCookie = "");
void fetchConfig(QString username, QString password, QString preloginCookie = "", QString userAuthCookie = "");
void emitFail(const QString& msg = "");
};

View File

@@ -1,8 +1,8 @@
#include "portalconfigresponse.h"
#include <QXmlStreamReader>
#include <QtCore/QXmlStreamReader>
#include <plog/Log.h>
#include "portalconfigresponse.h"
QString PortalConfigResponse::xmlUserAuthCookie = "portal-userauthcookie";
QString PortalConfigResponse::xmlPrelogonUserAuthCookie = "portal-prelogonuserauthcookie";
QString PortalConfigResponse::xmlGateways = "gateways";
@@ -15,8 +15,10 @@ PortalConfigResponse::~PortalConfigResponse()
{
}
PortalConfigResponse PortalConfigResponse::parse(const QByteArray& xml)
PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
{
PLOGI << "Start parsing the portal configuration...";
QXmlStreamReader xmlReader(xml);
PortalConfigResponse response;
response.setRawResponse(xml);
@@ -27,36 +29,50 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray& xml)
QString name = xmlReader.name().toString();
if (name == xmlUserAuthCookie) {
PLOGI << "Start reading " << name;
response.setUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlPrelogonUserAuthCookie) {
PLOGI << "Start reading " << name;
response.setPrelogonUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlGateways) {
response.setAllGateways(parseGateways(xmlReader));
}
}
PLOGI << "Finished parsing portal configuration.";
return response;
}
const QByteArray& PortalConfigResponse::rawResponse() const
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
{
return _password;
return m_password;
}
QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader)
{
PLOGI << "Start parsing the gateways from portal configuration...";
QList<GPGateway> gateways;
while (xmlReader.name() != "external"){
xmlReader.readNext();
}
while (xmlReader.name() != "list"){
xmlReader.readNext();
}
while (xmlReader.name() != xmlGateways || !xmlReader.isEndElement()) {
xmlReader.readNext();
// Parse the gateways -> external -> list -> entry
@@ -69,81 +85,94 @@ QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader
gateways.append(g);
}
}
PLOGI << "Finished parsing the gateways.";
return gateways;
}
QMap<QString, int> PortalConfigResponse::parsePriorityRules(QXmlStreamReader &xmlReader)
{
PLOGI << "Start parsing the priority rules...";
QMap<QString, int> priorityRules;
while (xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) {
while ((xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) && !xmlReader.hasError()) {
xmlReader.readNext();
if (xmlReader.name() == "entry" && xmlReader.isStartElement()) {
QString ruleName = xmlReader.attributes().value("name").toString();
// Read the priority tag
xmlReader.readNextStartElement();
while (xmlReader.name() != "priority"){
xmlReader.readNext();
}
int ruleValue = xmlReader.readElementText().toUInt();
priorityRules.insert(ruleName, ruleValue);
}
}
PLOGI << "Finished parsing the priority rules.";
return priorityRules;
}
QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader)
{
while (xmlReader.name() != "description" || !xmlReader.isEndElement()) {
xmlReader.readNext();
if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) {
return xmlReader.readElementText();
}
}
PLOGI << "Start parsing the gateway name...";
PLOGE << "Error: <description> tag not found";
return "";
while (xmlReader.name() != "description" || !xmlReader.isEndElement()) {
xmlReader.readNext();
if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) {
PLOGI << "Finished parsing the gateway name";
return xmlReader.readElementText();
}
}
PLOGE << "Error: <description> tag not found";
return "";
}
QString PortalConfigResponse::userAuthCookie() const
{
return _userAuthCookie;
return m_userAuthCookie;
}
QString PortalConfigResponse::prelogonUserAuthCookie() const
{
return _prelogonAuthCookie;
return m_prelogonAuthCookie;
}
QList<GPGateway> PortalConfigResponse::allGateways()
QList<GPGateway> PortalConfigResponse::allGateways() const
{
return _gateways;
return m_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;
}

View File

@@ -1,11 +1,11 @@
#ifndef PORTALCONFIGRESPONSE_H
#define PORTALCONFIGRESPONSE_H
#include "gpgateway.h"
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QXmlStreamReader>
#include <QString>
#include <QList>
#include <QXmlStreamReader>
#include "gpgateway.h"
class PortalConfigResponse
{
@@ -13,35 +13,35 @@ public:
PortalConfigResponse();
~PortalConfigResponse();
static PortalConfigResponse parse(const QByteArray& xml);
static PortalConfigResponse parse(const QByteArray xml);
const QByteArray& rawResponse() const;
QString username() const;
const QByteArray rawResponse() const;
const QString &username() const;
QString password() const;
QString userAuthCookie() const;
QString prelogonUserAuthCookie() const;
QList<GPGateway> allGateways();
QList<GPGateway> allGateways() const;
void setAllGateways(QList<GPGateway> gateways);
void setUsername(const QString& username);
void setPassword(const QString& password);
void setUsername(const QString username);
void setPassword(const QString password);
private:
static QString xmlUserAuthCookie;
static QString xmlPrelogonUserAuthCookie;
static QString xmlGateways;
QByteArray _rawResponse;
QString _username;
QString _password;
QString _userAuthCookie;
QString _prelogonAuthCookie;
QByteArray m_rawResponse;
QString m_username;
QString m_password;
QString m_userAuthCookie;
QString m_prelogonAuthCookie;
QList<GPGateway> _gateways;
QList<GPGateway> m_gateways;
void setRawResponse(const QByteArray& response);
void setUserAuthCookie(const QString& cookie);
void setPrelogonUserAuthCookie(const QString& cookie);
void setRawResponse(const QByteArray response);
void setUserAuthCookie(const QString cookie);
void setPrelogonUserAuthCookie(const QString cookie);
static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader);
static QMap<QString, int> parsePriorityRules(QXmlStreamReader &xmlReader);

View File

@@ -1,7 +1,8 @@
#include "preloginresponse.h"
#include <QtCore/QXmlStreamReader>
#include <QtCore/QMap>
#include <plog/Log.h>
#include <QXmlStreamReader>
#include <QMap>
#include "preloginresponse.h"
QString PreloginResponse::xmlAuthMessage = "authentication-message";
QString PreloginResponse::xmlLabelUsername = "username-label";
@@ -22,6 +23,8 @@ PreloginResponse::PreloginResponse()
PreloginResponse PreloginResponse::parse(const QByteArray& xml)
{
PLOGI << "Start parsing the prelogin response...";
QXmlStreamReader xmlReader(xml);
PreloginResponse response;
response.setRawResponse(xml);
@@ -81,17 +84,17 @@ bool PreloginResponse::hasNormalAuthFields() const
return !labelUsername().isEmpty() && !labelPassword().isEmpty();
}
void PreloginResponse::setRawResponse(const QByteArray &response)
void PreloginResponse::setRawResponse(const QByteArray response)
{
_rawResponse = response;
}
bool PreloginResponse::has(const QString &name) const
bool PreloginResponse::has(const QString name) const
{
return resultMap.contains(name);
}
void PreloginResponse::add(const QString &name, const QString &value)
void PreloginResponse::add(const QString name, const QString value)
{
resultMap.insert(name, value);
}

View File

@@ -1,8 +1,8 @@
#ifndef PRELOGINRESPONSE_H
#define PRELOGINRESPONSE_H
#include <QString>
#include <QMap>
#include <QtCore/QString>
#include <QtCore/QMap>
class PreloginResponse
{
@@ -33,9 +33,9 @@ private:
QMap<QString, QString> resultMap;
QByteArray _rawResponse;
void setRawResponse(const QByteArray &response);
void add(const QString &name, const QString &value);
bool has(const QString &name) const;
void setRawResponse(const QByteArray response);
void add(const QString name, const QString value);
bool has(const QString name) const;
};
#endif // PRELOGINRESPONSE_H

View File

@@ -1,10 +1,11 @@
<RCC>
<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>pending.png</file>
<file>not_connected.png</file>
<file>radio_unselected.png</file>
<file>radio_selected.png</file>
<file>settings_icon.png</file>
</qresource>
</RCC>

View File

@@ -1,15 +1,15 @@
#include "samlloginwindow.h"
#include <QVBoxLayout>
#include <QtWidgets/QVBoxLayout>
#include <QtWebEngineWidgets/QWebEngineProfile>
#include <QtWebEngineWidgets/QWebEngineView>
#include <plog/Log.h>
#include <QWebEngineProfile>
#include <QWebEngineView>
#include "samlloginwindow.h"
SAMLLoginWindow::SAMLLoginWindow(QWidget *parent)
: QDialog(parent)
, webView(new EnhancedWebView(this))
{
setWindowTitle("GlobalProtect SAML Login");
setWindowTitle("GlobalProtect Login");
setModal(true);
resize(700, 550);
@@ -59,22 +59,41 @@ void SAMLLoginWindow::onResponseReceived(QJsonObject params)
const QString username = headers.value("saml-username").toString();
const QString preloginCookie = headers.value("prelogin-cookie").toString();
const QString userAuthCookie = headers.value("portal-userauthcookie").toString();
if (!username.isEmpty() && !preloginCookie.isEmpty()) {
LOGI << "Response received from " << response.value("url").toString();
if (!username.isEmpty()) {
LOGI << "Got username from SAML response headers " << username;
samlResult.insert("username", username);
}
if (!preloginCookie.isEmpty()) {
LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie;
samlResult.insert("preloginCookie", preloginCookie);
}
}
void SAMLLoginWindow::onLoadFinished()
{
LOGI << "Load finished " << this->webView->page()->url().toString();
if (!userAuthCookie.isEmpty()) {
LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie;
samlResult.insert("userAuthCookie", userAuthCookie);
}
// Check the SAML result
if (!samlResult.value("username").isEmpty() && !samlResult.value("preloginCookie").isEmpty()) {
if (samlResult.contains("username")
&& (samlResult.contains("preloginCookie") || samlResult.contains("userAuthCookie"))) {
LOGI << "Got the SAML authentication information successfully. "
<< "username: " << samlResult.value("username")
<< ", preloginCookie: " << samlResult.value("preloginCookie")
<< ", userAuthCookie: " << samlResult.value("userAuthCookie");
emit success(samlResult);
accept();
} else {
this->show();
}
}
void SAMLLoginWindow::onLoadFinished()
{
LOGI << "Load finished " << this->webView->page()->url().toString();
}

View File

@@ -1,11 +1,11 @@
#ifndef SAMLLOGINWINDOW_H
#define SAMLLOGINWINDOW_H
#include "enhancedwebview.h"
#include <QtCore/QMap>
#include <QtGui/QCloseEvent>
#include <QtWidgets/QDialog>
#include <QDialog>
#include <QMap>
#include <QCloseEvent>
#include "enhancedwebview.h"
class SAMLLoginWindow : public QDialog
{

BIN
GPClient/settings_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,34 @@
#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();
}

28
GPClient/settingsdialog.h Normal file
View File

@@ -0,0 +1,28 @@
#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();
private:
Ui::SettingsDialog *ui;
};
#endif // SETTINGSDIALOG_H

107
GPClient/settingsdialog.ui Normal file
View File

@@ -0,0 +1,107 @@
<?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>177</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 &quot;/etc/gpservice/gp.conf&quot;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Value of &quot;clientos&quot;:</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="2" 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>
</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>

24
GPClient/vpn.h Normal file
View 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
View 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
View 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
View 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
View 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

77
GPService/CMakeLists.txt Normal file
View File

@@ -0,0 +1,77 @@
include("${CMAKE_SOURCE_DIR}/cmake/Add3rdParty.cmake")
project(GPService)
set(gpservice_GENERATED_SOURCES)
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 "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()

View File

@@ -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

View File

@@ -1,5 +1,5 @@
[D-BUS Service]
Name=com.yuezk.qt.GPService
Exec=/usr/bin/gpservice
Exec=@CMAKE_INSTALL_PREFIX@/bin/gpservice
User=root
SystemdService=gpservice.service

17
GPService/gp.conf Normal file
View 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=

View File

@@ -1,10 +1,13 @@
#include "gpservice.h"
#include "gpservice_adaptor.h"
#include <QtCore/QFileInfo>
#include <QtCore/QDateTime>
#include <QtCore/QVariant>
#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
#include <QtDBus/QtDBus>
#include <QFileInfo>
#include <QtDBus>
#include <QDateTime>
#include <QVariant>
#include "INIReader.h"
#include "gpservice.h"
#include "gpserviceadaptor.h"
GPService::GPService(QObject *parent)
: QObject(parent)
@@ -39,6 +42,61 @@ QString GPService::findBinary()
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()
{
if (openconnect->state() == QProcess::NotRunning) {
@@ -58,18 +116,56 @@ void GPService::connect(QString server, QString username, QString passwd)
QString bin = findBinary();
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;
}
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;
args << QCoreApplication::arguments().mid(1)
<< "--protocol=gp"
<< "-u" << username
<< "-C" << passwd
<< server;
<< "--protocol=gp"
<< splitCommand(extraArgs)
<< "-u" << username
<< "--cookie-on-stdin"
<< server;
log("Start process with arugments: " + args.join(" "));
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()
@@ -103,7 +199,7 @@ void GPService::onProcessStdout()
QString output = openconnect->readAllStandardOutput();
log(output);
if (output.indexOf("Connected as") >= 0) {
if (output.indexOf("Connected as") >= 0 || output.indexOf("Configured as") >= 0) {
vpnStatus = GPService::VpnConnected;
emit connected();
}

View File

@@ -1,8 +1,8 @@
#ifndef GLOBALPROTECTSERVICE_H
#define GLOBALPROTECTSERVICE_H
#include <QObject>
#include <QProcess>
#include <QtCore/QObject>
#include <QtCore/QProcess>
static const QString binaryPaths[] {
"/usr/local/bin/openconnect",
@@ -21,6 +21,8 @@ public:
explicit GPService(QObject *parent = nullptr);
~GPService();
void quit();
enum VpnStatus {
VpnNotConnected,
VpnConnecting,
@@ -31,13 +33,13 @@ public:
signals:
void connected();
void disconnected();
void error(QString errorMessage);
void logAvailable(QString log);
public slots:
void connect(QString server, QString username, QString passwd);
void disconnect();
int status();
void quit();
private slots:
void onProcessStarted();
@@ -52,7 +54,10 @@ private:
int vpnStatus = GPService::VpnNotConnected;
void log(QString msg);
bool isValidVersion(QString &bin);
static QString findBinary();
static QString extraOpenconnectArgs(const QString &gateway);
static QStringList splitCommand(const QString &command);
};
#endif // GLOBALPROTECTSERVICE_H

View File

@@ -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>

View File

@@ -1,4 +1,5 @@
#include <QtDBus>
#include <QtDBus/QtDBus>
#include "gpservice.h"
#include "singleapplication.h"
#include "sigwatch.h"

View File

@@ -5,7 +5,7 @@ Description=GlobalProtect openconnect DBus service
Environment="LANG=en_US.utf8"
Type=dbus
BusName=com.yuezk.qt.GPService
ExecStart=/usr/bin/gpservice
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/gpservice
[Install]
WantedBy=multi-user.target

View File

@@ -1,5 +0,0 @@
TEMPLATE = subdirs
SUBDIRS += \
GPClient \
GPService

202
README.md
View File

@@ -2,56 +2,192 @@
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">
<img src="screenshot.png">
<img src="https://user-images.githubusercontent.com/3297602/133869036-5c02b0d9-c2d9-4f87-8c81-e44f68cfd6ac.png">
</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
- Similar user experience as the offical client in macOS.
- Similar user experience as the official client in macOS.
- Supports both SAML and non-SAML authentication modes.
- Supports automatically selecting the preferred gateway from the multiple gateways.
- Supports switching gateway 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 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
[![Arch package](https://repology.org/badge/version-for-repo/arch/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![AUR package](https://repology.org/badge/version-for-repo/aur/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![Manjaro Stable package](https://repology.org/badge/version-for-repo/manjaro_stable/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![Manjaro Testing package](https://repology.org/badge/version-for-repo/manjaro_testing/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![Manjaro Unstable package](https://repology.org/badge/version-for-repo/manjaro_unstable/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/globalprotect-openconnect.svg)](https://repology.org/project/globalprotect-openconnect/versions)
[![Parabola package](https://repology.org/badge/version-for-repo/parabola/globalprotect-openconnect.svg)](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 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/openSUSE_Leap_15.2/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
git clone https://github.com/yuezk/GlobalProtect-openconnect.git
cd GlobalProtect-openconnect
git submodule update --init
# qmake or qmake-qt5
qmake CONFIG+=release
make
sudo make install
```
Open `GlobalProtect VPN` in the application dashboard.
### 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 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
...then build and install with:
```sh
./scripts/install.sh
```
### NixOS
In `configuration.nix`:
```
services.globalprotect = {
enable = true;
# if you need a Host Integrity Protection report
csdWrapper = "${pkgs.openconnect}/libexec/openconnect/hipreport.sh";
};
environment.systemPackages = [ globalprotect-openconnect ];
```
## Run
Once the software is installed, you can run `gpclient` to start the UI.
## Passing the Custom Parameters to `OpenConnect` CLI
See [Configuration](https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration)
## 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).
<p align="center">
<img src="https://user-images.githubusercontent.com/3297602/130831022-b93492fd-46dd-4a8e-94a4-13b5747120b7.png" />
<p>
## Future plan
- [x] Improve the release process
- [ ] Process bugs and feature requests
- [ ] Support for bypassing the `gpclient` parameters
- [ ] Support the CLI mode
## Troubleshooting
Run `gpclient` in the Terminal and collect the logs.
## [License](./LICENSE)
GPLv3

1
VERSION Normal file
View File

@@ -0,0 +1 @@
1.4.4

27
cmake/Add3rdParty.cmake Normal file
View 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()

View 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
View 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 neccessary
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" "$@"

5
debian/README.Debian vendored Normal file
View File

@@ -0,0 +1,5 @@
globalprotect-openconnect for Debian
Added debian packaging
-- Amit Joshi <> Fri, 29 May 2020 21:52:59 -0400

69
debian/changelog vendored Normal file
View File

@@ -0,0 +1,69 @@
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
-- Kevin Yue <k3vinyue@gmail.com> Thu, 09 Jul 2020 10:13:46 +0800

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
11

13
debian/control vendored Normal file
View File

@@ -0,0 +1,13 @@
Source: globalprotect-openconnect
Section: net
Priority: optional
Maintainer: Kevin Yue <k3vinyue@gmail.com>
Build-Depends: cmake (>=3.10), debhelper (>=11~), qtbase5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9)
Standards-Version: 4.1.4
Homepage: https://github.com/yuezk/GlobalProtect-openconnect
Package: globalprotect-openconnect
Architecture: any
Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9)
Description: A GlobalProtect VPN client (GUI) based on OpenConnect.

15
debian/copyright vendored Normal file
View File

@@ -0,0 +1,15 @@
Files: *
Copyright: 1975-present Kevin Yue
License: GPL-3+
Files: 3rdparty/plog
Copyright: 2016 Sergey Podobry (sergey.podobry at gmail.com)
License: MPL-2.0
Files: 3rdparty/qt-unix-signals
Copyright: 2014 Simon Knopp
License: MIT
Files: 3rdparty/SingleApplication
Copyright: Itay Grudev 2015 - 2020
License: MIT

1
debian/patches/series vendored Normal file
View File

@@ -0,0 +1 @@
# You must remove unused comment lines for the released package.

13
debian/rules vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/make -f
# You must remove unused comment lines for the released package.
export DH_VERBOSE = 1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
export DEBIAN_PACKAGE=1
%:
dh $@
override_dh_installsystemd:
dh_installsystemd gpservice.service

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
3.0 (native)

2
debian/source/local-options vendored Normal file
View File

@@ -0,0 +1,2 @@
#abort-on-upstream-changes
#unapply-patches

3
debian/watch vendored Normal file
View File

@@ -0,0 +1,3 @@
version=4
opts="mode=git,pgpmode=gittag" \
https://github.com/yuezk/GlobalProtect-openconnect.git refs/tags/v([\d\.]+)

40
packaging/aur/PKGBUILD Normal file
View File

@@ -0,0 +1,40 @@
# Maintainer: Keinv Yue <yuezk001@gmail.com>
_pkgver="1.4.4"
_commit="4327235093159c6569af33021d4c763ebea3787a"
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)
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
View 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)
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
View File

@@ -0,0 +1,8 @@
post_install() {
systemctl enable gpservice.service
systemctl restart gpservice.service
}
post_upgrade() {
post_install
}

View File

@@ -0,0 +1,26 @@
app-id: com.yuezk.qt.gpclient
base: io.qt.qtwebengine.BaseApp
base-version: '5.15'
runtime: org.kde.Platform
runtime-version: '5.15'
sdk: org.kde.Sdk
command: gpclient
finish-args:
- --share=network
- --share=ipc
- --socket=x11
- --socket=wayland
- --filesystem=host
- --device=dri
- --talk-name=org.kde.StatusNotifierWatcher
- --own-name=org.kde.*
- --system-own-name=com.yuezk.qt.GPService
modules:
- name: gpclient
buildsystem: cmake
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_CXX_FLAGS_RELEASE=-s
sources:
- type: archive
path: globalprotect-openconnect.tar.gz

Some files were not shown because too many files have changed in this diff Show More