Compare commits

..

32 Commits

Author SHA1 Message Date
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
33 changed files with 435 additions and 98 deletions

View File

@@ -1,4 +1,4 @@
name: CI name: Build
on: on:
push: push:
@@ -16,7 +16,7 @@ jobs:
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v2 uses: jurplel/install-qt-action@v2
with: with:
version: '5.15.2' version: 5.12.11
modules: 'qtwebengine qtwebsockets' modules: 'qtwebengine qtwebsockets'
# Checkout repository and submodules # Checkout repository and submodules

60
.github/workflows/pre-release.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Pre Release
on:
workflow_dispatch:
jobs:
pre-release:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
DEBFULLNAME: "Kevin Yue"
DEBEMAIL: "yuezk001@gmail.com"
steps:
# Checkout repository and submodules
- uses: actions/checkout@v2
with:
submodules: recursive
fetch-depth: 0
- name: Init variables
id: vars
run: |
TAG=$(git tag --sort=-v:refname --list "v[0-9]*" | head -n 1 | cut -c 2-)
echo ::set-output name=VERSION::"${TAG}+SNAPSHOT$(date -u +"%Y%m%d%H%M%S")"
echo ::set-output name=TAG::${TAG}
- name: Update debian/changelog
run: |
sudo apt install devscripts
git log --format="%s" v${{ steps.vars.outputs.TAG }}.. | xargs -L1 dch -v ${{ steps.vars.outputs.VERSION }}-1ppa1
- name: "Archive all"
run: |
python -m pip install --upgrade pip
pip install git-archive-all
git-archive-all \
--force-submodules \
--prefix=globalprotect-openconnect-${{ steps.vars.outputs.VERSION }}/ \
./globalprotect-openconnect-${{ steps.vars.outputs.VERSION }}.full.tar.gz
- name: "Debian Packaging"
run: |
sudo apt update
sudo apt install qtbase5-dev libqt5websockets5-dev qtwebengine5-dev qttools5-dev debhelper
mkdir build-debian && cd build-debian
cp ../*.tar.gz globalprotect-openconnect_${{ steps.vars.outputs.VERSION }}.orig.tar.gz
tar xf *.tar.gz
cd globalprotect-openconnect-${{ steps.vars.outputs.VERSION }}
fakeroot dpkg-buildpackage -uc -us -sa
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: true
title: "globalprotect-openconnect_${{ steps.vars.outputs.VERSION }}"
files: |
*.tar.gz
build-debian/*.deb

View File

@@ -11,7 +11,7 @@ jobs:
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v2 uses: jurplel/install-qt-action@v2
with: with:
version: 5.15.2 version: 5.12.11
modules: 'qtwebengine qtwebsockets' modules: 'qtwebengine qtwebsockets'
# Checkout repository and submodules # Checkout repository and submodules

5
.gitignore vendored
View File

@@ -2,6 +2,11 @@
gpclient gpclient
gpservice gpservice
*.rpm
*.gz
.DS_Store
build-debian
# Auto generated DBus files # Auto generated DBus files
*_adaptor.cpp *_adaptor.cpp
*_adaptor.h *_adaptor.h

View File

@@ -26,6 +26,7 @@ SOURCES += \
cdpcommandmanager.cpp \ cdpcommandmanager.cpp \
enhancedwebview.cpp \ enhancedwebview.cpp \
gatewayauthenticator.cpp \ gatewayauthenticator.cpp \
gatewayauthenticatorparams.cpp \
gpgateway.cpp \ gpgateway.cpp \
gphelper.cpp \ gphelper.cpp \
loginparams.cpp \ loginparams.cpp \
@@ -43,6 +44,7 @@ HEADERS += \
cdpcommandmanager.h \ cdpcommandmanager.h \
enhancedwebview.h \ enhancedwebview.h \
gatewayauthenticator.h \ gatewayauthenticator.h \
gatewayauthenticatorparams.h \
gpgateway.h \ gpgateway.h \
gphelper.h \ gphelper.h \
loginparams.h \ loginparams.h \

View File

@@ -8,12 +8,16 @@
using namespace gpclient::helper; using namespace gpclient::helper;
GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const PortalConfigResponse& portalConfig) GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const GatewayAuthenticatorParams params)
: QObject() : QObject()
, preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") , gateway(gateway)
, params(params)
, preloginUrl("https://" + gateway + "/ssl-vpn/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100")
, loginUrl("https://" + gateway + "/ssl-vpn/login.esp") , loginUrl("https://" + gateway + "/ssl-vpn/login.esp")
, portalConfig(portalConfig)
{ {
if (!params.clientos().isEmpty()) {
preloginUrl = preloginUrl + "&clientos=" + params.clientos();
}
} }
GatewayAuthenticator::~GatewayAuthenticator() GatewayAuthenticator::~GatewayAuthenticator()
@@ -25,19 +29,19 @@ void GatewayAuthenticator::authenticate()
{ {
PLOGI << "Start gateway authentication..."; PLOGI << "Start gateway authentication...";
LoginParams params; LoginParams loginParams { params.clientos() };
params.setUser(portalConfig.username()); loginParams.setUser(params.username());
params.setPassword(portalConfig.password()); loginParams.setPassword(params.password());
params.setUserAuthCookie(portalConfig.userAuthCookie()); loginParams.setUserAuthCookie(params.userAuthCookie());
login(params); 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); connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished);
} }
@@ -124,10 +128,11 @@ void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const Q
PLOGI << "Start to perform normal login..."; PLOGI << "Start to perform normal login...";
normalLoginWindow->setProcessing(true); normalLoginWindow->setProcessing(true);
LoginParams params; LoginParams loginParams { params.clientos() };
params.setUser(username); loginParams.setUser(username);
params.setPassword(password); loginParams.setPassword(password);
login(params);
login(loginParams);
} }
void GatewayAuthenticator::onLoginWindowRejected() void GatewayAuthenticator::onLoginWindowRejected()
@@ -162,12 +167,12 @@ void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &saml
PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
} }
LoginParams params; LoginParams loginParams { params.clientos() };
params.setUser(samlResult.value("username")); loginParams.setUser(samlResult.value("username"));
params.setPreloginCookie(samlResult.value("preloginCookie")); loginParams.setPreloginCookie(samlResult.value("preloginCookie"));
params.setUserAuthCookie(samlResult.value("userAuthCookie")); loginParams.setUserAuthCookie(samlResult.value("userAuthCookie"));
login(params); login(loginParams);
} }
void GatewayAuthenticator::onSAMLLoginFail(const QString msg) void GatewayAuthenticator::onSAMLLoginFail(const QString msg)

View File

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

View File

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

View File

@@ -0,0 +1,33 @@
#ifndef GATEWAYAUTHENTICATORPARAMS_H
#define GATEWAYAUTHENTICATORPARAMS_H
#include <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);
private:
QString m_username;
QString m_password;
QString m_userAuthCookie;
QString m_clientos;
};
#endif // GATEWAYAUTHENTICATORPARAMS_H

View File

@@ -4,6 +4,7 @@
#include "portalauthenticator.h" #include "portalauthenticator.h"
#include "gatewayauthenticator.h" #include "gatewayauthenticator.h"
#include "settingsdialog.h" #include "settingsdialog.h"
#include "gatewayauthenticatorparams.h"
#include <plog/Log.h> #include <plog/Log.h>
#include <QIcon> #include <QIcon>
@@ -30,6 +31,7 @@ GPClient::GPClient(QWidget *parent)
vpn = new com::yuezk::qt::GPService("com.yuezk.qt.GPService", "/", QDBusConnection::systemBus(), this); 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::connected, this, &GPClient::onVPNConnected);
connect(vpn, &com::yuezk::qt::GPService::disconnected, this, &GPClient::onVPNDisconnected); connect(vpn, &com::yuezk::qt::GPService::disconnected, this, &GPClient::onVPNDisconnected);
connect(vpn, &com::yuezk::qt::GPService::error, this, &GPClient::onVPNError);
connect(vpn, &com::yuezk::qt::GPService::logAvailable, this, &GPClient::onVPNLogAvailable); connect(vpn, &com::yuezk::qt::GPService::logAvailable, this, &GPClient::onVPNLogAvailable);
// Initiallize the context menu of system tray. // Initiallize the context menu of system tray.
@@ -48,7 +50,7 @@ GPClient::~GPClient()
void GPClient::setupSettings() void GPClient::setupSettings()
{ {
settingsButton = new QPushButton(this); settingsButton = new QPushButton(this);
settingsButton->setIcon(QIcon(":/images/settings_icon.svg")); settingsButton->setIcon(QIcon(":/images/settings_icon.png"));
settingsButton->setFixedSize(QSize(28, 28)); settingsButton->setFixedSize(QSize(28, 28));
QRect rect = this->geometry(); QRect rect = this->geometry();
@@ -66,12 +68,14 @@ void GPClient::setupSettings()
void GPClient::onSettingsButtonClicked() void GPClient::onSettingsButtonClicked()
{ {
settingsDialog->setExtraArgs(settings::get("extraArgs", "").toString()); settingsDialog->setExtraArgs(settings::get("extraArgs", "").toString());
settingsDialog->setClientos(settings::get("clientos", "Linux").toString());
settingsDialog->show(); settingsDialog->show();
} }
void GPClient::onSettingsAccepted() void GPClient::onSettingsAccepted()
{ {
settings::save("extraArgs", settingsDialog->extraArgs()); settings::save("extraArgs", settingsDialog->extraArgs());
settings::save("clientos", settingsDialog->clientos());
} }
void GPClient::on_connectButton_clicked() void GPClient::on_connectButton_clicked()
@@ -274,7 +278,7 @@ void GPClient::doConnect()
// Login to the portal interface to get the portal config and preferred gateway // Login to the portal interface to get the portal config and preferred gateway
void GPClient::portalLogin() void GPClient::portalLogin()
{ {
PortalAuthenticator *portalAuth = new PortalAuthenticator(portal()); PortalAuthenticator *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString());
connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess); connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess);
// Prelogin failed on the portal interface, try to treat the portal as a gateway interface // Prelogin failed on the portal interface, try to treat the portal as a gateway interface
@@ -351,7 +355,10 @@ void GPClient::gatewayLogin()
{ {
PLOGI << "Performing gateway login..."; PLOGI << "Performing gateway login...";
GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), portalConfig); 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::success, this, &GPClient::onGatewaySuccess);
connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail); connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail);
@@ -471,6 +478,12 @@ void GPClient::onVPNDisconnected()
} }
} }
void GPClient::onVPNError(QString errorMessage)
{
updateConnectionStatus(VpnStatus::disconnected);
openMessageBox("Failed to connect", errorMessage);
}
void GPClient::onVPNLogAvailable(QString log) void GPClient::onVPNLogAvailable(QString log)
{ {
PLOGI << log; PLOGI << log;

View File

@@ -45,6 +45,7 @@ private slots:
void onVPNConnected(); void onVPNConnected();
void onVPNDisconnected(); void onVPNDisconnected();
void onVPNError(QString errorMessage);
void onVPNLogAvailable(QString log); void onVPNLogAvailable(QString log);
private: private:

View File

@@ -116,7 +116,13 @@ void gpclient::helper::settings::save(const QString &key, const QVariant &value)
_settings->setValue(key, value); _settings->setValue(key, value);
} }
void gpclient::helper::settings::clear() void gpclient::helper::settings::clear()
{ {
_settings->clear(); QStringList keys = _settings->allKeys();
for (const auto &key : qAsConst(keys)) {
if (!reservedKeys.contains(key)) {
_settings->remove(key);
}
}
} }

View File

@@ -31,6 +31,7 @@ namespace gpclient {
namespace settings { namespace settings {
extern QSettings *_settings; extern QSettings *_settings;
static const QStringList reservedKeys {"extraArgs", "clientos"};
QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); QVariant get(const QString &key, const QVariant &defaultValue = QVariant());
void save(const QString &key, const QVariant &value); void save(const QString &key, const QVariant &value);

View File

@@ -2,7 +2,7 @@
#include <QUrlQuery> #include <QUrlQuery>
LoginParams::LoginParams() LoginParams::LoginParams(const QString clientos)
{ {
params.addQueryItem("prot", QUrl::toPercentEncoding("https:")); params.addQueryItem("prot", QUrl::toPercentEncoding("https:"));
params.addQueryItem("server", ""); params.addQueryItem("server", "");
@@ -15,7 +15,12 @@ LoginParams::LoginParams()
params.addQueryItem("direct", "yes"); params.addQueryItem("direct", "yes");
params.addQueryItem("clientVer", "4100"); params.addQueryItem("clientVer", "4100");
params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName())); params.addQueryItem("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-userauthcookie", "");
params.addQueryItem("portal-prelogonuserauthcookie", ""); params.addQueryItem("portal-prelogonuserauthcookie", "");
params.addQueryItem("prelogin-cookie", ""); params.addQueryItem("prelogin-cookie", "");

View File

@@ -6,7 +6,7 @@
class LoginParams class LoginParams
{ {
public: public:
LoginParams(); LoginParams(const QString clientos);
~LoginParams(); ~LoginParams();
void setUser(const QString user); void setUser(const QString user);

View File

@@ -6,7 +6,7 @@
#include <plog/Log.h> #include <plog/Log.h>
#include <plog/Appenders/ColorConsoleAppender.h> #include <plog/Appenders/ColorConsoleAppender.h>
static const QString version = "v1.3.0"; static const QString version = "v1.3.3";
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

View File

@@ -12,11 +12,15 @@
using namespace gpclient::helper; using namespace gpclient::helper;
PortalAuthenticator::PortalAuthenticator(const QString& portal) : QObject() PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& clientos) : QObject()
, portal(portal) , portal(portal)
, preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100&clientos=Linux") , clientos(clientos)
, preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100")
, configUrl("https://" + portal + "/global-protect/getconfig.esp") , configUrl("https://" + portal + "/global-protect/getconfig.esp")
{ {
if (!clientos.isEmpty()) {
preloginUrl = preloginUrl + "&clientos=" + clientos;
}
} }
PortalAuthenticator::~PortalAuthenticator() PortalAuthenticator::~PortalAuthenticator()
@@ -143,12 +147,12 @@ void PortalAuthenticator::onSAMLLoginFail(const QString msg)
void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie) void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie)
{ {
LoginParams params; LoginParams loginParams { clientos };
params.setServer(portal); loginParams.setServer(portal);
params.setUser(username); loginParams.setUser(username);
params.setPassword(password); loginParams.setPassword(password);
params.setPreloginCookie(preloginCookie); loginParams.setPreloginCookie(preloginCookie);
params.setUserAuthCookie(userAuthCookie); loginParams.setUserAuthCookie(userAuthCookie);
// Save the username and password for future use. // Save the username and password for future use.
this->username = username; this->username = username;
@@ -156,7 +160,7 @@ void PortalAuthenticator::fetchConfig(QString username, QString password, QStrin
PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username; 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); connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished);
} }

View File

@@ -12,7 +12,7 @@ class PortalAuthenticator : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PortalAuthenticator(const QString& portal); explicit PortalAuthenticator(const QString& portal, const QString& clientos);
~PortalAuthenticator(); ~PortalAuthenticator();
void authenticate(); void authenticate();
@@ -34,6 +34,7 @@ private slots:
private: private:
QString portal; QString portal;
QString clientos;
QString preloginUrl; QString preloginUrl;
QString configUrl; QString configUrl;
QString username; QString username;

View File

@@ -46,17 +46,17 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
const QByteArray PortalConfigResponse::rawResponse() const const QByteArray PortalConfigResponse::rawResponse() const
{ {
return _rawResponse; return m_rawResponse;
} }
QString PortalConfigResponse::username() const const QString &PortalConfigResponse::username() const
{ {
return _username; return m_username;
} }
QString PortalConfigResponse::password() const QString PortalConfigResponse::password() const
{ {
return _password; return m_password;
} }
QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader)
@@ -134,45 +134,45 @@ QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader)
QString PortalConfigResponse::userAuthCookie() const QString PortalConfigResponse::userAuthCookie() const
{ {
return _userAuthCookie; return m_userAuthCookie;
} }
QString PortalConfigResponse::prelogonUserAuthCookie() const QString PortalConfigResponse::prelogonUserAuthCookie() const
{ {
return _prelogonAuthCookie; return m_prelogonAuthCookie;
} }
QList<GPGateway> PortalConfigResponse::allGateways() const QList<GPGateway> PortalConfigResponse::allGateways() const
{ {
return _gateways; return m_gateways;
} }
void PortalConfigResponse::setAllGateways(QList<GPGateway> gateways) void PortalConfigResponse::setAllGateways(QList<GPGateway> gateways)
{ {
_gateways = gateways; m_gateways = gateways;
} }
void PortalConfigResponse::setRawResponse(const QByteArray response) void PortalConfigResponse::setRawResponse(const QByteArray response)
{ {
_rawResponse = response; m_rawResponse = response;
} }
void PortalConfigResponse::setUsername(const QString username) void PortalConfigResponse::setUsername(const QString username)
{ {
_username = username; m_username = username;
} }
void PortalConfigResponse::setPassword(const QString password) void PortalConfigResponse::setPassword(const QString password)
{ {
_password = password; m_password = password;
} }
void PortalConfigResponse::setUserAuthCookie(const QString cookie) void PortalConfigResponse::setUserAuthCookie(const QString cookie)
{ {
_userAuthCookie = cookie; m_userAuthCookie = cookie;
} }
void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie) void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie)
{ {
_prelogonAuthCookie = cookie; m_prelogonAuthCookie = cookie;
} }

View File

@@ -16,7 +16,7 @@ public:
static PortalConfigResponse parse(const QByteArray xml); static PortalConfigResponse parse(const QByteArray xml);
const QByteArray rawResponse() const; const QByteArray rawResponse() const;
QString username() const; const QString &username() const;
QString password() const; QString password() const;
QString userAuthCookie() const; QString userAuthCookie() const;
QString prelogonUserAuthCookie() const; QString prelogonUserAuthCookie() const;
@@ -31,13 +31,13 @@ private:
static QString xmlPrelogonUserAuthCookie; static QString xmlPrelogonUserAuthCookie;
static QString xmlGateways; static QString xmlGateways;
QByteArray _rawResponse; QByteArray m_rawResponse;
QString _username; QString m_username;
QString _password; QString m_password;
QString _userAuthCookie; QString m_userAuthCookie;
QString _prelogonAuthCookie; QString m_prelogonAuthCookie;
QList<GPGateway> _gateways; QList<GPGateway> m_gateways;
void setRawResponse(const QByteArray response); void setRawResponse(const QByteArray response);
void setUserAuthCookie(const QString cookie); void setUserAuthCookie(const QString cookie);

View File

@@ -6,6 +6,6 @@
<file>not_connected.png</file> <file>not_connected.png</file>
<file>radio_unselected.png</file> <file>radio_unselected.png</file>
<file>radio_selected.png</file> <file>radio_selected.png</file>
<file>settings_icon.svg</file> <file>settings_icon.png</file>
</qresource> </qresource>
</RCC> </RCC>

BIN
GPClient/settings_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
<g>
<path d="M16.5,19.9C16.5,19.9,16.5,19.9,16.5,19.9l3.1-3.1c0,0,0,0,0,0l2.3-2.3c2.2,0.6,4.5,0,6.2-1.6c1.8-1.8,2.3-4.4,1.4-6.8
c-0.1-0.3-0.4-0.5-0.7-0.6c-0.3-0.1-0.7,0-0.9,0.3L25.6,8l-1.3-0.3L24,6.4l2.2-2.2c0.2-0.2,0.3-0.6,0.3-0.9
c-0.1-0.3-0.3-0.6-0.6-0.7c-2.3-0.9-5-0.4-6.8,1.4c-1.6,1.6-2.2,4-1.6,6.2l-1.6,1.6l-2.6-2.6L11,5.3c-0.1-0.1-0.2-0.3-0.3-0.3
L6.8,2.7C6.4,2.4,5.9,2.5,5.5,2.8L2.5,5.9C2.1,6.2,2.1,6.7,2.3,7.1L4.6,11c0.1,0.1,0.2,0.3,0.3,0.3l3.7,2.2l2.6,2.6l-1.2,1.2
c-2.2-0.6-4.5,0-6.2,1.6c-1.8,1.8-2.3,4.4-1.4,6.8c0.1,0.3,0.4,0.5,0.7,0.6c0.3,0.1,0.7,0,0.9-0.3L6.4,24l1.3,0.3L8,25.6l-2.2,2.2
c-0.2,0.2-0.3,0.6-0.3,0.9c0.1,0.3,0.3,0.6,0.6,0.7c0.8,0.3,1.5,0.4,2.3,0.4c1.6,0,3.3-0.6,4.5-1.9c1.6-1.6,2.2-4,1.6-6.2
L16.5,19.9z"/>
<path d="M22.5,16.8l-6,6l6.1,6.1c0.8,0.8,1.9,1.3,3,1.3s2.2-0.4,3-1.3c0.8-0.8,1.3-1.9,1.3-3c0-1.1-0.4-2.2-1.3-3L22.5,16.8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -6,8 +6,6 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
ui(new Ui::SettingsDialog) ui(new Ui::SettingsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->extraArgsInput->setPlaceholderText("e.g. --name=value");
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()
@@ -15,12 +13,22 @@ SettingsDialog::~SettingsDialog()
delete ui; delete ui;
} }
void SettingsDialog::setExtraArgs(QString args) void SettingsDialog::setExtraArgs(QString extraArgs)
{ {
ui->extraArgsInput->setPlainText(args); ui->extraArgsInput->setPlainText(extraArgs);
} }
QString SettingsDialog::extraArgs() QString SettingsDialog::extraArgs()
{ {
return ui->extraArgsInput->toPlainText().trimmed(); return ui->extraArgsInput->toPlainText().trimmed();
} }
void SettingsDialog::setClientos(QString clientos)
{
ui->clientosInput->setText(clientos);
}
QString SettingsDialog::clientos()
{
return ui->clientosInput->text();
}

View File

@@ -15,9 +15,12 @@ public:
explicit SettingsDialog(QWidget *parent = nullptr); explicit SettingsDialog(QWidget *parent = nullptr);
~SettingsDialog(); ~SettingsDialog();
void setExtraArgs(QString); void setExtraArgs(QString extraArgs);
QString extraArgs(); QString extraArgs();
void setClientos(QString clientos);
QString clientos();
private: private:
Ui::SettingsDialog *ui; Ui::SettingsDialog *ui;
}; };

View File

@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>470</width> <width>488</width>
<height>183</height> <height>177</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@@ -23,18 +23,36 @@
<iconset resource="resources.qrc"> <iconset resource="resources.qrc">
<normaloff>:/images/connected.png</normaloff>:/images/connected.png</iconset> <normaloff>:/images/connected.png</normaloff>:/images/connected.png</iconset>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QFormLayout" name="formLayout_3">
<item> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Custom Parameters:</string> <string>Custom Parameters:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="0" column="1">
<widget class="QPlainTextEdit" name="extraArgsInput"/> <widget class="QPlainTextEdit" name="extraArgsInput">
<property name="placeholderText">
<string extracomment="Tokens with spaces can be surrounded by double quotes">e.g. --name=value --script=&quot;vpn-slice xxx&quot;</string>
</property>
</widget>
</item> </item>
<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"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>

View File

@@ -39,6 +39,47 @@ QString GPService::findBinary()
return nullptr; return nullptr;
} }
/* Port from https://github.com/qt/qtbase/blob/11d1dcc6e263c5059f34b44d531c9ccdf7c0b1d6/src/corelib/io/qprocess.cpp#L2115 */
QStringList GPService::splitCommand(QStringView command)
{
QStringList args;
QString tmp;
int quoteCount = 0;
bool inQuote = false;
// handle quoting. tokens can be surrounded by double quotes
// "hello world". three consecutive double quotes represent
// the quote character itself.
for (int i = 0; i < command.size(); ++i) {
if (command.at(i) == QLatin1Char('"')) {
++quoteCount;
if (quoteCount == 3) {
// third consecutive quote
quoteCount = 0;
tmp += command.at(i);
}
continue;
}
if (quoteCount) {
if (quoteCount == 1)
inQuote = !inQuote;
quoteCount = 0;
}
if (!inQuote && command.at(i).isSpace()) {
if (!tmp.isEmpty()) {
args += tmp;
tmp.clear();
}
} else {
tmp += command.at(i);
}
}
if (!tmp.isEmpty())
args += tmp;
return args;
}
void GPService::quit() void GPService::quit()
{ {
if (openconnect->state() == QProcess::NotRunning) { if (openconnect->state() == QProcess::NotRunning) {
@@ -58,14 +99,15 @@ void GPService::connect(QString server, QString username, QString passwd, QStrin
QString bin = findBinary(); QString bin = findBinary();
if (bin == nullptr) { if (bin == nullptr) {
log("Could not found openconnect binary, make sure openconnect is installed, exiting."); log("Could not find openconnect binary, make sure openconnect is installed, exiting.");
emit error("The OpenConect CLI was not found, make sure it has been installed!");
return; return;
} }
QStringList args; QStringList args;
args << QCoreApplication::arguments().mid(1) args << QCoreApplication::arguments().mid(1)
<< "--protocol=gp" << "--protocol=gp"
<< QProcess::splitCommand(extraArgs) << splitCommand(extraArgs)
<< "-u" << username << "-u" << username
<< "-C" << passwd << "-C" << passwd
<< server; << server;

View File

@@ -31,6 +31,7 @@ public:
signals: signals:
void connected(); void connected();
void disconnected(); void disconnected();
void error(QString errorMessage);
void logAvailable(QString log); void logAvailable(QString log);
public slots: public slots:
@@ -53,6 +54,7 @@ private:
void log(QString msg); void log(QString msg);
static QString findBinary(); static QString findBinary();
static QStringList splitCommand(QStringView command);
}; };
#endif // GLOBALPROTECTSERVICE_H #endif // GLOBALPROTECTSERVICE_H

View File

@@ -8,6 +8,9 @@
<signal name="logAvailable"> <signal name="logAvailable">
<arg name="log" type="s" /> <arg name="log" type="s" />
</signal> </signal>
<signal name="error">
<arg name="errorMessage" type="s" />
</signal>
<method name="connect"> <method name="connect">
<arg name="server" type="s" direction="in"/> <arg name="server" type="s" direction="in"/>
<arg name="username" type="s" direction="in"/> <arg name="username" type="s" direction="in"/>

View File

@@ -12,11 +12,30 @@ A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Q
- Supports automatically selecting the preferred gateway from the multiple gateways. - Supports automatically selecting the preferred gateway from the multiple gateways.
- Supports switching gateway from the system tray menu manually. - Supports switching gateway from the system tray menu manually.
## Future plan
- [ ] Improve the release process
- [ ] Process bugs and feature requests
- [ ] Support for bypassing the `gpclient` parameters
- [ ] Support the CLI mode
## Passing the Custom Parameters to `OpenConnect` CLI ## Passing the Custom Parameters to `OpenConnect` CLI
Custom parameters can be appended to the `OpenConnect` CLI with the following settings. Custom parameters can be appended to the `OpenConnect` CLI with the following settings.
> Tokens with spaces can be surrounded by double quotes; three consecutive double quotes represent the quote character itself.
<p align="center"> <p align="center">
<img src="https://user-images.githubusercontent.com/3297602/129464304-94eb8a2b-1c4a-47e1-b931-4422fff6eb81.png" /> <img src="https://user-images.githubusercontent.com/3297602/130319209-744be02b-d657-4f49-a76d-d2c81b5c46d5.png" />
<p>
## 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> <p>
## Prerequisites ## Prerequisites
@@ -59,7 +78,7 @@ sudo zypper install libqt5-qtbase-devel libqt5-qtwebsockets-devel libqt5-qtweben
Install the Qt dependencies: Install the Qt dependencies:
```sh ```sh
sudo dnf install qt5-qtbase-devel qt5-qttools-devel qt5-qtwebengine qt5-qtwebsockets-devel qt5-qtwebengine-devel sudo dnf install qt5-qtbase-devel qt5-qtwebengine-devel qt5-qtwebsockets-devel
``` ```
## Install ## Install

5
packaging/rpm/README.md Normal file
View File

@@ -0,0 +1,5 @@
## Command
```sh
docker run --rm -it -v ${PWD}:/rpm --workdir=/rpm --entrypoint ./entrypoint.sh centos:8
```

21
packaging/rpm/entrypoint.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash -e
# Install the build tools
dnf install -y epel-release
rpm --import http://download.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8
dnf install -y make rpm-build rpm-devel rpmlint rpmdevtools
# Install the build dependencies
dnf install -y qt5-qtbase-devel qt5-qtwebengine-devel qt5-qtwebsockets-devel
# Prepare the RPM build environment
rpmdev-setuptree
cp *.spec $HOME/rpmbuild/SPECS/
cp *.tar.gz $HOME/rpmbuild/SOURCES/
# Build
rpmbuild -ba $HOME/rpmbuild/SPECS/globalprotect-openconnect.spec
# Copy the package to the current directory
cp $HOME/rpmbuild/RPMS/x86_64/globalprotect-openconnect-*.rpm .
cp $HOME/rpmbuild/SRPMS/globalprotect-openconnect-*.src.rpm .

View File

@@ -0,0 +1,39 @@
Name: globalprotect-openconnect
Version: 1.3.0+SNAPSHOT20210829120923
Release: 1
Summary: A GlobalProtect VPN client
License: GPLv3
URL: https://github.com/yuezk/GlobalProtect-openconnect
Source0: %{url}/releases/download/latest/globalprotect-openconnect_%{version}.full.tar.gz
BuildRequires: qt5-qtbase-devel qt5-qtwebengine-devel qt5-qtwebsockets-devel
Requires: qt5-qtbase >= 5.12 qt5-qtwebengine >= 5.12 qt5-qtwebsockets >= 5.12 openconnect >= 8.0
%global debug_package %{nil}
%description
A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports SAML auth mode.
%prep
%autosetup
%build
qmake-qt5 CONFIG+=release
%make_build
%install
INSTALL_ROOT=${RPM_BUILD_ROOT} %make_install
%files
/etc/systemd/system/gpservice.service
/usr/bin/gpclient
/usr/bin/gpservice
/usr/share/applications/com.yuezk.qt.gpclient.desktop
/usr/share/dbus-1/system-services/com.yuezk.qt.GPService.service
/usr/share/dbus-1/system.d/com.yuezk.qt.GPService.conf
/usr/share/pixmaps/com.yuezk.qt.GPClient.svg