Merge branch 'dev' into android-7
# Conflicts: # CMakeLists.txt
This commit is contained in:
commit
e46e983bb8
15 changed files with 275 additions and 135 deletions
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.2.0
|
project(${PROJECT} VERSION 4.8.2.1
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 1067)
|
set(APP_ANDROID_VERSION_CODE 1068)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
|
@ -25,7 +25,6 @@ execute_process(
|
||||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||||
|
|
||||||
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
|
|
||||||
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
|
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
|
||||||
|
|
||||||
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#include "apiController.h"
|
#include "apiController.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
@ -11,8 +14,8 @@
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "configurators/wireguard_configurator.h"
|
#include "configurators/wireguard_configurator.h"
|
||||||
#include "core/enums/apiEnums.h"
|
#include "core/enums/apiEnums.h"
|
||||||
#include "version.h"
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -34,6 +37,7 @@ namespace
|
||||||
constexpr char userCountryCode[] = "user_country_code";
|
constexpr char userCountryCode[] = "user_country_code";
|
||||||
constexpr char serverCountryCode[] = "server_country_code";
|
constexpr char serverCountryCode[] = "server_country_code";
|
||||||
constexpr char serviceType[] = "service_type";
|
constexpr char serviceType[] = "service_type";
|
||||||
|
constexpr char serviceInfo[] = "service_info";
|
||||||
|
|
||||||
constexpr char aesKey[] = "aes_key";
|
constexpr char aesKey[] = "aes_key";
|
||||||
constexpr char aesIv[] = "aes_iv";
|
constexpr char aesIv[] = "aes_iv";
|
||||||
|
@ -65,6 +69,28 @@ namespace
|
||||||
return ErrorCode::ApiConfigDownloadError;
|
return ErrorCode::ApiConfigDownloadError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
|
||||||
|
const QByteArray &iv = "", const QByteArray &salt = "")
|
||||||
|
{
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
qDebug() << "Timeout occurred";
|
||||||
|
return true;
|
||||||
|
} else if (responseBody.contains("html")) {
|
||||||
|
qDebug() << "The response contains an html tag";
|
||||||
|
return true;
|
||||||
|
} else if (checkEncryption) {
|
||||||
|
try {
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||||
|
} catch (...) {
|
||||||
|
qDebug() << "Failed to decrypt the data";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
||||||
|
@ -138,6 +164,11 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
|
||||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||||
|
|
||||||
|
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||||
|
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
|
||||||
|
}
|
||||||
|
|
||||||
serverConfig[configKey::apiConfig] = apiConfig;
|
serverConfig[configKey::apiConfig] = apiConfig;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -320,24 +351,30 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
responseBody = reply->readAll();
|
||||||
|
|
||||||
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
||||||
m_proxyUrls = getProxyUrls();
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
for (const QString &proxyUrl : m_proxyUrls) {
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||||
|
reply->deleteLater(); // delete the previous reply
|
||||||
reply = amnApp->manager()->get(request);
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
|
||||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
responseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseBody = reply->readAll();
|
|
||||||
auto errorCode = checkErrors(sslErrors, reply);
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
return errorCode;
|
return errorCode;
|
||||||
|
@ -352,7 +389,6 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QNetworkAccessManager manager;
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setTransferTimeout(7000);
|
request.setTransferTimeout(7000);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
@ -410,7 +446,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||||
|
|
||||||
QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
@ -419,32 +455,36 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
auto encryptedResponseBody = reply->readAll();
|
||||||
if (m_proxyUrls.isEmpty()) {
|
|
||||||
m_proxyUrls = getProxyUrls();
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true)) {
|
||||||
}
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
for (const QString &proxyUrl : m_proxyUrls) {
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||||
reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
reply->deleteLater(); // delete the previous reply
|
||||||
|
reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
|
||||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
encryptedResponseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto errorCode = checkErrors(sslErrors, reply);
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
reply->deleteLater();
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto encryptedResponseBody = reply->readAll();
|
|
||||||
reply->deleteLater();
|
|
||||||
try {
|
try {
|
||||||
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||||
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||||
|
|
|
@ -248,7 +248,7 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
}
|
}
|
||||||
if (result != NO_ERROR) {
|
if (result != NO_ERROR) {
|
||||||
logger.error() << "Failed to create route to"
|
logger.error() << "Failed to create route to"
|
||||||
<< logger.sensitive(prefix.toString())
|
<< prefix.toString()
|
||||||
<< "result:" << result;
|
<< "result:" << result;
|
||||||
}
|
}
|
||||||
return result == NO_ERROR;
|
return result == NO_ERROR;
|
||||||
|
@ -265,7 +265,7 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
||||||
}
|
}
|
||||||
if (result != NO_ERROR) {
|
if (result != NO_ERROR) {
|
||||||
logger.error() << "Failed to delete route to"
|
logger.error() << "Failed to delete route to"
|
||||||
<< logger.sensitive(prefix.toString())
|
<< prefix.toString()
|
||||||
<< "result:" << result;
|
<< "result:" << result;
|
||||||
}
|
}
|
||||||
return result == NO_ERROR;
|
return result == NO_ERROR;
|
||||||
|
|
|
@ -34,13 +34,13 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
||||||
|
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
{
|
{
|
||||||
// #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
// if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||||
// {
|
{
|
||||||
// emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||||
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||||
|
|
29
client/ui/controllers/installController.cpp
Normal file → Executable file
29
client/ui/controllers/installController.cpp
Normal file → Executable file
|
@ -34,31 +34,6 @@ namespace
|
||||||
constexpr char apiConfig[] = "api_config";
|
constexpr char apiConfig[] = "api_config";
|
||||||
constexpr char authData[] = "auth_data";
|
constexpr char authData[] = "auth_data";
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
QString getNextDriverLetter()
|
|
||||||
{
|
|
||||||
QProcess drivesProc;
|
|
||||||
drivesProc.start("wmic logicaldisk get caption");
|
|
||||||
drivesProc.waitForFinished();
|
|
||||||
QString drives = drivesProc.readAll();
|
|
||||||
qDebug() << drives;
|
|
||||||
|
|
||||||
QString letters = "CFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
QString letter;
|
|
||||||
for (int i = letters.size() - 1; i > 0; i--) {
|
|
||||||
letter = letters.at(i);
|
|
||||||
if (!drives.contains(letter + ":"))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (letter == "C:") {
|
|
||||||
// set err info
|
|
||||||
qDebug() << "Can't find free drive letter";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return letter;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
|
InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
|
||||||
|
@ -668,7 +643,7 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
|
||||||
QString hostname = serverCredentials.hostName;
|
QString hostname = serverCredentials.hostName;
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
mountPath = getNextDriverLetter() + ":";
|
mountPath = Utils::getNextDriverLetter() + ":";
|
||||||
// QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3")
|
// QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3")
|
||||||
// .arg(labelTftpUserNameText())
|
// .arg(labelTftpUserNameText())
|
||||||
// .arg(labelTftpPortText())
|
// .arg(labelTftpPortText())
|
||||||
|
@ -872,6 +847,8 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
|
||||||
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
|
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
|
||||||
|
|
||||||
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
||||||
|
newServerConfig.insert(configKey::authData, authData);
|
||||||
|
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
|
||||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||||
|
|
||||||
if (reloadServiceConfig) {
|
if (reloadServiceConfig) {
|
||||||
|
|
|
@ -39,6 +39,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
||||||
case CountryNameRole: {
|
case CountryNameRole: {
|
||||||
return countryInfo.value(configKey::serverCountryName).toString();
|
return countryInfo.value(configKey::serverCountryName).toString();
|
||||||
}
|
}
|
||||||
|
case CountryImageCodeRole: {
|
||||||
|
return countryInfo.value(configKey::serverCountryCode).toString().toUpper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -76,5 +79,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[CountryNameRole] = "countryName";
|
roles[CountryNameRole] = "countryName";
|
||||||
roles[CountryCodeRole] = "countryCode";
|
roles[CountryCodeRole] = "countryCode";
|
||||||
|
roles[CountryImageCodeRole] = "countryImageCode";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ class ApiCountryModel : public QAbstractListModel
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
CountryNameRole = Qt::UserRole + 1,
|
CountryNameRole = Qt::UserRole + 1,
|
||||||
CountryCodeRole
|
CountryCodeRole,
|
||||||
|
CountryImageCodeRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ApiCountryModel(QObject *parent = nullptr);
|
explicit ApiCountryModel(QObject *parent = nullptr);
|
||||||
|
|
|
@ -771,5 +771,5 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
|
||||||
if (countryCode.isEmpty()) {
|
if (countryCode.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode);
|
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ PageType {
|
||||||
Layout.rightMargin: 32
|
Layout.rightMargin: 32
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg"
|
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ PageType {
|
||||||
|
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
|
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
@ -56,7 +58,7 @@ PageType {
|
||||||
|
|
||||||
headerText: qsTr("Connection")
|
headerText: qsTr("Connection")
|
||||||
|
|
||||||
actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : ""
|
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
|
||||||
actionButtonFunction: function() {
|
actionButtonFunction: function() {
|
||||||
moreActionsDrawer.open()
|
moreActionsDrawer.open()
|
||||||
}
|
}
|
||||||
|
@ -67,18 +69,19 @@ PageType {
|
||||||
parent: root
|
parent: root
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: root.height * 0.35
|
expandedHeight: root.height * 0.5
|
||||||
|
|
||||||
expandedContent: ColumnLayout {
|
expandedContent: ColumnLayout {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.leftMargin: 16
|
spacing: 0
|
||||||
anchors.rightMargin: 16
|
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: 32
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Settings")
|
headerText: qsTr("Settings")
|
||||||
}
|
}
|
||||||
|
@ -87,9 +90,12 @@ PageType {
|
||||||
id: switcher
|
id: switcher
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Enable logs")
|
text: qsTr("Enable logs")
|
||||||
|
|
||||||
|
visible: PageController.isStartPageVisible()
|
||||||
checked: SettingsController.isLoggingEnabled
|
checked: SettingsController.isLoggingEnabled
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (checked !== SettingsController.isLoggingEnabled) {
|
if (checked !== SettingsController.isLoggingEnabled) {
|
||||||
|
@ -98,6 +104,28 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: supportUuid
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
text: qsTr("Support tag")
|
||||||
|
descriptionText: SettingsController.getInstallationUuid()
|
||||||
|
|
||||||
|
descriptionOnTop: true
|
||||||
|
|
||||||
|
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||||
|
rightImageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
visible: SettingsController.getInstallationUuid() !== ""
|
||||||
|
clickedFunction: function() {
|
||||||
|
GC.copyToClipBoard(descriptionText)
|
||||||
|
PageController.showNotificationMessage(qsTr("Copied"))
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
this.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -772,7 +772,8 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
|
||||||
expandedContent: ColumnLayout {
|
expandedContent: ColumnLayout {
|
||||||
id: expandedContent
|
id: expandedContent
|
||||||
|
@ -783,8 +784,6 @@ PageType {
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
onImplicitHeightChanged: {
|
onImplicitHeightChanged: {
|
||||||
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
|
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
|
||||||
}
|
}
|
||||||
|
@ -797,57 +796,54 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2Type {
|
Header2TextType {
|
||||||
Layout.fillWidth: true
|
Layout.maximumWidth: parent.width
|
||||||
|
|
||||||
headerText: clientName
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout
|
|
||||||
{
|
|
||||||
id: textColumn
|
|
||||||
property string textColor: AmneziaStyle.color.mutedGray
|
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
ParagraphTextType {
|
text: clientName
|
||||||
color: textColumn.textColor
|
maximumLineCount: 2
|
||||||
visible: creationDate
|
wrapMode: Text.Wrap
|
||||||
Layout.fillWidth: true
|
elide: Qt.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
text: qsTr("Creation date: %1").arg(creationDate)
|
ParagraphTextType {
|
||||||
}
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: creationDate
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ParagraphTextType {
|
text: qsTr("Creation date: %1").arg(creationDate)
|
||||||
color: textColumn.textColor
|
}
|
||||||
visible: latestHandshake
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
ParagraphTextType {
|
||||||
}
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: latestHandshake
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ParagraphTextType {
|
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||||
color: textColumn.textColor
|
}
|
||||||
visible: dataReceived
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Data received: %1").arg(dataReceived)
|
ParagraphTextType {
|
||||||
}
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: dataReceived
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ParagraphTextType {
|
text: qsTr("Data received: %1").arg(dataReceived)
|
||||||
color: textColumn.textColor
|
}
|
||||||
visible: dataSent
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Data sent: %1").arg(dataSent)
|
ParagraphTextType {
|
||||||
}
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: dataSent
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ParagraphTextType {
|
text: qsTr("Data sent: %1").arg(dataSent)
|
||||||
color: textColumn.textColor
|
}
|
||||||
visible: allowedIps
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Allowed IPs: %1").arg(allowedIps)
|
ParagraphTextType {
|
||||||
}
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: allowedIps
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Allowed IPs: %1").arg(allowedIps)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -952,6 +948,7 @@ PageType {
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: revokeButton
|
id: revokeButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
defaultColor: AmneziaStyle.color.transparent
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||||
|
|
141
client/utilities.cpp
Normal file → Executable file
141
client/utilities.cpp
Normal file → Executable file
|
@ -10,7 +10,62 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "version.h"
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QString printErrorMessage(DWORD errorCode) {
|
||||||
|
LPVOID lpMsgBuf;
|
||||||
|
|
||||||
|
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
|
|
||||||
|
DWORD dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
||||||
|
|
||||||
|
FormatMessageW(
|
||||||
|
dwFlags,
|
||||||
|
NULL,
|
||||||
|
errorCode,
|
||||||
|
dwLanguageId,
|
||||||
|
(LPWSTR)&lpMsgBuf,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
QString errorMsg = QString::fromWCharArray((LPCWSTR)lpMsgBuf);
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
|
return errorMsg.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::getNextDriverLetter()
|
||||||
|
{
|
||||||
|
DWORD drivesBitmask = GetLogicalDrives();
|
||||||
|
if (drivesBitmask == 0) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
qDebug() << "GetLogicalDrives failed. Error code:" << error;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString letters = "FGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
QString availableLetter;
|
||||||
|
|
||||||
|
for (int i = letters.size() - 1; i >= 0; --i) {
|
||||||
|
QChar letterChar = letters.at(i);
|
||||||
|
int driveIndex = letterChar.toLatin1() - 'A';
|
||||||
|
|
||||||
|
if ((drivesBitmask & (1 << driveIndex)) == 0) {
|
||||||
|
availableLetter = letterChar;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableLetter.isEmpty()) {
|
||||||
|
qDebug() << "Can't find free drive letter";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableLetter;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QString Utils::getRandomString(int len)
|
QString Utils::getRandomString(int len)
|
||||||
{
|
{
|
||||||
|
@ -108,30 +163,34 @@ QString Utils::usrExecutable(const QString &baseName)
|
||||||
bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QProcess process;
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
process.setReadChannel(QProcess::StandardOutput);
|
if (hSnapshot == INVALID_HANDLE_VALUE) {
|
||||||
process.setProcessChannelMode(QProcess::MergedChannels);
|
qWarning() << "Utils::processIsRunning error CreateToolhelp32Snapshot";
|
||||||
process.start("wmic.exe",
|
return false;
|
||||||
QStringList() << "/OUTPUT:STDOUT"
|
}
|
||||||
<< "PROCESS"
|
|
||||||
<< "get"
|
|
||||||
<< "Caption");
|
|
||||||
process.waitForStarted();
|
|
||||||
process.waitForFinished();
|
|
||||||
QString processData(process.readAll());
|
|
||||||
QStringList processList = processData.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts);
|
|
||||||
foreach (const QString &rawLine, processList) {
|
|
||||||
const QString line = rawLine.simplified();
|
|
||||||
if (line.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line == fileName) {
|
PROCESSENTRY32W pe32;
|
||||||
|
pe32.dwSize = sizeof(PROCESSENTRY32W);
|
||||||
|
|
||||||
|
if (!Process32FirstW(hSnapshot, &pe32)) {
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
|
qWarning() << "Utils::processIsRunning error Process32FirstW";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
QString exeFile = QString::fromWCharArray(pe32.szExeFile);
|
||||||
|
|
||||||
|
if (exeFile.compare(fileName, Qt::CaseInsensitive) == 0) {
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} while (Process32NextW(hSnapshot, &pe32));
|
||||||
|
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
return false;
|
return false;
|
||||||
#elif defined(Q_OS_IOS)
|
|
||||||
|
#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
@ -149,13 +208,45 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::killProcessByName(const QString &name)
|
bool Utils::killProcessByName(const QString &name)
|
||||||
{
|
{
|
||||||
qDebug().noquote() << "Kill process" << name;
|
qDebug().noquote() << "Kill process" << name;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QProcess::execute("taskkill", QStringList() << "/IM" << name << "/F");
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
#elif defined Q_OS_IOS
|
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
PROCESSENTRY32W pe32;
|
||||||
|
pe32.dwSize = sizeof(PROCESSENTRY32W);
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (Process32FirstW(hSnapshot, &pe32)) {
|
||||||
|
do {
|
||||||
|
QString exeFile = QString::fromWCharArray(pe32.szExeFile);
|
||||||
|
|
||||||
|
if (exeFile.compare(name, Qt::CaseInsensitive) == 0) {
|
||||||
|
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
|
||||||
|
if (hProcess != NULL) {
|
||||||
|
if (TerminateProcess(hProcess, 0)) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
qCritical() << "Can't terminate process" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error);
|
||||||
|
}
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
} else {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
qCritical() << "Can't open process for termination" << exeFile << "(PID:" << pe32.th32ProcessID << "). Error:" << printErrorMessage(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (Process32NextW(hSnapshot, &pe32));
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
|
return success;
|
||||||
|
#elif defined Q_OS_IOS || defined(Q_OS_ANDROID)
|
||||||
|
return false;
|
||||||
#else
|
#else
|
||||||
QProcess::execute(QString("pkill %1").arg(name));
|
QProcess::execute(QString("pkill %1").arg(name));
|
||||||
#endif
|
#endif
|
||||||
|
|
6
client/utilities.h
Normal file → Executable file
6
client/utilities.h
Normal file → Executable file
|
@ -7,7 +7,8 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include "Windows.h"
|
#include <windows.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Utils : public QObject
|
class Utils : public QObject
|
||||||
|
@ -27,7 +28,7 @@ public:
|
||||||
static bool initializePath(const QString &path);
|
static bool initializePath(const QString &path);
|
||||||
|
|
||||||
static bool processIsRunning(const QString &fileName, const bool fullFlag = false);
|
static bool processIsRunning(const QString &fileName, const bool fullFlag = false);
|
||||||
static void killProcessByName(const QString &name);
|
static bool killProcessByName(const QString &name);
|
||||||
|
|
||||||
static QString openVpnExecPath();
|
static QString openVpnExecPath();
|
||||||
static QString wireguardExecPath();
|
static QString wireguardExecPath();
|
||||||
|
@ -39,6 +40,7 @@ public:
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
|
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
|
||||||
|
static QString getNextDriverLetter();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,14 +56,15 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
|
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
|
||||||
|
|
||||||
if (IpcClient::Interface()) {
|
if (IpcClient::Interface()) {
|
||||||
if (state == Vpn::ConnectionState::Connected) {
|
if (state == Vpn::ConnectionState::Connected) {
|
||||||
IpcClient::Interface()->resetIpStack();
|
IpcClient::Interface()->resetIpStack();
|
||||||
IpcClient::Interface()->flushDns();
|
IpcClient::Interface()->flushDns();
|
||||||
|
|
||||||
if (!m_vpnConfiguration.value(config_key::configVersion).toInt()) {
|
if (!m_vpnConfiguration.value(config_key::configVersion).toInt() && container != DockerContainer::Awg
|
||||||
|
&& container != DockerContainer::WireGuard) {
|
||||||
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
|
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
|
||||||
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
|
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue