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)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.2.0
|
||||
project(${PROJECT} VERSION 4.8.2.1
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
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")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
|
|
@ -25,7 +25,6 @@ execute_process(
|
|||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||
|
||||
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(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "apiController.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
@ -11,8 +14,8 @@
|
|||
#include "amnezia_application.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "core/enums/apiEnums.h"
|
||||
#include "version.h"
|
||||
#include "utilities.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -34,6 +37,7 @@ namespace
|
|||
constexpr char userCountryCode[] = "user_country_code";
|
||||
constexpr char serverCountryCode[] = "server_country_code";
|
||||
constexpr char serviceType[] = "service_type";
|
||||
constexpr char serviceInfo[] = "service_info";
|
||||
|
||||
constexpr char aesKey[] = "aes_key";
|
||||
constexpr char aesIv[] = "aes_iv";
|
||||
|
@ -65,6 +69,28 @@ namespace
|
|||
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)
|
||||
|
@ -138,6 +164,11 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
|
|||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
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;
|
||||
|
||||
return;
|
||||
|
@ -320,24 +351,30 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
|||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
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();
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||
for (const QString &proxyUrl : m_proxyUrls) {
|
||||
qDebug() << "Go to the next endpoint";
|
||||
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
reply = amnApp->manager()->get(request);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
||||
|
||||
responseBody = reply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
|
||||
break;
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
responseBody = reply->readAll();
|
||||
auto errorCode = checkErrors(sslErrors, reply);
|
||||
reply->deleteLater();
|
||||
return errorCode;
|
||||
|
@ -352,7 +389,6 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
|||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(7000);
|
||||
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::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||
|
||||
QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||
QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
|
||||
QEventLoop wait;
|
||||
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; });
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::TimeoutError || reply->error() == QNetworkReply::NetworkError::OperationCanceledError) {
|
||||
if (m_proxyUrls.isEmpty()) {
|
||||
m_proxyUrls = getProxyUrls();
|
||||
}
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
|
||||
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) {
|
||||
qDebug() << "Go to the next endpoint";
|
||||
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);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
|
||||
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
|
||||
|
||||
encryptedResponseBody = reply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
||||
break;
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
auto errorCode = checkErrors(sslErrors, reply);
|
||||
reply->deleteLater();
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
reply->deleteLater();
|
||||
try {
|
||||
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||
|
|
|
@ -248,7 +248,7 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
|||
}
|
||||
if (result != NO_ERROR) {
|
||||
logger.error() << "Failed to create route to"
|
||||
<< logger.sensitive(prefix.toString())
|
||||
<< prefix.toString()
|
||||
<< "result:" << result;
|
||||
}
|
||||
return result == NO_ERROR;
|
||||
|
@ -265,7 +265,7 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
|||
}
|
||||
if (result != NO_ERROR) {
|
||||
logger.error() << "Failed to delete route to"
|
||||
<< logger.sensitive(prefix.toString())
|
||||
<< prefix.toString()
|
||||
<< "result:" << result;
|
||||
}
|
||||
return result == NO_ERROR;
|
||||
|
|
|
@ -34,13 +34,13 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||
|
||||
void ConnectionController::openConnection()
|
||||
{
|
||||
// #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
// if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||
// {
|
||||
// emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||
// return;
|
||||
// }
|
||||
// #endif
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||
{
|
||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
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 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,
|
||||
|
@ -668,7 +643,7 @@ void InstallController::mountSftpDrive(const QString &port, const QString &passw
|
|||
QString hostname = serverCredentials.hostName;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
mountPath = getNextDriverLetter() + ":";
|
||||
mountPath = Utils::getNextDriverLetter() + ":";
|
||||
// QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3")
|
||||
// .arg(labelTftpUserNameText())
|
||||
// .arg(labelTftpPortText())
|
||||
|
@ -872,6 +847,8 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
|
|||
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
|
||||
|
||||
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);
|
||||
|
||||
if (reloadServiceConfig) {
|
||||
|
|
|
@ -39,6 +39,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
|||
case CountryNameRole: {
|
||||
return countryInfo.value(configKey::serverCountryName).toString();
|
||||
}
|
||||
case CountryImageCodeRole: {
|
||||
return countryInfo.value(configKey::serverCountryCode).toString().toUpper();
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -76,5 +79,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
|||
QHash<int, QByteArray> roles;
|
||||
roles[CountryNameRole] = "countryName";
|
||||
roles[CountryCodeRole] = "countryCode";
|
||||
roles[CountryImageCodeRole] = "countryImageCode";
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ class ApiCountryModel : public QAbstractListModel
|
|||
public:
|
||||
enum Roles {
|
||||
CountryNameRole = Qt::UserRole + 1,
|
||||
CountryCodeRole
|
||||
CountryCodeRole,
|
||||
CountryImageCodeRole
|
||||
};
|
||||
|
||||
explicit ApiCountryModel(QObject *parent = nullptr);
|
||||
|
|
|
@ -771,5 +771,5 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
|
|||
if (countryCode.isEmpty()) {
|
||||
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.alignment: Qt.AlignRight
|
||||
|
||||
source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg"
|
||||
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ PageType {
|
|||
|
||||
|
||||
HeaderType {
|
||||
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
|
@ -56,7 +58,7 @@ PageType {
|
|||
|
||||
headerText: qsTr("Connection")
|
||||
|
||||
actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : ""
|
||||
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
|
||||
actionButtonFunction: function() {
|
||||
moreActionsDrawer.open()
|
||||
}
|
||||
|
@ -67,18 +69,19 @@ PageType {
|
|||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.35
|
||||
expandedHeight: root.height * 0.5
|
||||
|
||||
expandedContent: ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
HeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Settings")
|
||||
}
|
||||
|
@ -87,9 +90,12 @@ PageType {
|
|||
id: switcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Enable logs")
|
||||
|
||||
visible: PageController.isStartPageVisible()
|
||||
checked: SettingsController.isLoggingEnabled
|
||||
onCheckedChanged: {
|
||||
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 {
|
||||
id: expandedContent
|
||||
|
@ -783,8 +784,6 @@ PageType {
|
|||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 8
|
||||
|
||||
onImplicitHeightChanged: {
|
||||
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
|
||||
}
|
||||
|
@ -797,57 +796,54 @@ PageType {
|
|||
}
|
||||
}
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: clientName
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: textColumn
|
||||
property string textColor: AmneziaStyle.color.mutedGray
|
||||
Header2TextType {
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: creationDate
|
||||
Layout.fillWidth: true
|
||||
text: clientName
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.Wrap
|
||||
elide: Qt.ElideRight
|
||||
}
|
||||
|
||||
text: qsTr("Creation date: %1").arg(creationDate)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: creationDate
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: latestHandshake
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Creation date: %1").arg(creationDate)
|
||||
}
|
||||
|
||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: latestHandshake
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataReceived
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||
}
|
||||
|
||||
text: qsTr("Data received: %1").arg(dataReceived)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: dataReceived
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataSent
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Data received: %1").arg(dataReceived)
|
||||
}
|
||||
|
||||
text: qsTr("Data sent: %1").arg(dataSent)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: dataSent
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: allowedIps
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Data sent: %1").arg(dataSent)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -952,6 +948,7 @@ PageType {
|
|||
BasicButtonType {
|
||||
id: revokeButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
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 "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)
|
||||
{
|
||||
|
@ -108,30 +163,34 @@ QString Utils::usrExecutable(const QString &baseName)
|
|||
bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess process;
|
||||
process.setReadChannel(QProcess::StandardOutput);
|
||||
process.setProcessChannelMode(QProcess::MergedChannels);
|
||||
process.start("wmic.exe",
|
||||
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;
|
||||
}
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE) {
|
||||
qWarning() << "Utils::processIsRunning error CreateToolhelp32Snapshot";
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
} while (Process32NextW(hSnapshot, &pe32));
|
||||
|
||||
CloseHandle(hSnapshot);
|
||||
return false;
|
||||
#elif defined(Q_OS_IOS)
|
||||
|
||||
#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
|
||||
return false;
|
||||
#else
|
||||
QProcess process;
|
||||
|
@ -149,13 +208,45 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
|||
#endif
|
||||
}
|
||||
|
||||
void Utils::killProcessByName(const QString &name)
|
||||
bool Utils::killProcessByName(const QString &name)
|
||||
{
|
||||
qDebug().noquote() << "Kill process" << name;
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess::execute("taskkill", QStringList() << "/IM" << name << "/F");
|
||||
#elif defined Q_OS_IOS
|
||||
return;
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
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
|
||||
QProcess::execute(QString("pkill %1").arg(name));
|
||||
#endif
|
||||
|
|
6
client/utilities.h
Normal file → Executable file
6
client/utilities.h
Normal file → Executable file
|
@ -7,7 +7,8 @@
|
|||
#include <QJsonDocument>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "Windows.h"
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#endif
|
||||
|
||||
class Utils : public QObject
|
||||
|
@ -27,7 +28,7 @@ public:
|
|||
static bool initializePath(const QString &path);
|
||||
|
||||
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 wireguardExecPath();
|
||||
|
@ -39,6 +40,7 @@ public:
|
|||
|
||||
#ifdef Q_OS_WIN
|
||||
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
|
||||
static QString getNextDriverLetter();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -56,14 +56,15 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||
{
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
|
||||
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
|
||||
|
||||
if (IpcClient::Interface()) {
|
||||
if (state == Vpn::ConnectionState::Connected) {
|
||||
IpcClient::Interface()->resetIpStack();
|
||||
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 dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue