From 8599b206788ed258ba0901844fa1dd7ebdb291e7 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 18 Dec 2023 13:17:39 +0700 Subject: [PATCH 01/65] full access config no longer contains the last_config field --- client/ui/controllers/exportController.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 9930926f..3ad8862f 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -48,6 +48,19 @@ void ExportController::generateFullAccessConfig() int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); QJsonObject config = m_settings->server(serverIndex); + QJsonArray containers = config.value(config_key::containers).toArray(); + for (auto i = 0; i < containers.size(); i++) { + auto container = containers.at(i).toObject(); + auto containerType = ContainerProps::containerFromString(container.value(config_key::container).toString()); + auto containerConfig = container.value(ContainerProps::containerTypeToString(containerType)).toObject(); + + containerConfig.remove(config_key::last_config); + + container[ContainerProps::containerTypeToString(containerType)] = containerConfig; + containers.replace(i, container); + } + config[config_key::containers] = containers; + QByteArray compressedConfig = QJsonDocument(config).toJson(); compressedConfig = qCompress(compressedConfig, 8); m_config = QString("vpn://%1") From f37c8e5fd45df5fe14f9966a910b7b00bc2db813 Mon Sep 17 00:00:00 2001 From: KsZnak Date: Wed, 20 Dec 2023 19:14:15 +0200 Subject: [PATCH 02/65] Update amneziavpn_ru.ts --- client/translations/amneziavpn_ru.ts | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index 4f24e540..6ef99dff 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -92,7 +92,7 @@ Configure your server - Настроить ваш сервер + Настроить свой сервер @@ -174,7 +174,7 @@ Added containers that were already installed on the server - + Добавлены сервисы и протоколы, которые были ранее установлены на сервер @@ -1012,17 +1012,17 @@ Already installed containers were found on the server. All installed containers Allow application screenshots - + Разрешить скриншоты приложения Auto start - + Автозапуск Launch the application every time the device is starts - + Открывать приложение при запуске устройства @@ -1353,7 +1353,7 @@ Already installed containers were found on the server. All installed containers Clear cached profiles? - Удалить кэш Amnezia с сервера? + Удалить кэш Amnezia? @@ -1680,7 +1680,7 @@ and will not be shared or disclosed to the Amnezia or any third parties All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties - + Все введенные вами данные останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или третьим лицам @@ -1978,12 +1978,12 @@ and will not be shared or disclosed to the Amnezia or any third parties Save ShadowSocks config - + Сохраните конфигурацию ShadowSocks Save Cloak config - + Сохранить конфигурацию Cloak @@ -1993,12 +1993,12 @@ and will not be shared or disclosed to the Amnezia or any third parties ShadowSocks native format - + Нативный формат ShadowSocks Cloak native format - + Нативный формат Cloak @@ -2008,23 +2008,23 @@ and will not be shared or disclosed to the Amnezia or any third parties Share full access to the server and VPN - + Поделиться полным доступом к серверу Use for your own devices, or share with those you trust to manage the server. - + Используйте для собственных устройств или передайте управление сервером тем, кому вы доверяете. Users - + Пользователи User name - + Имя пользователя @@ -2059,7 +2059,7 @@ and will not be shared or disclosed to the Amnezia or any third parties The user will no longer be able to connect to your server. - + Пользователь больше не сможет подключаться к вашему серверу @@ -2104,24 +2104,24 @@ and will not be shared or disclosed to the Amnezia or any third parties Full access to the server and VPN - + Полный доступ к серверу и VPN We recommend that you use full access to the server only for your own additional devices. - + Мы рекомендуем использовать полный доступ к серверу только для собственных устройств. If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. - + Если вы поделитесь полным доступом с другими людьми, то они смогут удалять и добавлять протоколы и сервисы на сервер, что приведет к некорректной работе VPN для всех пользователей. Server - + Сервер @@ -2537,7 +2537,7 @@ and will not be shared or disclosed to the Amnezia or any third parties The config does not contain any containers and credentials for connecting to the server - + Конфиг не содержит контейнеров и учетных данных для подключения к серверу Failed to save config to disk @@ -2814,7 +2814,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin Sftp file sharing service - is secure FTP service - Сервис обмена файлами Sftp - безопасный FTP-сервис + Файловое хранилище для безопасного хранения данных @@ -2959,7 +2959,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin Show connection settings - + Показать настройки подключения From 41aaac7d32c8bba00337798bc1371251ed781132 Mon Sep 17 00:00:00 2001 From: KsZnak Date: Wed, 20 Dec 2023 20:16:35 +0200 Subject: [PATCH 03/65] Update amneziavpn_ru.ts --- client/translations/amneziavpn_ru.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index 6ef99dff..af86594d 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -1978,7 +1978,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Save ShadowSocks config - Сохраните конфигурацию ShadowSocks + Сохранить конфигурацию ShadowSocks @@ -1993,12 +1993,12 @@ and will not be shared or disclosed to the Amnezia or any third parties ShadowSocks native format - Нативный формат ShadowSocks + ShadowSocks нативный формат Cloak native format - Нативный формат Cloak + Cloak нативный формат From 7aac9f9d0e1a6971cf4acc178e810d3d04fda30f Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 21 Dec 2023 17:47:34 +0700 Subject: [PATCH 04/65] fixed hang after creating configuration on connection - config created on connection is displayed as admin (platform name) on the client management page - added config creation time on the client management page --- client/amnezia_application.cpp | 1 + client/configurators/vpn_configurator.h | 1 + client/ui/models/clientManagementModel.cpp | 15 +++++++++++---- client/ui/models/clientManagementModel.h | 3 ++- client/ui/qml/Pages2/PageShare.qml | 2 +- client/vpnconnection.cpp | 6 +++++- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 40aab515..34c0862b 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -330,6 +330,7 @@ void AmneziaApplication::initModels() ServerCredentials credentials) { m_serversModel->reloadContainerConfig(); m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + emit m_configurator->clientModelUpdated(); }); } diff --git a/client/configurators/vpn_configurator.h b/client/configurators/vpn_configurator.h index 61dc2ac6..7164bd8e 100644 --- a/client/configurators/vpn_configurator.h +++ b/client/configurators/vpn_configurator.h @@ -46,6 +46,7 @@ public: signals: void newVpnConfigCreated(const QString &clientId, const QString &clientName, const DockerContainer container, ServerCredentials credentials); + void clientModelUpdated(); }; #endif // VPN_CONFIGURATOR_H diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 8ec31d02..45495836 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -15,6 +15,7 @@ namespace constexpr char clientName[] = "clientName"; constexpr char container[] = "container"; constexpr char userData[] = "userData"; + constexpr char creationDate[] = "creationDate"; } } @@ -40,6 +41,7 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const switch (role) { case ClientNameRole: return userData.value(configKey::clientName).toString(); + case CreationDateRole: return userData.value(configKey::creationDate).toString(); } return QVariant(); @@ -200,19 +202,20 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt for (int i = 0; i < m_clientsTable.size(); i++) { if (m_clientsTable.at(i).toObject().value(configKey::clientId) == clientId) { - return renameClient(i, clientName, container, credentials); + return renameClient(i, clientName, container, credentials, true); } } - beginResetModel(); + beginInsertRows(QModelIndex(), rowCount(), 1); QJsonObject client; client[configKey::clientId] = clientId; QJsonObject userData; userData[configKey::clientName] = clientName; + userData[configKey::creationDate] = QDateTime::currentDateTime().toString(); client[configKey::userData] = userData; m_clientsTable.push_back(client); - endResetModel(); + endInsertRows(); const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); @@ -229,11 +232,14 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt } ErrorCode ClientManagementModel::renameClient(const int row, const QString &clientName, const DockerContainer container, - ServerCredentials credentials) + ServerCredentials credentials, bool addTimeStamp) { auto client = m_clientsTable.at(row).toObject(); auto userData = client[configKey::userData].toObject(); userData[configKey::clientName] = clientName; + if (addTimeStamp) { + userData[configKey::creationDate] = QDateTime::currentDateTime().toString(); + } client[configKey::userData] = userData; m_clientsTable.replace(row, client); @@ -369,5 +375,6 @@ QHash ClientManagementModel::roleNames() const { QHash roles; roles[ClientNameRole] = "clientName"; + roles[CreationDateRole] = "creationDate"; return roles; } diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index 6b6adf68..6df7f802 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -14,6 +14,7 @@ class ClientManagementModel : public QAbstractListModel public: enum Roles { ClientNameRole = Qt::UserRole + 1, + CreationDateRole }; ClientManagementModel(std::shared_ptr settings, QObject *parent = nullptr); @@ -26,7 +27,7 @@ public slots: ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container, ServerCredentials credentials); ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container, - ServerCredentials credentials); + ServerCredentials credentials, bool addTimeStamp = false); ErrorCode revokeClient(const int index, const DockerContainer container, ServerCredentials credentials); protected: diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 38010b8f..41e9497a 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -578,7 +578,7 @@ PageType { Layout.bottomMargin: 24 headerText: clientName - descriptionText: serverSelector.text + descriptionText: qsTr("Creation date: ") + creationDate } BasicButtonType { diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 80163ef1..d67767bd 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -246,7 +247,10 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser m_settings->setProtocolConfig(serverIndex, container, proto, protoObject); } - emit m_configurator->newVpnConfigCreated(clientId, "unnamed client", container, credentials); + QEventLoop wait; + emit m_configurator->newVpnConfigCreated(clientId, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), container, credentials); + QObject::connect(m_configurator.get(), &VpnConfigurator::clientModelUpdated, &wait, &QEventLoop::quit); + wait.exec(); } return configData; From 9290775ab5247c5b27bec459138d483154b4fd21 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 21 Dec 2023 23:34:27 +0700 Subject: [PATCH 05/65] added removal of last_config when revoke admin config on client management panel --- client/amnezia_application.cpp | 2 ++ client/ui/controllers/exportController.cpp | 15 ++++++++----- client/ui/models/clientManagementModel.cpp | 26 +++++++++++++++++++--- client/ui/models/clientManagementModel.h | 3 +++ client/ui/models/servers_model.cpp | 8 +++++++ client/ui/models/servers_model.h | 1 + 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 40aab515..14f6befe 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -324,6 +324,8 @@ void AmneziaApplication::initModels() m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); + connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, + m_serversModel.get(), &ServersModel::clearCachedProfile); connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this, [this](const QString &clientId, const QString &clientName, const DockerContainer container, diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 3ad8862f..9209f4cd 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -50,14 +50,17 @@ void ExportController::generateFullAccessConfig() QJsonArray containers = config.value(config_key::containers).toArray(); for (auto i = 0; i < containers.size(); i++) { - auto container = containers.at(i).toObject(); - auto containerType = ContainerProps::containerFromString(container.value(config_key::container).toString()); - auto containerConfig = container.value(ContainerProps::containerTypeToString(containerType)).toObject(); + auto containerConfig = containers.at(i).toObject(); + auto containerType = ContainerProps::containerFromString(containerConfig.value(config_key::container).toString()); - containerConfig.remove(config_key::last_config); + for (auto protocol : ContainerProps::protocolsForContainer(containerType)) { + auto protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject(); - container[ContainerProps::containerTypeToString(containerType)] = containerConfig; - containers.replace(i, container); + protocolConfig.remove(config_key::last_config); + containerConfig[ProtocolProps::protoToString(protocol)] = protocolConfig; + } + + containers.replace(i, containerConfig); } config[config_key::containers] = containers; diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 8ec31d02..15c23229 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -257,13 +257,33 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContainer container, ServerCredentials credentials) { + ErrorCode errorCode = ErrorCode::NoError; + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) { - return revokeOpenVpn(row, container, credentials); + errorCode = revokeOpenVpn(row, container, credentials); } else if (container == DockerContainer::WireGuard || container == DockerContainer::Awg) { - return revokeWireGuard(row, container, credentials); + errorCode = revokeWireGuard(row, container, credentials); } - return ErrorCode::NoError; + + if (errorCode == ErrorCode::NoError) { + auto client = m_clientsTable.at(row).toObject(); + QString clientId = client.value(configKey::clientId).toString(); + + const auto server = m_settings->defaultServer(); + QJsonArray containers = server.value(config_key::containers).toArray(); + for (auto i = 0; i < containers.size(); i++) { + auto containerConfig = containers.at(i).toObject(); + auto containerType = ContainerProps::containerFromString(containerConfig.value(config_key::container).toString()); + auto protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(containerType)).toObject(); + + if (protocolConfig.value(config_key::last_config).toString().contains(clientId)) { + emit adminConfigRevoked(container); + } + } + } + + return errorCode; } ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContainer container, diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index 6b6adf68..864bd297 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -32,6 +32,9 @@ public slots: protected: QHash roleNames() const override; +signals: + void adminConfigRevoked(const DockerContainer container); + private: bool isClientExists(const QString &clientId); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index ad927fce..b8bfbbc5 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -479,6 +479,14 @@ void ServersModel::clearCachedProfiles() updateContainersModel(); } +void ServersModel::clearCachedProfile(const DockerContainer container) +{ + m_settings->clearLastConnectionConfig(m_currentlyProcessedServerIndex, container); + + m_servers.replace(m_currentlyProcessedServerIndex, m_settings->server(m_currentlyProcessedServerIndex)); + updateContainersModel(); +} + bool ServersModel::isAmneziaDnsContainerInstalled(const int serverIndex) { QJsonObject server = m_servers.at(serverIndex).toObject(); diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 901605e2..af88febb 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -84,6 +84,7 @@ public slots: void addContainerConfig(const int containerIndex, const QJsonObject config); void clearCachedProfiles(); + void clearCachedProfile(const DockerContainer container); ErrorCode removeContainer(const int containerIndex); ErrorCode removeAllContainers(); From 8d3e21d46ab0175663125da31bd3bc7e9350ea97 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 23 Dec 2023 23:14:02 +0200 Subject: [PATCH 06/65] Fix resolv-update script for MacOS (OpenVPN DNS) script from https://github.com/andrewgdotcom/openvpn-mac-dns --- deploy/data/macos/update-resolv-conf.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/deploy/data/macos/update-resolv-conf.sh b/deploy/data/macos/update-resolv-conf.sh index 09759269..6d5bf3b5 100755 --- a/deploy/data/macos/update-resolv-conf.sh +++ b/deploy/data/macos/update-resolv-conf.sh @@ -2,7 +2,7 @@ # Mac name-resolution updater based on @cl's script here: # https://blog.netnerds.net/2011/10/openvpn-update-client-dns-on-mac-os-x-using-from-the-command-line/ -# Openvpn envvar parsing taken from the script in debian's openvpn package. +# Openvpn envar parsing taken from the script in debian's openvpn package. # Smushed together and improved by @andrewgdotcom. # Parses DHCP options from openvpn to update resolv.conf @@ -10,6 +10,8 @@ # up /etc/openvpn/update-resolv-conf # down /etc/openvpn/update-resolv-conf +echo "*** starting update-resolv-config script ***" + [ "$script_type" ] || exit 0 [ "$dev" ] || exit 0 @@ -34,11 +36,11 @@ update_all_dns() echo updating dns for $adapter # set dns server to the vpn dns server if [[ "${SRCHS[@]}" ]]; then - networksetup -setsearchdomains "$adapter" "${SRCHS[@]}" + networksetup -setsearchdomains "$adapter" "${SRCHS[@]}" fi if [[ "${NMSRVRS[@]}" ]]; then - networksetup -setdnsservers "$adapter" "${NMSRVRS[@]}" - fi + networksetup -setdnsservers "$adapter" "${NMSRVRS[@]}" + fi done } @@ -61,7 +63,7 @@ case "$script_type" in if [ "$part1" = "dhcp-option" ] ; then if [ "$part2" = "DNS" ] ; then NMSRVRS=(${NMSRVRS[@]} $part3) - elif [ "$part2" = "DOMAIN" ] ; then + elif [ "$part2" = "DOMAIN" ] || [ "$part2" = "DOMAIN-SEARCH" ]; then SRCHS=(${SRCHS[@]} $part3) fi fi @@ -72,3 +74,5 @@ case "$script_type" in clear_all_dns ;; esac + +echo "*** finished update-resolv-config script ***" From a68f19d72f4a1c673b864c0308024a97024f6736 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sun, 24 Dec 2023 12:25:05 +0700 Subject: [PATCH 07/65] added migration from version 3 of client management to version 4 --- client/ui/models/clientManagementModel.cpp | 19 +++++++++++++++++++ client/ui/models/clientManagementModel.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 8ec31d02..81237917 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -45,6 +45,23 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const return QVariant(); } +void ClientManagementModel::migration(const QByteArray &clientsTableString) +{ + QJsonObject clientsTable = QJsonDocument::fromJson(clientsTableString).object(); + + for (auto &clientId : clientsTable.keys()) { + QJsonObject client; + client[configKey::clientId] = clientId; + + QJsonObject userData; + userData[configKey::clientName] = clientsTable.value(clientId).toObject().value(configKey::clientName); + client[configKey::userData] = userData; + + m_clientsTable.push_back(client); + } + +} + ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCredentials credentials) { beginResetModel(); @@ -67,6 +84,8 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr m_clientsTable = QJsonDocument::fromJson(clientsTableString).array(); if (m_clientsTable.isEmpty()) { + migration(clientsTableString); + int count = 0; if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks diff --git a/client/ui/models/clientManagementModel.h b/client/ui/models/clientManagementModel.h index 6b6adf68..4bbe3e27 100644 --- a/client/ui/models/clientManagementModel.h +++ b/client/ui/models/clientManagementModel.h @@ -35,6 +35,8 @@ protected: private: bool isClientExists(const QString &clientId); + void migration(const QByteArray &clientsTableString); + ErrorCode revokeOpenVpn(const int row, const DockerContainer container, ServerCredentials credentials); ErrorCode revokeWireGuard(const int row, const DockerContainer container, ServerCredentials credentials); From 7437d47d92dfae9311c3bdbc45f7b76fdf011738 Mon Sep 17 00:00:00 2001 From: albexk Date: Mon, 25 Dec 2023 15:16:22 +0300 Subject: [PATCH 08/65] Remove unnecessary permission RECEIVE_BOOT_COMPLETED --- client/android/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 30b77f09..cf237d07 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -22,7 +22,6 @@ - From 8f53d563a42f616f41f8474f50c931eb108fda2d Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 25 Dec 2023 22:49:24 +0700 Subject: [PATCH 09/65] moved the client table for cloak and ss to the openvpn folder --- client/ui/models/clientManagementModel.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 81237917..08a95353 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -71,8 +71,14 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr ErrorCode error = ErrorCode::NoError; - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)); + } else { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container)); + } + const QByteArray clientsTableString = serverController.getTextFileFromContainer(container, credentials, clientsTableFile, &error); if (error != ErrorCode::NoError) { From 3e0a5104e799157f3d398b366a4ce7dc965cf5ee Mon Sep 17 00:00:00 2001 From: KsZnak Date: Mon, 25 Dec 2023 19:18:23 +0200 Subject: [PATCH 10/65] Update amneziavpn_ru.ts --- client/translations/amneziavpn_ru.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index af86594d..58171e67 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -196,7 +196,7 @@ Already installed containers were found on the server. All installed containers All containers from server '%1' have been removed - Все протоклы и сервисы были удалены с сервера '%1' + Все протоколы и сервисы были удалены с сервера '%1' @@ -1703,7 +1703,7 @@ and will not be shared or disclosed to the Amnezia or any third parties What is the level of internet control in your region? - Какой уровень контроля интеренета в вашем регионе? + Какой уровень контроля интернета в вашем регионе? From 67f29ac48311d24c9a908ca94d7c7539e0c13757 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 4 Dec 2023 18:13:19 +0300 Subject: [PATCH 11/65] added conversion using the system locale --- client/core/servercontroller.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 398b46b3..6fac14ea 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -206,8 +206,14 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential localFile.write(data); localFile.close(); +#ifdef Q_OS_WINDOWS + error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toLocal8Bit().toStdString(), remotePath.toStdString(), + "non_desc"); +#else error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc"); +#endif + if (error != ErrorCode::NoError) { return error; } From e8cc80f0464d03e2c6c48d989b71bb647bdcbff3 Mon Sep 17 00:00:00 2001 From: albexk Date: Tue, 26 Dec 2023 16:23:05 +0300 Subject: [PATCH 12/65] Refactor Android open file method Fix some bugs with mimetype filters when the Qt mimetype database does not match the Android database --- .../src/org/amnezia/vpn/AmneziaActivity.kt | 43 +++++++++++++++++++ .../org/amnezia/vpn/qt/QtAndroidController.kt | 2 + .../platforms/android/android_controller.cpp | 36 ++++++++++++++++ client/platforms/android/android_controller.h | 3 ++ client/ui/controllers/systemController.cpp | 19 +++----- 5 files changed, 89 insertions(+), 14 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 11497274..0c0ec0f9 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -2,6 +2,7 @@ package org.amnezia.vpn import android.content.ComponentName import android.content.Intent +import android.content.Intent.EXTRA_MIME_TYPES import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY import android.content.ServiceConnection import android.net.Uri @@ -12,11 +13,13 @@ import android.os.IBinder import android.os.Looper import android.os.Message import android.os.Messenger +import android.webkit.MimeTypeMap import android.widget.Toast import androidx.annotation.MainThread import androidx.core.content.ContextCompat import java.io.IOException import kotlin.LazyThreadSafetyMode.NONE +import kotlin.text.RegexOption.IGNORE_CASE import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -35,6 +38,7 @@ private const val TAG = "AmneziaActivity" private const val CHECK_VPN_PERMISSION_ACTION_CODE = 1 private const val CREATE_FILE_ACTION_CODE = 2 +private const val OPEN_FILE_ACTION_CODE = 3 private const val BIND_SERVICE_TIMEOUT = 1000L class AmneziaActivity : QtActivity() { @@ -201,6 +205,15 @@ class AmneziaActivity : QtActivity() { } } + OPEN_FILE_ACTION_CODE -> { + when (resultCode) { + RESULT_OK -> data?.data?.toString() ?: "" + else -> "" + }.let { uri -> + QtAndroidController.onFileOpened(uri) + } + } + CHECK_VPN_PERMISSION_ACTION_CODE -> { when (resultCode) { RESULT_OK -> { @@ -370,6 +383,36 @@ class AmneziaActivity : QtActivity() { } } + @Suppress("unused") + fun openFile(filter: String?) { + Log.v(TAG, "Open file with filter: $filter") + + val mimeTypes = if (!filter.isNullOrEmpty()) { + val extensionRegex = "\\*\\.[a-z .]+".toRegex(IGNORE_CASE) + val mime = MimeTypeMap.getSingleton() + extensionRegex.findAll(filter).map { + mime.getMimeTypeFromExtension(it.value.drop(2)) + }.filterNotNull().toSet() + } else emptySet() + + Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + Log.d(TAG, "File mimyType filter: $mimeTypes") + when (mimeTypes.size) { + 1 -> type = mimeTypes.first() + + in 2..Int.MAX_VALUE -> { + type = "*/*" + putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray()) + } + + else -> type = "*/*" + } + }.also { + startActivityForResult(it, OPEN_FILE_ACTION_CODE) + } + } + @Suppress("unused") fun setNotificationText(title: String, message: String, timerSec: Int) { Log.v(TAG, "Set notification text") diff --git a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt index bc8cc425..cab810a7 100644 --- a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt +++ b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt @@ -15,6 +15,8 @@ object QtAndroidController { external fun onVpnReconnecting() external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long) + external fun onFileOpened(uri: String) + external fun onConfigImported(data: String) external fun decodeQrCode(data: String): Boolean diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index a739bee3..28bec20e 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "android_controller.h" #include "ui/controllers/importController.h" @@ -106,6 +107,7 @@ bool AndroidController::initialize() {"onVpnDisconnected", "()V", reinterpret_cast(onVpnDisconnected)}, {"onVpnReconnecting", "()V", reinterpret_cast(onVpnReconnecting)}, {"onStatisticsUpdate", "(JJ)V", reinterpret_cast(onStatisticsUpdate)}, + {"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast(onFileOpened)}, {"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast(onConfigImported)}, {"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast(decodeQrCode)} }; @@ -165,6 +167,24 @@ void AndroidController::saveFile(const QString &fileName, const QString &data) QJniObject::fromString(data).object()); } +QString AndroidController::openFile(const QString &filter) +{ + QEventLoop wait; + QString fileName; + connect(this, &AndroidController::fileOpened, this, + [&fileName, &wait](const QString &uri) { + qDebug() << "Android event: file opened; uri:" << uri; + fileName = QQmlFile::urlToLocalFileOrQrc(uri); + qDebug() << "Android opened filename:" << fileName; + wait.quit(); + }, + static_cast(Qt::QueuedConnection | Qt::SingleShotConnection)); + callActivityMethod("openFile", "(Ljava/lang/String;)V", + QJniObject::fromString(filter).object()); + wait.exec(); + return fileName; +} + void AndroidController::setNotificationText(const QString &title, const QString &message, int timerSec) { callActivityMethod("setNotificationText", "(Ljava/lang/String;Ljava/lang/String;I)V", @@ -284,6 +304,22 @@ void AndroidController::onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBy emit AndroidController::instance()->statisticsUpdated((quint64) rxBytes, (quint64) txBytes); } +// static +void AndroidController::onFileOpened(JNIEnv *env, jobject thiz, jstring uri) +{ + Q_UNUSED(thiz); + + const char *buffer = env->GetStringUTFChars(uri, nullptr); + if (!buffer) { + return; + } + + QString lUri(buffer); + env->ReleaseStringUTFChars(uri, buffer); + + emit AndroidController::instance()->fileOpened(lUri); +} + // static void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data) { diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 4e72cbdf..c7933232 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -31,6 +31,7 @@ public: void stop(); void setNotificationText(const QString &title, const QString &message, int timerSec); void saveFile(const QString& fileName, const QString &data); + QString openFile(const QString &filter); void startQrReaderActivity(); signals: @@ -43,6 +44,7 @@ signals: void vpnDisconnected(); void vpnReconnecting(); void statisticsUpdated(quint64 rxBytes, quint64 txBytes); + void fileOpened(QString uri); void configImported(QString config); void importConfigFromOutside(QString config); void initConnectionState(Vpn::ConnectionState state); @@ -65,6 +67,7 @@ private: static void onVpnReconnecting(JNIEnv *env, jobject thiz); static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onConfigImported(JNIEnv *env, jobject thiz, jstring data); + static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri); static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data); template diff --git a/client/ui/controllers/systemController.cpp b/client/ui/controllers/systemController.cpp index 96fc2792..ecd68c8f 100644 --- a/client/ui/controllers/systemController.cpp +++ b/client/ui/controllers/systemController.cpp @@ -60,6 +60,11 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString const QString &selectedFile, const bool isSaveMode, const QString &defaultSuffix) { QString fileName; +#ifdef Q_OS_ANDROID + Q_ASSERT(!isSaveMode); + return AndroidController::instance()->openFile(nameFilter); +#endif + #ifdef Q_OS_IOS MobileUtils mobileUtils; @@ -108,20 +113,6 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString } fileName = mainFileDialog->property("selectedFile").toString(); - -#ifdef Q_OS_ANDROID - // patch for files containing spaces etc - const QString sep { "raw%3A%2F" }; - if (fileName.startsWith("content://") && fileName.contains(sep)) { - QString contentUrl = fileName.split(sep).at(0); - QString rawUrl = fileName.split(sep).at(1); - rawUrl.replace(" ", "%20"); - fileName = contentUrl + sep + rawUrl; - } - - return fileName; -#endif - return QUrl(fileName).toLocalFile(); } From a961932b2e224ac95c2c177cf6a79bf733ce2bc3 Mon Sep 17 00:00:00 2001 From: albexk Date: Tue, 26 Dec 2023 17:00:06 +0300 Subject: [PATCH 13/65] Refactor AndroidUtils --- client/cmake/android.cmake | 4 +- .../platforms/android/android_controller.cpp | 35 +--- client/platforms/android/android_controller.h | 5 +- client/platforms/android/android_utils.cpp | 30 +++ client/platforms/android/android_utils.h | 16 ++ client/platforms/android/androidutils.cpp | 183 ------------------ client/platforms/android/androidutils.h | 49 ----- client/ui/controllers/exportController.cpp | 2 +- client/ui/controllers/pageController.cpp | 2 +- client/ui/controllers/settingsController.cpp | 3 +- 10 files changed, 60 insertions(+), 269 deletions(-) create mode 100644 client/platforms/android/android_utils.cpp create mode 100644 client/platforms/android/android_utils.h delete mode 100644 client/platforms/android/androidutils.cpp delete mode 100644 client/platforms/android/androidutils.h diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake index 2d08b4b6..7ffa680e 100644 --- a/client/cmake/android.cmake +++ b/client/cmake/android.cmake @@ -27,7 +27,7 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android) set(HEADERS ${HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.h - ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.h + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h ) @@ -35,7 +35,7 @@ set(HEADERS ${HEADERS} set(SOURCES ${SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ) diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 28bec20e..225ceebe 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -1,9 +1,10 @@ -#include #include #include #include +#include #include "android_controller.h" +#include "android_utils.h" #include "ui/controllers/importController.h" namespace @@ -129,7 +130,7 @@ auto AndroidController::callActivityMethod(const char *methodName, const char *s const std::function &defValue, Args &&...args) { qDebug() << "Call activity method:" << methodName; - QJniObject activity = QNativeInterface::QAndroidApplication::context(); + QJniObject activity = AndroidUtils::getActivity(); if (activity.isValid()) { return activity.callMethod(methodName, signature, std::forward(args)...); } else { @@ -309,32 +310,15 @@ void AndroidController::onFileOpened(JNIEnv *env, jobject thiz, jstring uri) { Q_UNUSED(thiz); - const char *buffer = env->GetStringUTFChars(uri, nullptr); - if (!buffer) { - return; - } - - QString lUri(buffer); - env->ReleaseStringUTFChars(uri, buffer); - - emit AndroidController::instance()->fileOpened(lUri); + emit AndroidController::instance()->fileOpened(AndroidUtils::convertJString(env, uri)); } // static void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data) { - Q_UNUSED(env); Q_UNUSED(thiz); - const char *buffer = env->GetStringUTFChars(data, nullptr); - if (!buffer) { - return; - } - - QString config(buffer); - env->ReleaseStringUTFChars(data, buffer); - - emit AndroidController::instance()->configImported(config); + emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data)); } // static @@ -342,12 +326,5 @@ bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data) { Q_UNUSED(thiz); - const char *buffer = env->GetStringUTFChars(data, nullptr); - if (!buffer) { - return false; - } - - QString code(buffer); - env->ReleaseStringUTFChars(data, buffer); - return ImportController::decodeQrCode(code); + return ImportController::decodeQrCode(AndroidUtils::convertJString(env, data)); } diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index c7933232..481f4b49 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -18,7 +18,8 @@ public: bool initialize(); // keep synchronized with org.amnezia.vpn.protocol.ProtocolState - enum class ConnectionState { + enum class ConnectionState + { CONNECTED, CONNECTING, DISCONNECTED, @@ -30,7 +31,7 @@ public: ErrorCode start(const QJsonObject &vpnConfig); void stop(); void setNotificationText(const QString &title, const QString &message, int timerSec); - void saveFile(const QString& fileName, const QString &data); + void saveFile(const QString &fileName, const QString &data); QString openFile(const QString &filter); void startQrReaderActivity(); diff --git a/client/platforms/android/android_utils.cpp b/client/platforms/android/android_utils.cpp new file mode 100644 index 00000000..4a994ab0 --- /dev/null +++ b/client/platforms/android/android_utils.cpp @@ -0,0 +1,30 @@ +#include +#include "android_utils.h" + +namespace AndroidUtils +{ + +QJniObject getActivity() +{ + return QNativeInterface::QAndroidApplication::context(); +} + +QString convertJString(JNIEnv *env, jstring data) +{ + int len = env->GetStringLength(data); + QString res(len, Qt::Uninitialized); + env->GetStringRegion(data, 0, len, reinterpret_cast(res.data())); + return res; +} + +void runOnAndroidThreadSync(const std::function &runnable) +{ + QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable).waitForFinished(); +} + +void runOnAndroidThreadAsync(const std::function &runnable) +{ + QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable); +} + +} diff --git a/client/platforms/android/android_utils.h b/client/platforms/android/android_utils.h new file mode 100644 index 00000000..9ed58b75 --- /dev/null +++ b/client/platforms/android/android_utils.h @@ -0,0 +1,16 @@ +#ifndef ANDROID_UTILS_H +#define ANDROID_UTILS_H + +#include + +namespace AndroidUtils +{ +QJniObject getActivity(); + +QString convertJString(JNIEnv *env, jstring data); + +void runOnAndroidThreadSync(const std::function &runnable); +void runOnAndroidThreadAsync(const std::function &runnable); +}; + +#endif // ANDROID_UTILS_H diff --git a/client/platforms/android/androidutils.cpp b/client/platforms/android/androidutils.cpp deleted file mode 100644 index 7cc39824..00000000 --- a/client/platforms/android/androidutils.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "androidutils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "jni.h" - -namespace -{ - AndroidUtils *s_instance = nullptr; -} // namespace - -// static -QString AndroidUtils::GetDeviceName() -{ - QJniEnvironment env; - jclass BUILD = env->FindClass("android/os/Build"); - jfieldID model = env->GetStaticFieldID(BUILD, "MODEL", "Ljava/lang/String;"); - jstring value = (jstring)env->GetStaticObjectField(BUILD, model); - - if (!value) { - return QString("Android Device"); - } - - const char *buffer = env->GetStringUTFChars(value, nullptr); - if (!buffer) { - return QString("Android Device"); - } - - QString res(buffer); - env->ReleaseStringUTFChars(value, buffer); - - return res; -}; - -// static -AndroidUtils *AndroidUtils::instance() -{ - if (!s_instance) { - Q_ASSERT(qApp); - s_instance = new AndroidUtils(qApp); - } - - return s_instance; -} - -AndroidUtils::AndroidUtils(QObject *parent) : QObject(parent) -{ - Q_ASSERT(!s_instance); - s_instance = this; -} - -AndroidUtils::~AndroidUtils() -{ - Q_ASSERT(s_instance == this); - s_instance = nullptr; -} - -// static -void AndroidUtils::dispatchToMainThread(std::function callback) -{ - QTimer *timer = new QTimer(); - timer->moveToThread(qApp->thread()); - timer->setSingleShot(true); - QObject::connect(timer, &QTimer::timeout, [=]() { - callback(); - timer->deleteLater(); - }); - QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection); -} - -// static -QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv *env, jstring data) -{ - const char *buffer = env->GetStringUTFChars(data, nullptr); - if (!buffer) { - qDebug() << "getQByteArrayFromJString - failed to parse data."; - return QByteArray(); - } - - QByteArray out(buffer); - env->ReleaseStringUTFChars(data, buffer); - return out; -} - -// static -QString AndroidUtils::getQStringFromJString(JNIEnv *env, jstring data) -{ - const char *buffer = env->GetStringUTFChars(data, nullptr); - if (!buffer) { - qDebug() << "getQStringFromJString - failed to parse data."; - return QString(); - } - - QString out(buffer); - env->ReleaseStringUTFChars(data, buffer); - return out; -} - -// static -QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv *env, jstring data) -{ - QByteArray raw(getQByteArrayFromJString(env, data)); - QJsonParseError jsonError; - QJsonDocument json = QJsonDocument::fromJson(raw, &jsonError); - if (QJsonParseError::NoError != jsonError.error) { - qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: " << jsonError.error - << "Offset: " << jsonError.offset << "Message: " << jsonError.errorString() << "Data: " << raw; - return QJsonObject(); - } - - if (!json.isObject()) { - qDebug() << "getQJsonObjectFromJString - object expected."; - return QJsonObject(); - } - - return json.object(); -} - -QJniObject AndroidUtils::getActivity() -{ - return QNativeInterface::QAndroidApplication::context(); -} - -int AndroidUtils::GetSDKVersion() -{ - QJniEnvironment env; - jclass versionClass = env->FindClass("android/os/Build$VERSION"); - jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I"); - int sdk = env->GetStaticIntField(versionClass, sdkIntFieldID); - - return sdk; -} - -QString AndroidUtils::GetManufacturer() -{ - QJniEnvironment env; - jclass buildClass = env->FindClass("android/os/Build"); - jfieldID manuFacturerField = env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;"); - jstring value = (jstring)env->GetStaticObjectField(buildClass, manuFacturerField); - - const char *buffer = env->GetStringUTFChars(value, nullptr); - - if (!buffer) { - qDebug() << "Failed to fetch MANUFACTURER"; - return QByteArray(); - } - - QString res(buffer); - qDebug() << "MANUFACTURER: " << res; - env->ReleaseStringUTFChars(value, buffer); - return res; -} - -void AndroidUtils::runOnAndroidThreadSync(const std::function runnable) -{ - QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable).waitForFinished(); -} - -void AndroidUtils::runOnAndroidThreadAsync(const std::function runnable) -{ - QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable); -} - -// Static -// Creates a copy of the passed QByteArray in the JVM and passes back a ref -jbyteArray AndroidUtils::tojByteArray(const QByteArray &data) -{ - QJniEnvironment env; - jbyteArray out = env->NewByteArray(data.size()); - env->SetByteArrayRegion(out, 0, data.size(), reinterpret_cast(data.constData())); - return out; -} diff --git a/client/platforms/android/androidutils.h b/client/platforms/android/androidutils.h deleted file mode 100644 index 8559400c..00000000 --- a/client/platforms/android/androidutils.h +++ /dev/null @@ -1,49 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ANDROIDUTILS_H -#define ANDROIDUTILS_H - -#include - -#include -#include -#include -#include -#include - -class AndroidUtils final : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY_MOVE(AndroidUtils) - -public: - static QString GetDeviceName(); - - static int GetSDKVersion(); - static QString GetManufacturer(); - - static AndroidUtils* instance(); - - static void dispatchToMainThread(std::function callback); - - static QByteArray getQByteArrayFromJString(JNIEnv* env, jstring data); - - static jbyteArray tojByteArray(const QByteArray& data); - - static QString getQStringFromJString(JNIEnv* env, jstring data); - - static QJsonObject getQJsonObjectFromJString(JNIEnv* env, jstring data); - - static QJniObject getActivity(); - - static void runOnAndroidThreadSync(const std::function runnable); - static void runOnAndroidThreadAsync(const std::function runnable); - -private: - AndroidUtils(QObject* parent); - ~AndroidUtils(); -}; - -#endif // ANDROIDUTILS_H diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 9930926f..815a241e 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -15,7 +15,7 @@ #include "core/errorstrings.h" #include "systemController.h" #ifdef Q_OS_ANDROID - #include "platforms/android/androidutils.h" + #include "platforms/android/android_utils.h" #endif #include "qrcodegen.hpp" diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index ed60500a..105f2115 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -7,7 +7,7 @@ #endif #ifdef Q_OS_ANDROID - #include "../../platforms/android/androidutils.h" + #include "platforms/android/android_utils.h" #include #endif #if defined Q_OS_MAC diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 9fa4d76b..4656e4a8 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -7,8 +7,7 @@ #include "ui/qautostart.h" #include "version.h" #ifdef Q_OS_ANDROID - #include "../../platforms/android/android_controller.h" - #include "../../platforms/android/androidutils.h" + #include "platforms/android/android_utils.h" #include #endif From aa41e4d91583b8763c0a6be94b5d016b7b95c16b Mon Sep 17 00:00:00 2001 From: Morteza Sherafati Date: Sat, 30 Dec 2023 11:08:42 +0000 Subject: [PATCH 14/65] full persian translation --- client/translations/amneziavpn_fa_IR.ts | 279 +++++++++++++----------- 1 file changed, 154 insertions(+), 125 deletions(-) diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index 7471208b..bf93a619 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -15,15 +15,13 @@ AndroidController - AmneziaVPN - AmneziaVPN + AmneziaVPN - VPN Connected Refers to the app - which is currently running the background and waiting - وی‎پی‎ان متصل است + وی‎پی‎ان متصل است @@ -31,7 +29,7 @@ Error when retrieving configuration from cloud server - + خطا در حین دریافت پیکربندی از سمت سرور @@ -46,7 +44,7 @@ Connection... - ارتباط + در حال ارتباط... @@ -62,12 +60,12 @@ Settings updated successfully - + تنظیمات با موفقیت به‎روز‎رسانی شدند Reconnection... - در حال اتصال دوباره... + اتصال دوباره... @@ -152,7 +150,7 @@ ImportController - + Scanned %1 of %2. ارزیابی %1 از %2. @@ -245,12 +243,12 @@ Already installed containers were found on the server. All installed containers VPN Connected - وی‎پی‎ان متصل است + وی‎پی‎ان وصل شد VPN Disconnected - وی‎پی‎ان قطع است + وی‎پی‎ان قطع شد @@ -1430,22 +1428,22 @@ Already installed containers were found on the server. All installed containers نام سرور - + Save ذخیره - + Protocols پروتکل‎ها - + Services سرویس‎ها - + Data داده @@ -1682,7 +1680,7 @@ and will not be shared or disclosed to the Amnezia or any third parties All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties - + تمام داده‎هایی که شما وارد می‎کنید به شدت محرمانه‎ است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمی‎شود @@ -1766,7 +1764,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Cancel installation - + لغو عملیات نصب @@ -1918,12 +1916,12 @@ and will not be shared or disclosed to the Amnezia or any third parties OpenVpn native format - فرمت محلی OpenVPN + فرمت OpenVPN WireGuard native format - فرمت محلی WireGuard + فرمت WireGuard VPN Access @@ -1943,8 +1941,8 @@ and will not be shared or disclosed to the Amnezia or any third parties Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. - + Server سرور @@ -1959,7 +1957,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Config revoked - + تنظیمات ابطال‎شد @@ -1984,12 +1982,12 @@ and will not be shared or disclosed to the Amnezia or any third parties Save ShadowSocks config - + ذخیره تنظیمات ShadowSocks Save Cloak config - + ذخیره تنظیمات Cloak @@ -1999,12 +1997,12 @@ and will not be shared or disclosed to the Amnezia or any third parties ShadowSocks native format - + فرمت ShadowSocks Cloak native format - + فرمت Cloak @@ -2014,68 +2012,77 @@ and will not be shared or disclosed to the Amnezia or any third parties Share full access to the server and VPN - + به اشتراک گذاشتن دسترسی کامل به سرور و وی‎پی‎ان Use for your own devices, or share with those you trust to manage the server. - + برای دستگاه‎های خودتان استفاده کنید یا با آنهایی که برای مدیریت سرور به آن‎ها اعتماد دارید به اشتراک بگذارید. - + Users - + کاربران User name - + نام کاربری - + Search + جستجو + + + + Creation date: - + Rename - + تغییر نام - + Client name - + نام کلاینت - + Save - ذخیره + ذخیره - + Revoke + ابطال + + + + Revoke the config for a user - %1? - Revoke the config for a user - - + ابطال تنظیمات برای کاربر - + The user will no longer be able to connect to your server. - + کاربر دیگر نمی‎تواند به سرور وصل شود. - + Continue - + ادامه - + Cancel - کنسل + کنسل Full access @@ -2091,20 +2098,20 @@ and will not be shared or disclosed to the Amnezia or any third parties به اشتراک گذاری دسترسی به مدیریت سرور. کاربری که دسترسی کامل سرور با او به اشتراک گذاشته می‎شود می‎تواند پروتکل‌‎ها و سرویس‎ها را در سرور حذف یا اضافه کند و یا تنظیمات سرور را تغییر دهد. - + Protocol پروتکل - + Connection format فرمت ارتباط - + Share اشتراک‎گذاری @@ -2114,49 +2121,50 @@ and will not be shared or disclosed to the Amnezia or any third parties Full access to the server and VPN - + دسترسی کامل به سرور و وی‎پی‎ان We recommend that you use full access to the server only for your own additional devices. - + ما پیشنهاد میکنیم که ازحالت دسترسی کامل به سرور فقط برای دستگاه‎های دیگر خودتان استفاده کنید + If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. - + اگر دسترسی کامل را با دیگران به اشتراک بگذارید، آن‎ها می‎توانند پروتکل‎ها و سرویس‎ها را حذف یا اضافه کنند که باعث می‎شود که وی‎پی‎ان دیگر برای سایر کاربران کار نکند. Server - + سرور - + Accessing - در حال دسترسی به + در حال دسترسی به - + File with accessing settings to - فایل شامل تنظیمات دسترسی به + فایل شامل تنظیمات دسترسی به Share - اشتراک‎گذاری + اشتراک‎گذاری Connection to - ارتباط با + ارتباط با File with connection settings to - فایل شامل تنظیمات ارتباط با + فایل شامل تنظیمات ارتباط با @@ -2547,6 +2555,11 @@ and will not be shared or disclosed to the Amnezia or any third parties The config does not contain any containers and credentials for connecting to the server + تنظیمات شامل هیچ کانتینر یا اعتبارنامه‎ای برای اتصال به سرور نیست + + + + VPN connection error @@ -2608,7 +2621,7 @@ and will not be shared or disclosed to the Amnezia or any third parties The config does not contain any containers and credentiaks for connecting to the server - + Internal error Internal error @@ -2628,31 +2641,31 @@ While it offers a blend of security, stability, and speed, it's essential t * Minimal configuration * Recognised by DPI analysis systems * Works over UDP network protocol, ports 500 and 4500. - IKEv2 в сочетании с уровнем шифрования IPSec это современный и стабильный протокол VPN. -Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. -Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке. + پروتکل IKEv2 به همراه لایه رمزنگاری IPSec به عنوان پروتکل وی‎پی‎ان مدرن و پایدار است. +یکی از قابلیت‎‎های متمایز این پروتکل قابلیت سوییچ بین شبکه‎ها و دستگاه‎هاست که قابلیت انطباق بالایی در محیط شبکه‎های دینامیک را دارد +در حالیکه ترکیبی از امنیت، پایداری و سرعت را ارائه میدهد اما مهم است که اشاره کنیم IKEv2 به راحتی قابل تشخیص در شبکه و بلاک شدن میباشد. -* Доступно в AmneziaVPN только для Windows. -* Низкое энергопотребление, на мобильных устройствах -* Минимальная конфигурация -* Распознается системами DPI-анализа -* Работает по сетевому протоколу UDP, порты 500 и 4500. +* در AmneziaVPN فقط بر روی ویندوز در دسترس است +* مصرف باتری کم روی دستگاه‎های موبایل +* تنظیمات ساده +* امکان شناسایی شدن در شبکه‎های تحلیل DPI +* روی پروتکل شبکه UDP، پورت‎های 500 و 4500 کار می‎کند. DNS Service - DNS Сервис + سرویس DNS Sftp file sharing service - Сервис обмена файлами Sftp + سرویس اشتراک گذاری فایل Sftp Website in Tor network - Веб-сайт в сети Tor + وب سایت در شبکه Tor @@ -2662,47 +2675,47 @@ While it offers a blend of security, stability, and speed, it's essential t OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its own security protocol with SSL/TLS for key exchange. - OpenVPN - популярный VPN-протокол, с гибкой настройкой. Имеет собственный протокол безопасности с SSL/TLS для обмена ключами. + پروتکل OpenVPN یکی از پروتکل‎های وی‎پی‎ان محبوب می‎باشد با تنظیمات و پیکربندی‎های قابل تغییر. از پروتکل امنیتی داخلی خود با تبادل کلید SSL/TLS استفاده می‎کند. ShadowSocks - masks VPN traffic, making it similar to normal web traffic, but is recognised by analysis systems in some highly censored regions. - ShadowSocks - маскирует VPN-трафик под обычный веб-трафик, но распознается системами анализа в некоторых регионах с высоким уровнем цензуры. + پروتکل ShadowSocks ترافیک وی‎پی‎ان را پنهان و آن را شبیه ترافیک عادی وب می‎کند، اما در مناطقی که سانسور شدیدی اعمال می‎شود با سیستم‎های تحلیلی قابل شناسایی است. OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probbing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN over Cloak - OpenVPN с маскировкой VPN под web-трафик и защитой от обнаружения active-probbing. Подходит для регионов с самым высоким уровнем цензуры. + پروتکل OpenVPN over Cloak که همان پروتکل OpenVPN با قابلیت پنهان کردن ترافیک از سیستم‎های تحلیل فعال برروی شبکه. ایده‎آل برای گذر از ممنوعیت در مناطقی که سانسور شدیدی اعمال می‎کنند. WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - WireGuard - Популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением. Для регионов с низким уровнем цензуры. + پروتکل WireGuard یک پروتکل وی‎پی‎ان جدید با عملکرد بسیار خوب، سرعت بالا و مصرف انرژی پایین. برای مناطقی که سطح سانسور پایینی دارند پیشنهاد می‎شود. AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - Специальный протокол от Amnezia, основанный на протоколе WireGuard. Он такой же быстрый, как WireGuard, но очень устойчив к блокировкам. Рекомендуется для регионов с высоким уровнем цензуры. + پروتکل AmneziaWG یک پروتکل اختصاصی Amnezia که بر اساس WireGaurd کار میکند. به اندازه WireGaurd پرسرعت است و در عین حال بسیار مقاوم به بلاک شدن توسط شبکه ست. مناسب برای مناطق با سطح سانسور بالاست. IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. - IKEv2 Современный стабильный протокол, немного быстрее других восстанавливает соединение после потери сигнала. Имеет нативную поддержку последних версиий Android и iOS. + پروتکل IKEv2 پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکل‎هاست. بعد از قطع سیگنال دوباره اتصال را بازیابی می‎کند. به صورت پیش‎فرض بر روی آخرین نسخه دستگاه‎های اندروید و iOS پیشتیبانی می‎شود. Deploy a WordPress site on the Tor network in two clicks. - Разверните сайт на WordPress в сети Tor в два клика. + با دو کلیک یک سایت وردپرس در شبکه Tor راه‎اندازی کنید. Replace the current DNS server with your own. This will increase your privacy level. - Замените DNS-сервер на Amnezia DNS. Это повысит уровень конфиденциальности. + سرور DNS را با مال خودتان جایگزین کنید. این کار سطح حریم خصوصی شما را افزایش می‎دهد. Creates a file vault on your server to securely store and transfer files. - Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов. + یک محفظه ایمن بر روی سرور خودتان ایجاد کنید که به طور امن بتوانید فایل‎ها را ذخیره و جابجا کنید. @@ -2714,14 +2727,18 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN однин из самых популярных и проверенных временем VPN-протоколов. -В нем используется уникальный протокол безопасности, опирающийся на протокол SSL/TLS для шифрования и обмена ключами. Кроме того, поддержка OpenVPN множества методов аутентификации делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Благодаря оптимальному соотношению производительности, безопасности и совместимости OpenVPN остается лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности. + پروتکل OpenVPN یکی از پروتکل‎های محبوب و تست شده در دسترس می‎باشد که از پروتکل امنیتی مخصوص خودش استفاده میکند. +از امتیازات SSL/TLS برای رمزنگاری و تبادل کلید استفاده میکند. +همچنین OpenVPN از روش‎های چندگانه‎ای برای احراز هویت پشتیبانی می‎کند که آن را قابل انطباق و منعطف میکند. +از طیف وسیعی از دستگاه‎ها و سیستم عامل‎ها نیز پشتیبانی می‎کند. +به دلیل طبیعت متن-باز آن، OpenVPN از بررسی گسترده توسط یک جامعه جهانی سود می‎برد که باعث بهتر شدن وضعیت امنیتی آن می‎شود. +به دلیل تعادل قوی بین عملکرد، امنیت و سازگاری OpenVPN تبدیل به یکی از انتخاب‎های اصلی برای اشخاص آگاه بر حریم خصوصی و تجارت‎های مشابه شده است. -* Доступность AmneziaVPN для всех платформ -* Нормальное энергопотребление на мобильных устройствах -* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами -* Распознается системами DPI-анализа и поэтому подвержен блокировке -* Может работать по сетевым протоколам TCP и UDP. +* بر روی تمام سیستم‎عامل‎ها در AmneziaVPN در دسترس است. +* مصرف انرژی عادی بر روی دستگاه‎های موبایل +* قابلیت شخصی‎سازی منعطف مطابق با نیاز شما که امکان کار بر روی دستگاه‎ها و سیستم عامل‎های مختلف را می‎دهد. +* قابل شناسایی توسط سیستم‎های تحلیل عمیق DPI در شبکه و در نتیجه امکان بلاک شدن +* امکان کار بر روی دو پروتکل TCP و UDP @@ -2733,12 +2750,13 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Configurable encryption protocol * Detectable by some DPI systems * Works over TCP network protocol. - Shadowsocks, создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Однако некоторые системы анализа трафика все же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG, или OpenVPN over Cloak. + پروتکل Shadowsocks، الهام گرفته از پروتکل Socks5، اتصال را با استفاده از رمزگذاری AEAD امن میکند. اگرچه Shadowsocks طوری طراحی شده که برای شناسایی در شبکه چالش‎برانگیز باشد و محتاط عمل کند اما این پروتکل مانند یک اتصال استاندارد HTTPS نیست و برخی از سیستم‎های تحلیل ترافیک مشخص ممکن است بتوانند اتصال Shadowsocks را شناسایی کنند. به دلیل محدودیت پشتیبانی در Amnezia پیشنهاد می‎شود که از َAmneziaWG استفاده شود. -* Доступен в AmneziaVPN только на ПК ноутбуках. -* Настраиваемый протокол шифрования -* Обнаруживается некоторыми DPI-системами -* Работает по сетевому протоколу TCP. +* فقط بر روی پلتفرم دسکتاپ بر روی Amnezia قابل دسترس است +* مصرف انرژی عادی در دستگاه‎های موبایل +* پروتکل رمزنگاری قابل پیکربندی +* قابل شناسایی توسط برخی سیستم‎های تحلیل عمیق DPI +* عملکرد بر روی پروتکل شبکه TCP @@ -2760,23 +2778,24 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от блокировок. + این یک ترکیب از پروتکل OpenVPN و افزونه Cloak می‎باشد که به طور خاص برای محافظت از بلاک شدن طراحی شده است. -OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером. +پروتکل OpenVPN با رمزنگاری تمام ترافیک اینترنت بین دستگاه و سرور یک اتصال وی‎پی‎ان امن را فراهم می‎کند. -Cloak защищает OpenVPN от обнаружения и блокировок. +افزونه Cloak از OpenVPN در مقابل شناسایی و بلاک شدن محافظت می‎کند -Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению +افزونه Cloak می‎تواند داده‎های بسته ترافیکی را تغییر دهد و در نتیجه ترافیک وی‎پی‎ان شبیه ترافیک عادی وب می‎شود و همچنین از وی‎پی‎ان در مقابل شناسایی شدن توسط DPI محافظت می‎کند. این باعث می‎شود که این پروتکل به شناسایی‎شدن بسیار مقاوم باشد -Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. +درست بعد از دریافت اولین بسته داده،افزونه Cloak اتصال ورودی را احراز هویت می‎کند و اگر عملیات احراز هویت انجام نشود Cloak سرور را به عنوان یک وب سایت جعلی در‎ می‎آورد و وی‎پی‎ان شما را از تحلیل شبکه پنهان می‎کند. -Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak +اگر در منطقه شما سطح بالایی از سانسور وجود دارد ما به شما پیشنهاد می‎کنیم از اولین ارتباط تنها از OpenVPN over Cloak استفاده کنید. -* Доступность AmneziaVPN на всех платформах -* Высокое энергопотребление на мобильных устройствах -* Гибкие настройки -* Не распознается системами DPI-анализа -* Работает по сетевому протоколу TCP, 443 порт. + +* بر روی تمام پلتفرم‎ها در AmneziaVPN در دسترس است +* مصرف بالای انرژی در دستگاه‎های موبایل +* تنظیمات منطعف +* غیرقابل تشخیص و شناسایی توسط سیستم‎های تحلیل عمیق DPI +* کار کردن روی پروتکل شبکه TCP، پورت 443 @@ -2790,15 +2809,15 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой. -Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. -WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга. + یک پروتکل نسبتا محبوب وی‎پی‎ان با معماری ساده +اتصال وی‎پی‎‎ان پایدار با عملکرد بالا بر روی تمام دستگاه‎‌ها فراهم می‎کند. از تنظیمات ثابت برای رمزنگاری استفاده می‎کند و در مقایسه با OpenVPN سرعت بهتری در انتقال اطلاعات دارد. +پروتکل WireGaurd به دلیل امضای بسته داده مخصوص، احتمال بسیار بالایی برای شناسایی و بلاک شدن دارد.برعکس سایر پروتکل‎های وی‎پی‎ان که از روش‎های مخفی کردن استفاده می‎کنند، امضای ثابت WireGuard به راحتی می‎تواند توسط سیستم‎های تحلیل عمیق DPI یا سایر روش‎های بررسی شبکه شناسایی و بلاک شود. -* Доступность AmneziaVPN для всех платформ -* Низкое энергопотребление -* Минимальное количество настроек -* Легко распознается системами DPI-анализа, подвержен блокировке -* Работает по сетевому протоколу UDP. +* بر روی تمام پلتفرم‌ها در AmneziaVPN قابل دسترسی است. +* مصرف انرژی پایین +* کمترین میزان تنظیمات +* امکان شناسایی شدن توسط سیستم‎های تحلیل عمیق DPI به آسانی و بلاک شدن +* کار بر روی پروتکل شبکه UDP @@ -2811,15 +2830,15 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - AmneziaWG - усовершенствованная версия популярного VPN-протокола Wireguard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокопроизводительные возможности работы на разных устройствах. -Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком. -Таким образом, AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. + یک نسخه مدرن از پروتکل وی‎پی‎ان محبوب، AmneziaWG بر روی پایه‎های WireGuard ساخته شده و معماری ساده و عملکرد بالای آن را بر روی تمام دستگاه‎ها حفظ کرده است. +در حالی‎که WireGuard به دلیل بازدهی آن شناخته می‎شود اما امکان شناسایی شدن بالا به دلیل امضای ثابت بسته داده‎های آن یکی از مشکلات آن است. AmneziaWG این مشکل را با استفاده از متدهای مخفی سازی حل کرده و در نتیجه ترافیک آن همانند با ترافیک عادی اینترنت است. +این بدین معنی است که AmneziaWG عملکرد سریع اصلی را حفظ کرده و یک لایه پنهان سازی به آن اضافه کرده که باعث می‎شود که به انتخابی عالی برای آنها که وی‎پی‎ان امن و سریع می‎خواهند تبدیل شود. -* Доступность AmneziaVPN на всех платформах -* Низкое энергопотребление -* Минимальное количество настроек -* Не распознается системами DPI-анализа, устойчив к блокировке -* Работает по сетевому протоколу UDP. +* بر روی تمام پلتفرم‌ها در AmneziaVPN قابل دسترسی است. +* مصرف انرژی پایین +* کمترین میزان تنظیمات +* غیرقابل تشخیص توسط سیستم‎های تحلیل عمیق DPI و مقاوم به بلاک شدن +* کار بر روی پروتکل شبکه UDP AmneziaWG container @@ -2828,12 +2847,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin Sftp file sharing service - is secure FTP service - Сервис обмена файлами Sftp - безопасный FTP-сервис + سرویس اشتراک فایل Sftp یک سرویس امن FTP می‎باشد Sftp service - Сервис SFTP + سرویس Sftp @@ -2895,6 +2914,16 @@ This means that AmneziaWG keeps the fast performance of the original while addin error 0x%1: %2 error 0x%1: %2 + + + WireGuard Configuration Highlighter + هایلایتر پیکربندی WireGuard + + + + &Randomize colors + رنگ‎های تصادفی + SelectLanguageDrawer @@ -2968,7 +2997,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin Copy config string - + کپی‎کردن متن تنظیمات @@ -3048,7 +3077,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin Visit Website - بازدید وب‎سایت + بازدید وب سایت @@ -3068,7 +3097,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps From bb8a11d110c45510b2ceed16bcc41df1269d7002 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 6 Jan 2024 20:38:41 +0700 Subject: [PATCH 15/65] added reset routeMode when resetting application settings --- client/amnezia_application.cpp | 2 +- client/ui/controllers/settingsController.cpp | 3 +++ client/ui/controllers/settingsController.h | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 2e3b7866..fe360818 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -361,7 +361,7 @@ void AmneziaApplication::initControllers() m_settings, m_configurator)); m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); - m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings)); + m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings)); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 9fa4d76b..73b9d276 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -15,11 +15,13 @@ SettingsController::SettingsController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &languageModel, + const QSharedPointer &sitesModel, const std::shared_ptr &settings, QObject *parent) : QObject(parent), m_serversModel(serversModel), m_containersModel(containersModel), m_languageModel(languageModel), + m_sitesModel(sitesModel), m_settings(settings) { m_appVersion = QString("%1: %2 (%3)").arg(tr("Software version"), QString(APP_VERSION), __DATE__); @@ -134,6 +136,7 @@ void SettingsController::clearSettings() m_serversModel->resetModel(); m_languageModel->changeLanguage( static_cast(m_languageModel->getCurrentLanguageIndex())); + m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites); emit changeSettingsFinished(tr("All settings have been reset to default values")); } diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index 710d255f..a6cbc587 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -6,6 +6,7 @@ #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/servers_model.h" +#include "ui/models/sites_model.h" class SettingsController : public QObject { @@ -14,6 +15,7 @@ public: explicit SettingsController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, const QSharedPointer &languageModel, + const QSharedPointer &sitesModel, const std::shared_ptr &settings, QObject *parent = nullptr); Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged) @@ -76,6 +78,7 @@ private: QSharedPointer m_serversModel; QSharedPointer m_containersModel; QSharedPointer m_languageModel; + QSharedPointer m_sitesModel; std::shared_ptr m_settings; QString m_appVersion; From 0690d86e520e191c236a95d3b465bea62de348d6 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 6 Jan 2024 20:42:43 +0700 Subject: [PATCH 16/65] added the ability to change the site link via a translation file --- client/ui/qml/Pages2/PageSettingsAbout.qml | 2 +- client/ui/qml/Pages2/PageSetupWizardStart.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index eaa9eb3d..b387cc64 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -101,7 +101,7 @@ PageType { text: qsTr("Show other methods on Github") - onClicked: Qt.openUrlExternally("https://github.com/amnezia-vpn/amnezia-client#donate") + onClicked: Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client#donate")) } ParagraphTextType { diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index 2f89bc57..a820ac71 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -135,7 +135,7 @@ PageType { text: qsTr("I have nothing") - onClicked: Qt.openUrlExternally("https://amnezia.org/instructions/0_starter-guide") + onClicked: Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide")) } } From c1b6149e4944b775e06ae5768c779ea51e939bd9 Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Sat, 6 Jan 2024 20:41:06 +0400 Subject: [PATCH 17/65] Improve servercontroller.cpp for Ubuntu (#482) Improve servercontroller.cpp for Ubuntu, CentOS, snd Fedora --- client/core/controllers/serverController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 36560e10..f4c58b10 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -168,7 +168,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, } else return ErrorCode::NotImplementedError; - if (stdOut.contains("Error: No such container:")) { + if (stdOut.contains("Error") && stdOut.contains("No such container")) { return ErrorCode::ServerContainerMissingError; } From 2a03834bb240d78944d77f01b7cf40e5c6b8eb54 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 9 Jan 2024 00:25:18 +0700 Subject: [PATCH 18/65] fixed current processed server selection after import/install new server --- client/ui/models/servers_model.cpp | 5 +++++ .../ui/qml/Components/HomeContainersListView.qml | 16 ++++++++++++++++ client/ui/qml/Pages2/PageHome.qml | 1 + .../ui/qml/Pages2/PageSetupWizardInstalling.qml | 1 + .../ui/qml/Pages2/PageSetupWizardViewConfig.qml | 1 + 5 files changed, 24 insertions(+) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index b8bfbbc5..feaf1e8d 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -15,6 +15,10 @@ ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString()); emit ServersModel::defaultContainerChanged(defaultContainer); }); + connect(this, &ServersModel::currentlyProcessedServerIndexChanged, this, [this](const int serverIndex) { + auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString()); + emit ServersModel::defaultContainerChanged(defaultContainer); + }); } int ServersModel::rowCount(const QModelIndex &parent) const @@ -269,6 +273,7 @@ void ServersModel::removeServer() if (m_settings->serversCount() == 0) { setDefaultServerIndex(-1); } + setCurrentlyProcessedServerIndex(m_defaultServerIndex); endResetModel(); } diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index 78ea9330..2c07ca65 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -26,6 +26,22 @@ ListView { id: containersRadioButtonGroup } + Connections { + target: ServersModel + + function onCurrentlyProcessedServerIndexChanged() { + menuContent.checkCurrentItem() + } + } + + function checkCurrentItem() { + var item = menuContent.itemAtIndex(currentIndex) + if (item !== null) { + var radioButton = item.children[0].children[0] + radioButton.checked = true + } + } + delegate: Item { implicitWidth: rootWidth implicitHeight: content.implicitHeight diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 8374dbc3..5b44bc7c 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -31,6 +31,7 @@ PageType { containersDropDown.rootButtonClickedFunction() } } + function onForceCloseDrawer() { buttonContent.state = "collapsed" } diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index 9811d87d..1a3e7c07 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -42,6 +42,7 @@ PageType { function onInstallServerFinished(finishedMessage) { if (!ConnectionController.isConnected) { ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); + ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex } PageController.goToStartPage() diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 65a6f319..6df26fc0 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -26,6 +26,7 @@ PageType { function onImportFinished() { if (!ConnectionController.isConnected) { ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); + ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex } PageController.goToStartPage() From 673b8ad5b2ac397e9b751afe051a2c139176d110 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 10 Jan 2024 11:55:32 +0700 Subject: [PATCH 19/65] fixed insert rows count in appendClient function --- client/ui/models/clientManagementModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index f1576d08..5c89382b 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -231,7 +231,7 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt } } - beginInsertRows(QModelIndex(), rowCount(), 1); + beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1); QJsonObject client; client[configKey::clientId] = clientId; From 645cf52803cc38271b1f55395922008897b3214d Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 10 Jan 2024 23:05:22 +0700 Subject: [PATCH 20/65] now value and setValue of secureQSettings are always called in the main thread --- client/secure_qsettings.h | 6 +++-- client/settings.cpp | 47 ++++++++++++++++++++++++++++++++------- client/settings.h | 45 ++++++++++++++++++++----------------- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h index 7421ce01..89147fc1 100644 --- a/client/secure_qsettings.h +++ b/client/secure_qsettings.h @@ -14,12 +14,14 @@ constexpr const char *keyChainName = "AmneziaVPN-Keychain"; class SecureQSettings : public QObject { + Q_OBJECT + public: explicit SecureQSettings(const QString &organization, const QString &application = QString(), QObject *parent = nullptr); - QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; - void setValue(const QString &key, const QVariant &value); + Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + Q_INVOKABLE void setValue(const QString &key, const QVariant &value); void remove(const QString &key); void sync(); diff --git a/client/settings.cpp b/client/settings.cpp index 4757dba6..f3fb57a7 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -1,4 +1,8 @@ #include "settings.h" + +#include "QThread" +#include "QCoreApplication" + #include "utilities.h" #include "version.h" @@ -12,10 +16,10 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N { // Import old settings if (serversCount() == 0) { - QString user = m_settings.value("Server/userName").toString(); - QString password = m_settings.value("Server/password").toString(); - QString serverName = m_settings.value("Server/serverName").toString(); - int port = m_settings.value("Server/serverPort").toInt(); + QString user = value("Server/userName").toString(); + QString password = value("Server/password").toString(); + QString serverName = value("Server/serverName").toString(); + int port = value("Server/serverPort").toInt(); if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) { QJsonObject server; @@ -211,7 +215,7 @@ QString Settings::nextAvailableServerName() const void Settings::setSaveLogs(bool enabled) { - m_settings.setValue("Conf/saveLogs", enabled); + setValue("Conf/saveLogs", enabled); if (!isSaveLogs()) { Logger::deInit(); } else { @@ -233,7 +237,7 @@ QString Settings::routeModeString(RouteMode mode) const Settings::RouteMode Settings::routeMode() const { - return static_cast(m_settings.value("Conf/routeMode", 0).toInt()); + return static_cast(value("Conf/routeMode", 0).toInt()); } bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip) @@ -321,12 +325,12 @@ void Settings::removeAllVpnSites(RouteMode mode) QString Settings::primaryDns() const { - return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString(); + return value("Conf/primaryDns", cloudFlareNs1).toString(); } QString Settings::secondaryDns() const { - return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString(); + return value("Conf/secondaryDns", cloudFlareNs2).toString(); } void Settings::clearSettings() @@ -351,3 +355,30 @@ ServerCredentials Settings::serverCredentials(int index) const return credentials; } + +QVariant Settings::value(const QString &key, const QVariant &defaultValue) const +{ + QVariant returnValue; + if (QThread::currentThread() == QCoreApplication::instance()->thread()) { + returnValue = m_settings.value(key, defaultValue); + } else { + QMetaObject::invokeMethod(&m_settings, "value", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariant, returnValue), + Q_ARG(const QString&, key), + Q_ARG(const QVariant&, defaultValue)); + } + return returnValue; +} + +void Settings::setValue(const QString &key, const QVariant &value) +{ + if (QThread::currentThread() == QCoreApplication::instance()->thread()) { + m_settings.setValue(key, value); + } else { + QMetaObject::invokeMethod(&m_settings, "setValue", + Qt::BlockingQueuedConnection, + Q_ARG(const QString&, key), + Q_ARG(const QVariant&, value)); + } +} diff --git a/client/settings.h b/client/settings.h index f530f6c5..50a28dc4 100644 --- a/client/settings.h +++ b/client/settings.h @@ -29,11 +29,11 @@ public: QJsonArray serversArray() const { - return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array(); + return QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array(); } void setServersArray(const QJsonArray &servers) { - m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson()); + setValue("Servers/serversList", QJsonDocument(servers).toJson()); } // Servers section @@ -45,11 +45,11 @@ public: int defaultServerIndex() const { - return m_settings.value("Servers/defaultServerIndex", 0).toInt(); + return value("Servers/defaultServerIndex", 0).toInt(); } void setDefaultServer(int index) { - m_settings.setValue("Servers/defaultServerIndex", index); + setValue("Servers/defaultServerIndex", index); } QJsonObject defaultServer() const { @@ -78,25 +78,25 @@ public: // App settings section bool isAutoConnect() const { - return m_settings.value("Conf/autoConnect", false).toBool(); + return value("Conf/autoConnect", false).toBool(); } void setAutoConnect(bool enabled) { - m_settings.setValue("Conf/autoConnect", enabled); + setValue("Conf/autoConnect", enabled); } bool isStartMinimized() const { - return m_settings.value("Conf/startMinimized", false).toBool(); + return value("Conf/startMinimized", false).toBool(); } void setStartMinimized(bool enabled) { - m_settings.setValue("Conf/startMinimized", enabled); + setValue("Conf/startMinimized", enabled); } bool isSaveLogs() const { - return m_settings.value("Conf/saveLogs", false).toBool(); + return value("Conf/saveLogs", false).toBool(); } void setSaveLogs(bool enabled); @@ -110,15 +110,15 @@ public: QString routeModeString(RouteMode mode) const; RouteMode routeMode() const; - void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); } + void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); } QVariantMap vpnSites(RouteMode mode) const { - return m_settings.value("Conf/" + routeModeString(mode)).toMap(); + return value("Conf/" + routeModeString(mode)).toMap(); } void setVpnSites(RouteMode mode, const QVariantMap &sites) { - m_settings.setValue("Conf/" + routeModeString(mode), sites); + setValue("Conf/" + routeModeString(mode), sites); m_settings.sync(); } bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = ""); @@ -132,11 +132,11 @@ public: bool useAmneziaDns() const { - return m_settings.value("Conf/useAmneziaDns", true).toBool(); + return value("Conf/useAmneziaDns", true).toBool(); } void setUseAmneziaDns(bool enabled) { - m_settings.setValue("Conf/useAmneziaDns", enabled); + setValue("Conf/useAmneziaDns", enabled); } QString primaryDns() const; @@ -145,13 +145,13 @@ public: // QString primaryDns() const { return m_primaryDns; } void setPrimaryDns(const QString &primaryDns) { - m_settings.setValue("Conf/primaryDns", primaryDns); + setValue("Conf/primaryDns", primaryDns); } // QString secondaryDns() const { return m_secondaryDns; } void setSecondaryDns(const QString &secondaryDns) { - m_settings.setValue("Conf/secondaryDns", secondaryDns); + setValue("Conf/secondaryDns", secondaryDns); } static const char cloudFlareNs1[]; @@ -171,20 +171,20 @@ public: QLocale getAppLanguage() { - return m_settings.value("Conf/appLanguage", QLocale()).toLocale(); + return value("Conf/appLanguage", QLocale()).toLocale(); }; void setAppLanguage(QLocale locale) { - m_settings.setValue("Conf/appLanguage", locale); + setValue("Conf/appLanguage", locale); }; bool isScreenshotsEnabled() const { - return m_settings.value("Conf/screenshotsEnabled", false).toBool(); + return value("Conf/screenshotsEnabled", false).toBool(); } void setScreenshotsEnabled(bool enabled) { - m_settings.setValue("Conf/screenshotsEnabled", enabled); + setValue("Conf/screenshotsEnabled", enabled); } void clearSettings(); @@ -193,7 +193,10 @@ signals: void saveLogsChanged(); private: - SecureQSettings m_settings; + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + void setValue(const QString &key, const QVariant &value); + + mutable SecureQSettings m_settings; }; #endif // SETTINGS_H From 7a435f76b6bbfb203f4421c5e1b5082acea85ed5 Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Thu, 11 Jan 2024 00:15:14 +0400 Subject: [PATCH 21/65] ArchLinux_support (#463) Arch linux support (#464) --- client/server_scripts/check_server_is_busy.sh | 3 ++- client/server_scripts/install_docker.sh | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/server_scripts/check_server_is_busy.sh b/client/server_scripts/check_server_is_busy.sh index e1ba63f8..4e6a2c26 100644 --- a/client/server_scripts/check_server_is_busy.sh +++ b/client/server_scripts/check_server_is_busy.sh @@ -1,5 +1,6 @@ if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\ elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\ elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\ +elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\ else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\ -if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi +if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index 6f19e090..6fed78c0 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -1,6 +1,7 @@ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\ +elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="> /dev/null 2>&1"; docker_pkg="docker"; dist="archlinux";\ else echo "Packet manager not found"; exit 1; fi;\ echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\ if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\ From 145f51906e042b8daca4d9d157592160ebbef1b8 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 14 Jan 2024 21:58:53 +0200 Subject: [PATCH 22/65] Update AWG core (v0.1.8) --- client/3rd-prebuilt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index fcf3022a..e568e7d0 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit fcf3022a2724402f68cc11bcbed9b43ea9ffcc07 +Subproject commit e568e7d0e8defe8fe009c0127323f2c55fd9be76 From ae2872830bc87662c3574a72c72444df70a7a81f Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 15 Jan 2024 09:20:13 +0000 Subject: [PATCH 23/65] Updated submodule qtkeychain --- client/3rd/qtkeychain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/3rd/qtkeychain b/client/3rd/qtkeychain index 8bbaa6d8..f6247fff 160000 --- a/client/3rd/qtkeychain +++ b/client/3rd/qtkeychain @@ -1 +1 @@ -Subproject commit 8bbaa6d8302cf0747d9786ace4dd13c7fb746502 +Subproject commit f6247fff2a97b5036f7a7a53eefc314f41916126 From 6dbf4ac62c3cbdea19d75d15e677918c41b122cf Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 15 Jan 2024 09:28:48 +0000 Subject: [PATCH 24/65] Release 4.2.0.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 084ff3d0..c149b3ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.1.0.1 +project(${PROJECT} VERSION 4.2.0.1 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) From 66c5d2f0a8326133d41063d40eef78b9b3e99a00 Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 15 Jan 2024 10:06:20 +0000 Subject: [PATCH 25/65] Updated submodule qtkeychain --- client/3rd/qtkeychain | 2 +- client/cmake/3rdparty.cmake | 2 +- client/translations/amneziavpn_fa_IR.ts | 150 +++++++++-------- client/translations/amneziavpn_ru.ts | 208 ++++++++++++----------- client/translations/amneziavpn_zh_CN.ts | 210 +++++++++++++----------- 5 files changed, 306 insertions(+), 266 deletions(-) diff --git a/client/3rd/qtkeychain b/client/3rd/qtkeychain index f6247fff..74776e2a 160000 --- a/client/3rd/qtkeychain +++ b/client/3rd/qtkeychain @@ -1 +1 @@ -Subproject commit f6247fff2a97b5036f7a7a53eefc314f41916126 +Subproject commit 74776e2a3e2d98d19943e0968901c5b5e04cc1bd diff --git a/client/cmake/3rdparty.cmake b/client/cmake/3rdparty.cmake index ca7d659e..ec544764 100644 --- a/client/cmake/3rdparty.cmake +++ b/client/cmake/3rdparty.cmake @@ -90,7 +90,7 @@ include_directories( ${LIBSSH_ROOT_DIR}/include ${CLIENT_ROOT_DIR}/3rd/libssh/include ${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include - ${CLIENT_ROOT_DIR}/3rd/qtkeychain + ${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include ) diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index bf93a619..e843f807 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -946,6 +946,11 @@ Already installed containers were found on the server. All installed containers Show other methods on Github نمایش متد‎های دیگر در گیت هاب + + + https://github.com/amnezia-vpn/amnezia-client#donate + + Contacts @@ -1854,6 +1859,11 @@ and will not be shared or disclosed to the Amnezia or any third parties I have nothing من هیچی ندارم + + + https://amnezia.org/instructions/0_starter-guide + + PageSetupWizardTextKey @@ -2178,38 +2188,38 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::DeletePasswordJobPrivate - + Password entry not found Password entry not found - + Could not decrypt data Could not decrypt data - - + + Unknown error Unknown error - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not remove private key from keystore Could not remove private key from keystore @@ -2217,12 +2227,12 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::JobPrivate - + Unknown error Unknown error - + Access to keychain denied Access to keychain denied @@ -2230,27 +2240,27 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::PlainTextStore - + Could not store data in settings: access error Could not store data in settings: access error - + Could not store data in settings: format error Could not store data in settings: format error - + Could not delete data from settings: access error Could not delete data from settings: access error - + Could not delete data from settings: format error Could not delete data from settings: format error - + Entry not found Entry not found @@ -2258,80 +2268,80 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::ReadPasswordJobPrivate - + Password entry not found Password entry not found - - + + Could not decrypt data Could not decrypt data - + D-Bus is not running D-Bus is not running - - + + Unknown error Unknown error - + No keychain service available No keychain service available - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Access to keychain denied Access to keychain denied - + Could not determine data type: %1; %2 Could not determine data type: %1; %2 - - + + Entry not found Entry not found - + Unsupported entry type 'Map' Unsupported entry type 'Map' - + Unknown kwallet entry type '%1' Unknown kwallet entry type '%1' - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not retrieve private key from keystore Could not retrieve private key from keystore - + Could not create decryption cipher Could not create decryption cipher @@ -2339,73 +2349,73 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::WritePasswordJobPrivate - + Credential size exceeds maximum size of %1 Credential size exceeds maximum size of %1 - + Credential key exceeds maximum size of %1 Credential key exceeds maximum size of %1 - + Writing credentials failed: Win32 error code %1 Writing credentials failed: Win32 error code %1 - + Encryption failed Encryption failed - + D-Bus is not running D-Bus is not running - - + + Unknown error Unknown error - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not create private key generator Could not create private key generator - + Could not generate new private key Could not generate new private key - + Could not retrieve private key from keystore Could not retrieve private key from keystore - + Could not create encryption cipher Could not create encryption cipher - + Could not encrypt data Could not encrypt data @@ -2855,74 +2865,72 @@ This means that AmneziaWG keeps the fast performance of the original while addin سرویس Sftp - + Entry not found Entry not found - + Access to keychain denied Access to keychain denied - + No keyring daemon No keyring daemon - + Already unlocked Already unlocked - + No such keyring No such keyring - + Bad arguments Bad arguments - + I/O error I/O error - + Cancelled Cancelled - + Keyring already exists Keyring already exists - + No match No match - + Unknown error Unknown error - + error 0x%1: %2 error 0x%1: %2 - WireGuard Configuration Highlighter - هایلایتر پیکربندی WireGuard + هایلایتر پیکربندی WireGuard - &Randomize colors - رنگ‎های تصادفی + رنگ‎های تصادفی @@ -2936,13 +2944,13 @@ This means that AmneziaWG keeps the fast performance of the original while addin Settings - + Server #1 Server #1 - - + + Server Server @@ -2950,22 +2958,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin SettingsController - + Software version نسخه نرم‎افزار - + All settings have been reset to default values تمام تنظیمات به مقادیر پیش فرض ریست شد - + Cached profiles cleared پروفایل ذخیره شده پاک شد - + Backup file is corrupted فایل بک‎آپ خراب شده است diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index 58171e67..ed580ec0 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -15,15 +15,13 @@ AndroidController - AmneziaVPN - AmneziaVPN + AmneziaVPN - VPN Connected Refers to the app - which is currently running the background and waiting - VPN Подключен + VPN Подключен @@ -151,7 +149,7 @@ ImportController - + Scanned %1 of %2. Отсканировано %1 из%2. @@ -946,6 +944,11 @@ Already installed containers were found on the server. All installed containers Show other methods on Github Показать другие способы на Github + + + https://github.com/amnezia-vpn/amnezia-client#donate + + Contacts @@ -1428,22 +1431,22 @@ Already installed containers were found on the server. All installed containers Имя сервера - + Save Сохранить - + Protocols Протоколы - + Services Сервисы - + Data Данные @@ -1854,6 +1857,11 @@ and will not be shared or disclosed to the Amnezia or any third parties I have nothing У меня ничего нет + + + https://amnezia.org/instructions/0_starter-guide + + PageSetupWizardTextKey @@ -1941,8 +1949,8 @@ and will not be shared or disclosed to the Amnezia or any third parties Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. - + Server Сервер @@ -2017,7 +2025,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Users Пользователи @@ -2027,47 +2035,52 @@ and will not be shared or disclosed to the Amnezia or any third parties Имя пользователя - + Search - + + Creation date: + + + + Rename - + Client name - + Save Сохранить - + Revoke - - Revoke the config for a user - + + Revoke the config for a user - %1? - + The user will no longer be able to connect to your server. Пользователь больше не сможет подключаться к вашему серверу - + Continue Продолжить - + Cancel Отменить @@ -2081,20 +2094,20 @@ and will not be shared or disclosed to the Amnezia or any third parties Поделиться доступом к VPN, без возможности управления сервером - + Protocol Протокол - + Connection format Формат подключения - + Share Поделиться @@ -2124,12 +2137,12 @@ and will not be shared or disclosed to the Amnezia or any third parties Сервер - + Accessing Доступ - + File with accessing settings to @@ -2160,38 +2173,38 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::DeletePasswordJobPrivate - + Password entry not found Password entry not found - + Could not decrypt data Could not decrypt data - - + + Unknown error Unknown error - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not remove private key from keystore Could not remove private key from keystore @@ -2199,12 +2212,12 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::JobPrivate - + Unknown error Unknown error - + Access to keychain denied Access to keychain denied @@ -2212,27 +2225,27 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::PlainTextStore - + Could not store data in settings: access error Could not store data in settings: access error - + Could not store data in settings: format error Could not store data in settings: format error - + Could not delete data from settings: access error Could not delete data from settings: access error - + Could not delete data from settings: format error Could not delete data from settings: format error - + Entry not found Entry not found @@ -2240,80 +2253,80 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::ReadPasswordJobPrivate - + Password entry not found Password entry not found - - + + Could not decrypt data Could not decrypt data - + D-Bus is not running D-Bus is not running - - + + Unknown error Unknown error - + No keychain service available No keychain service available - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Access to keychain denied Access to keychain denied - + Could not determine data type: %1; %2 Could not determine data type: %1; %2 - - + + Entry not found Entry not found - + Unsupported entry type 'Map' Unsupported entry type 'Map' - + Unknown kwallet entry type '%1' Unknown kwallet entry type '%1' - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not retrieve private key from keystore Could not retrieve private key from keystore - + Could not create decryption cipher Could not create decryption cipher @@ -2321,73 +2334,73 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::WritePasswordJobPrivate - + Credential size exceeds maximum size of %1 Credential size exceeds maximum size of %1 - + Credential key exceeds maximum size of %1 Credential key exceeds maximum size of %1 - + Writing credentials failed: Win32 error code %1 Writing credentials failed: Win32 error code %1 - + Encryption failed Encryption failed - + D-Bus is not running D-Bus is not running - - + + Unknown error Unknown error - + Could not open wallet: %1; %2 Could not open wallet: %1; %2 - + Password not found Password not found - + Could not open keystore Could not open keystore - + Could not create private key generator Could not create private key generator - + Could not generate new private key Could not generate new private key - + Could not retrieve private key from keystore Could not retrieve private key from keystore - + Could not create encryption cipher Could not create encryption cipher - + Could not encrypt data Could not encrypt data @@ -2594,7 +2607,12 @@ and will not be shared or disclosed to the Amnezia or any third parties VPN pool error: no available addresses - + + VPN connection error + + + + Internal error Internal error @@ -2822,62 +2840,62 @@ This means that AmneziaWG keeps the fast performance of the original while addin Сервис SFTP - + Entry not found Entry not found - + Access to keychain denied Access to keychain denied - + No keyring daemon No keyring daemon - + Already unlocked Already unlocked - + No such keyring No such keyring - + Bad arguments Bad arguments - + I/O error I/O error - + Cancelled Cancelled - + Keyring already exists Keyring already exists - + No match No match - + Unknown error Unknown error - + error 0x%1: %2 error 0x%1: %2 @@ -2893,13 +2911,13 @@ This means that AmneziaWG keeps the fast performance of the original while addin Settings - + Server #1 Server #1 - - + + Server Server @@ -2907,22 +2925,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin SettingsController - + Software version Версия ПО - + All settings have been reset to default values Все настройки были сброшены к значению "По умолчанию" - + Cached profiles cleared Кэш профиля очищен - + Backup file is corrupted Backup файл поврежден @@ -3054,7 +3072,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index e63344d8..7143c76b 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -11,15 +11,9 @@ AndroidController - - AmneziaVPN - - - - VPN Connected Refers to the app - which is currently running the background and waiting - VPN已连接 + VPN已连接 @@ -158,7 +152,7 @@ ImportController - + Scanned %1 of %2. 扫描 %1 of %2. @@ -997,6 +991,11 @@ And if you don't like the app, all the more support it - the donation will Show other methods on Github 其他捐款途径 + + + https://github.com/amnezia-vpn/amnezia-client#donate + + Contacts @@ -1511,22 +1510,22 @@ And if you don't like the app, all the more support it - the donation will 服务器名 - + Save 保存 - + Protocols 协议 - + Services 服务 - + Data 数据 @@ -1957,6 +1956,11 @@ and will not be shared or disclosed to the Amnezia or any third parties I have nothing 我没有 + + + https://amnezia.org/instructions/0_starter-guide + + PageSetupWizardTextKey @@ -2078,7 +2082,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Users @@ -2088,47 +2092,52 @@ and will not be shared or disclosed to the Amnezia or any third parties 共享 VPN 访问,无需管理服务器 - + Search - + + Creation date: + + + + Rename - + Client name - + Save 保存 - + Revoke - - Revoke the config for a user - + + Revoke the config for a user - %1? - + The user will no longer be able to connect to your server. - + Continue 继续 - + Cancel 取消 @@ -2170,8 +2179,8 @@ and will not be shared or disclosed to the Amnezia or any third parties 服务器 - + Server 服务器 @@ -2193,8 +2202,8 @@ and will not be shared or disclosed to the Amnezia or any third parties 协议 - + Protocol 协议 @@ -2214,14 +2223,14 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Connection format 连接格式 - + Share 共享 @@ -2251,12 +2260,12 @@ and will not be shared or disclosed to the Amnezia or any third parties 服务器 - + Accessing 访问 - + File with accessing settings to 访问配置文件的内容为: @@ -2287,38 +2296,38 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::DeletePasswordJobPrivate - + Password entry not found 未发现秘密 - + Could not decrypt data 数据无法加密 - - + + Unknown error 未知错误 - + Could not open wallet: %1; %2 无法打开钱包: %1; %2 - + Password not found 未发现密码 - + Could not open keystore 无法打开密钥库 - + Could not remove private key from keystore 无法从密钥库中删除私钥 @@ -2326,12 +2335,12 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::JobPrivate - + Unknown error 未知错误 - + Access to keychain denied 访问钥匙串被拒绝 @@ -2339,27 +2348,27 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::PlainTextStore - + Could not store data in settings: access error 无法在配置中存储数据:访问错误 - + Could not store data in settings: format error 无法在陪置中存储数据:格式错误 - + Could not delete data from settings: access error 无法在配置中删除数据:访问错误 - + Could not delete data from settings: format error 无法在配置中删除数据:格式错误 - + Entry not found 未找到条目 @@ -2367,80 +2376,80 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::ReadPasswordJobPrivate - + Password entry not found 未发现密码 - - + + Could not decrypt data 数据无法加密 - + D-Bus is not running D-Bus未运行 - - + + Unknown error 未知错误 - + No keychain service available 没有有效的钥匙串服务 - + Could not open wallet: %1; %2 无法打开钱包: %1; %2 - + Access to keychain denied 访问钥匙串被拒绝 - + Could not determine data type: %1; %2 无法确定数据类型: %1; %2 - - + + Entry not found 未找到记录 - + Unsupported entry type 'Map' 不支持的记录类型 'Map' - + Unknown kwallet entry type '%1' 未知钱包类型 '%1' - + Password not found 未发现密码 - + Could not open keystore 无法打开密钥库 - + Could not retrieve private key from keystore 无法从密钥存储库中检索私钥 - + Could not create decryption cipher 无法创建解密算法 @@ -2448,73 +2457,73 @@ and will not be shared or disclosed to the Amnezia or any third parties QKeychain::WritePasswordJobPrivate - + Credential size exceeds maximum size of %1 证书大小超过上限,最大为: %1 - + Credential key exceeds maximum size of %1 凭证密钥大小超过上限,最大为: %1 - + Writing credentials failed: Win32 error code %1 写入凭证失败,Win32错误码: %1 - + Encryption failed 加密失败 - + D-Bus is not running D-Bus未运行 - - + + Unknown error 未知错误 - + Could not open wallet: %1; %2 无法打开钱包: %1; %2 - + Password not found 未发现密码 - + Could not open keystore 无法打开密钥库 - + Could not create private key generator 无法创建私钥生成器 - + Could not generate new private key 无法生成新的私钥 - + Could not retrieve private key from keystore 无法从密钥库检索私钥 - + Could not create encryption cipher 无法创建加密密码 - + Could not encrypt data 无法加密数据 @@ -2666,6 +2675,11 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp error: No media was in remote drive Sftp 错误: 远程驱动器中没有媒介 + + + VPN connection error + + Failed to save config to disk 配置保存到磁盘失败 @@ -2730,7 +2744,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 该配置不包含任何用于连接到服务器的容器和凭据。 - + Internal error 内部错误 @@ -2970,62 +2984,62 @@ While it offers a blend of security, stability, and speed, it's essential t Sftp 文件共享服务 - 安全的 FTP 服务 - + Entry not found 未找到记录 - + Access to keychain denied 访问钥匙串被拒绝 - + No keyring daemon 没有密钥环守护进程 - + Already unlocked 已经解锁 - + No such keyring 没有这样的密钥环 - + Bad arguments 错误参数 - + I/O error I/O错误 - + Cancelled 已取消 - + Keyring already exists 密匙环已经存在 - + No match 不匹配 - + Unknown error 未知错误 - + error 0x%1: %2 错误 0x%1: %2 @@ -3041,13 +3055,13 @@ While it offers a blend of security, stability, and speed, it's essential t Settings - + Server #1 - - + + Server 服务器 @@ -3055,22 +3069,22 @@ While it offers a blend of security, stability, and speed, it's essential t SettingsController - + Software version 软件版本 - + Backup file is corrupted 备份文件已损坏 - + All settings have been reset to default values 所配置恢复为默认值 - + Cached profiles cleared 缓存的配置文件已清除 @@ -3206,7 +3220,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps From d527f6a5ce245b2c7310cedb5f73754ba43528c5 Mon Sep 17 00:00:00 2001 From: Morteza Sherafati Date: Mon, 15 Jan 2024 20:18:10 +0000 Subject: [PATCH 26/65] show installed protocols first in protocols tab --- client/ui/qml/Pages2/PageSettingsServerProtocols.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml index 21401bf5..9fc2abde 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml @@ -55,6 +55,9 @@ PageType { model: SortFilterProxyModel { id: proxyContainersModel sourceModel: ContainersModel + sorters: [ + RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder } + ] } Component.onCompleted: updateContainersModelFilters() From 60aeefe1c209624b339069a29ac192dae86b68ca Mon Sep 17 00:00:00 2001 From: albexk Date: Tue, 16 Jan 2024 15:06:34 +0300 Subject: [PATCH 27/65] Restore previous Android keyboard behaviour There is a Qt bug related to this behaviour: https://bugreports.qt.io/browse/QTBUG-41170 --- client/android/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index cf237d07..960dc87d 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -38,6 +38,7 @@ android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density |fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc" android:launchMode="singleInstance" + android:windowSoftInputMode="adjustResize" android:exported="true"> From fac57ac89a0cc1efa81f6a3d77ca92e54b9141e5 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 17 Jan 2024 00:34:23 +0700 Subject: [PATCH 28/65] for servers received via api, ignore the split tunneling settings --- client/configurators/openvpn_configurator.cpp | 40 ++++++++++--------- client/configurators/openvpn_configurator.h | 2 +- client/configurators/vpn_configurator.cpp | 2 +- client/ui/models/servers_model.cpp | 5 +++ client/ui/models/servers_model.h | 2 + .../qml/Pages2/PageSettingsSplitTunneling.qml | 8 +++- client/vpnconnection.cpp | 33 ++++++++------- 7 files changed, 55 insertions(+), 37 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index e3362236..8b201fbf 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -118,31 +118,33 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia return QJsonDocument(jConfig).toJson(); } -QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig) +QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig, const int serverIndex) { QJsonObject json = QJsonDocument::fromJson(jsonConfig.toUtf8()).object(); QString config = json[config_key::config].toString(); - QRegularExpression regex("redirect-gateway.*"); - config.replace(regex, ""); + if (!m_settings->server(serverIndex).value(config_key::configVersion).toInt()) { + QRegularExpression regex("redirect-gateway.*"); + config.replace(regex, ""); - if (m_settings->routeMode() == Settings::VpnAllSites) { - config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); - // Prevent ipv6 leak - config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); - config.append("block-ipv6\n"); - } - if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings->routeMode() == Settings::VpnAllSites) { + config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); + // Prevent ipv6 leak + config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); + config.append("block-ipv6\n"); + } + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { - // no redirect-gateway - } - if (m_settings->routeMode() == Settings::VpnAllExceptSites) { -#ifndef Q_OS_ANDROID - config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); -#endif - // Prevent ipv6 leak - config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); - config.append("block-ipv6\n"); + // no redirect-gateway + } + if (m_settings->routeMode() == Settings::VpnAllExceptSites) { + #ifndef Q_OS_ANDROID + config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); + #endif + // Prevent ipv6 leak + config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); + config.append("block-ipv6\n"); + } } #ifndef MZ_WINDOWS diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index cc66d13f..424a20e1 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -26,7 +26,7 @@ public: QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr); - QString processConfigWithLocalSettings(QString jsonConfig); + QString processConfigWithLocalSettings(QString jsonConfig, const int serverIndex); QString processConfigWithExportSettings(QString jsonConfig); ErrorCode signCert(DockerContainer container, diff --git a/client/configurators/vpn_configurator.cpp b/client/configurators/vpn_configurator.cpp index 3018b52f..c74a3d4f 100644 --- a/client/configurators/vpn_configurator.cpp +++ b/client/configurators/vpn_configurator.cpp @@ -92,7 +92,7 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker processConfigWithDnsSettings(serverIndex, container, proto, config); if (proto == Proto::OpenVpn) { - config = openVpnConfigurator->processConfigWithLocalSettings(config); + config = openVpnConfigurator->processConfigWithLocalSettings(config, serverIndex); } return config; } diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index feaf1e8d..1922e188 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -531,3 +531,8 @@ void ServersModel::toggleAmneziaDns(bool enabled) emit defaultServerDescriptionChanged(); } +bool ServersModel::isDefaultServerFromApi() +{ + return m_settings->server(m_defaultServerIndex).value(config_key::configVersion).toInt(); +} + diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index af88febb..38f2bdd4 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -97,6 +97,8 @@ public slots: void toggleAmneziaDns(bool enabled); + bool isDefaultServerFromApi(); + protected: QHash roleNames() const override; diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index 873ae997..9ad88524 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -21,7 +21,13 @@ PageType { id: root property bool pageEnabled: { - return !ConnectionController.isConnected + return !ConnectionController.isConnected && !ServersModel.isDefaultServerFromApi() + } + + Component.onCompleted: { + if (ServersModel.isDefaultServerFromApi()) { + PageController.showNotificationMessage(qsTr("This server does not support split tunneling function")) + } } Connections { diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 5d4d5f99..75483d89 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -64,24 +64,26 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) IpcClient::Interface()->resetIpStack(); IpcClient::Interface()->flushDns(); - if (m_settings->routeMode() != Settings::VpnAllSites) { - IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0"); - // qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); - } - QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); - QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); + if (!m_vpnConfiguration.value(config_key::configVersion).toInt()) { + if (m_settings->routeMode() != Settings::VpnAllSites) { + IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0"); + // qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); + } + QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString(); + QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString(); - IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2); + IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2); - if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { - QTimer::singleShot(1000, m_vpnProtocol.data(), - [this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); }); - } else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { - IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1"); - IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1"); + if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { + QTimer::singleShot(1000, m_vpnProtocol.data(), + [this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); }); + } else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { + IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1"); + IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1"); - IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress()); - addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode()); + IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress()); + addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode()); + } } } else if (state == Vpn::ConnectionState::Error) { @@ -296,6 +298,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, const ServerC vpnConfiguration[config_key::hostName] = server.value(config_key::hostName).toString(); vpnConfiguration[config_key::description] = server.value(config_key::description).toString(); + vpnConfiguration[config_key::configVersion] = server.value(config_key::configVersion).toInt(); // TODO: try to get hostName, port, description for 3rd party configs // vpnConfiguration[config_key::port] = ...; From 301141c7557a85f6faddec2ba73a94934de3879c Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 17 Jan 2024 17:43:54 +0300 Subject: [PATCH 29/65] Remove Android TextField workaround It has been fixed in Qt 6.5.3, 6.6.0 --- client/amnezia_application.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index fe360818..72630f06 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -167,20 +167,6 @@ void AmneziaApplication::init() }); } #endif - -// Android TextField clipboard workaround -// https://bugreports.qt.io/browse/QTBUG-113461 -#ifdef Q_OS_ANDROID - QObject::connect(qApp, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) { - if (state == Qt::ApplicationActive) { - if (qApp->clipboard()->mimeData()->formats().contains("text/html")) { - QTextDocument doc; - doc.setHtml(qApp->clipboard()->mimeData()->html()); - qApp->clipboard()->setText(doc.toPlainText()); - } - } - }); -#endif } void AmneziaApplication::registerTypes() From 2552e33d6470d9365f34b92db3c07b0bf10a8ddd Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 17 Jan 2024 17:46:54 +0300 Subject: [PATCH 30/65] Add Android TextArea clipboard workaround --- client/amnezia_application.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 72630f06..0590b6d1 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -167,6 +167,23 @@ void AmneziaApplication::init() }); } #endif + +// Android TextArea clipboard workaround +// Text from TextArea always has "text/html" mime-type: +// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865 +// Next, html is created for this mime-type: +// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885 +// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText: +// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46 +// So we catch all the copies to the clipboard and clear them from "text/html" +#ifdef Q_OS_ANDROID + connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() { + auto clipboard = QGuiApplication::clipboard(); + if (clipboard->mimeData()->hasHtml()) { + clipboard->setText(clipboard->text()); + } + }); +#endif } void AmneziaApplication::registerTypes() From 33e229d0b2fdc2f027e2062a409e99d853e49750 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 17 Jan 2024 23:38:20 +0700 Subject: [PATCH 31/65] added blocking of the page with dns settings for servers received by API --- client/ui/qml/Pages2/PageSettingsDns.qml | 8 ++++++++ client/ui/qml/Pages2/PageSettingsSplitTunneling.qml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index 5670464f..1970da52 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -28,6 +28,14 @@ PageType { anchors.bottom: parent.bottom contentHeight: content.height + enabled: !ServersModel.isDefaultServerFromApi() + + Component.onCompleted: { + if (ServersModel.isDefaultServerFromApi()) { + PageController.showNotificationMessage(qsTr("Default server does not support custom dns")) + } + } + ColumnLayout { id: content diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index 9ad88524..4300d591 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -26,7 +26,7 @@ PageType { Component.onCompleted: { if (ServersModel.isDefaultServerFromApi()) { - PageController.showNotificationMessage(qsTr("This server does not support split tunneling function")) + PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function")) } } From f576eb509dc16ece9af8836187397e2bc3a08d7a Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Thu, 18 Jan 2024 15:25:36 +0300 Subject: [PATCH 32/65] Fix IPv6 problem --- client/3rd/awg-apple | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/3rd/awg-apple b/client/3rd/awg-apple index 233eda67..09c0fc7f 160000 --- a/client/3rd/awg-apple +++ b/client/3rd/awg-apple @@ -1 +1 @@ -Subproject commit 233eda6760962efddc860f177a0ce2bcdf533d85 +Subproject commit 09c0fc7f70bdf68f6953ea959d8a18952603fe94 From ad21d7ab649bde4b40fb45bc407ea3348f41204e Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 17 Jan 2024 21:24:42 +0300 Subject: [PATCH 33/65] Add Android App Bundle build and upload Up upload-artifact action to version 4 --- .github/workflows/deploy.yml | 38 ++++++++++++++++++++++++------------ deploy/build_android.sh | 29 ++++++++++++++++++--------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a218a1ad..99db8d8f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,13 +49,13 @@ jobs: bash deploy/build_linux.sh - name: 'Upload installer artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_Linux_installer path: deploy/AmneziaVPN_Linux_Installer retention-days: 7 - name: 'Upload unpacked artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_Linux_unpacked path: deploy/AppDir @@ -110,13 +110,13 @@ jobs: call deploy\\build_windows.bat - name: 'Upload installer artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_Windows_installer path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe retention-days: 7 - name: 'Upload unpacked artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_Windows_unpacked path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release @@ -200,7 +200,7 @@ jobs: IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }} # - name: 'Upload appstore .ipa and dSYMs to artifacts' -# uses: actions/upload-artifact@v3 +# uses: actions/upload-artifact@v4 # with: # name: app-store ipa & dsyms # path: | @@ -255,13 +255,13 @@ jobs: bash deploy/build_macos.sh - name: 'Upload installer artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_MacOS_installer path: AmneziaVPN.dmg retention-days: 7 - name: 'Upload unpacked artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN_MacOS_unpacked path: deploy/build/client/AmneziaVPN.app @@ -277,6 +277,7 @@ jobs: ANDROID_BUILD_PLATFORM: android-34 QT_VERSION: 6.6.1 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' + BUILD_AAB: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }} steps: - name: 'Install desktop Qt' @@ -375,32 +376,45 @@ jobs: ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} shell: bash - run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} + run: ./deploy/build_android.sh ${{ env.BUILD_AAB == 'true' && '--aab' || '' }} --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} - name: 'Upload x86_64 apk' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android-x86_64 path: deploy/build/AmneziaVPN-x86_64-release.apk + compression-level: 0 retention-days: 7 - name: 'Upload x86 apk' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android-x86 path: deploy/build/AmneziaVPN-x86-release.apk + compression-level: 0 retention-days: 7 - name: 'Upload arm64-v8a apk' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android-arm64-v8a path: deploy/build/AmneziaVPN-arm64-v8a-release.apk + compression-level: 0 retention-days: 7 - name: 'Upload armeabi-v7a apk' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android-armeabi-v7a path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk + compression-level: 0 + retention-days: 7 + + - name: 'Upload aab' + if: ${{ env.BUILD_AAB == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: AmneziaVPN-android + path: deploy/build/AmneziaVPN-release.aab + compression-level: 0 retention-days: 7 diff --git a/deploy/build_android.sh b/deploy/build_android.sh index 0c5a80c8..151cabc2 100755 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -7,15 +7,18 @@ usage() { cat < -Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) is built. +Build AmneziaVPN android client. -Options: - -d, --debug Build debug version +Artifact types: + -u, --aab Build Android App Bundle (AAB) -a, --apk ( | all) Build APKs for the specified ABIs or for all available ABIs Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' - list of ABIs delimited by ';' + +Options: + -d, --debug Build debug version -b, --build-platform The SDK platform used for building the Java code of the application By default, the latest available platform is used -m, --move Move the build result to the root of the build directory @@ -25,14 +28,14 @@ EOT } BUILD_TYPE="release" -AAB=1 -opts=$(getopt -l debug,apk:,build-platform:,move,help -o "da:b:mh" -- "$@") +opts=$(getopt -l debug,aab,apk:,build-platform:,move,help -o "dua:b:mh" -- "$@") eval set -- "$opts" while true; do case "$1" in -d | --debug) BUILD_TYPE="debug"; shift;; - -a | --apk) ABIS=$2; unset AAB; shift 2;; + -u | --aab) AAB=1; shift;; + -a | --apk) ABIS=$2; shift 2;; -b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;; -m | --move) MOVE_RESULT=1; shift;; -h | --help) usage; exit 0;; @@ -49,6 +52,11 @@ if [[ -v ABIS && \ exit 1 fi +# At least one artifact type must be specified +if [[ ! (-v AAB || -v ABIS) ]]; then + usage; exit 0 +fi + echo "Build script started..." PROJECT_DIR=$(pwd) @@ -137,7 +145,8 @@ gradle_opts=() if [ -v AAB ]; then gradle_opts+=(bundle"${BUILD_TYPE^}") -else +fi +if [ -v ABIS ]; then gradle_opts+=(assemble"${BUILD_TYPE^}") fi @@ -151,7 +160,9 @@ if [[ -v CI || -v MOVE_RESULT ]]; then if [ -v AAB ]; then mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/AmneziaVPN-$BUILD_TYPE.aab \ $PROJECT_DIR/deploy/build/ - else + fi + + if [ -v ABIS ]; then if [ "$ABIS" = "all" ]; then ABIS="x86;x86_64;armeabi-v7a;arm64-v8a" fi From d7fbddb97f7c52ab8f0baa626d7ca5f9e839a67f Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Thu, 18 Jan 2024 17:13:04 +0300 Subject: [PATCH 34/65] Update README.md --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 94dffe09..21b01865 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ AmneziaVPN uses a number of open source projects to work: Make sure to pull all submodules after checking out the repo. ```bash -git submodule update --init +git submodule update --init --recursive ``` ## Development @@ -50,7 +50,15 @@ Look deploy folder for build scripts. 1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher. -2. We use QT to generate the XCode project. we need QT version 6.4. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html) +2. We use QT to generate the XCode project. we need QT version 6.6.1. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules: + - macOS + - iOS + - Qt 5 Compatibility Module + - Qt Shader Tools + - Additional Libraries: + - Qt Image Formats + - Qt Multimedia + - Qt Remote Objects 3. Install cmake is require. We recommend cmake version 3.25. You can install cmake in [here](https://cmake.org/download/) @@ -66,10 +74,11 @@ gomobile init 5. Build project ```bash export QT_BIN_DIR="/Qt//ios/bin" +export QT_MACOS_ROOT_DIR="/Qt//macos" export QT_IOS_BIN=$QT_BIN_DIR export PATH=$PATH:~/go/bin mkdir build-ios -$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_BIN_DIR +$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR ``` Replace PATH-TO-QT-FOLDER and QT-VERSION to your environment From eec81f8124ee8d655115cc7d43bc1d31bab9b078 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 18 Jan 2024 21:29:56 +0700 Subject: [PATCH 35/65] fixed append/rename/revoke client for cloak and ss on client management panel --- client/ui/models/clientManagementModel.cpp | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 5c89382b..0b1be2cc 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -245,8 +245,13 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); ServerController serverController(m_settings); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)); + } else { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container)); + } error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { @@ -273,8 +278,13 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); ServerController serverController(m_settings); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)); + } else { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container)); + } ErrorCode error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); @@ -345,8 +355,13 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)); + } else { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container)); + } error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { logger.error() << "Failed to upload the clientsTable file to the server"; @@ -395,8 +410,13 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson(); - const QString clientsTableFile = - QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container)); + QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable"); + if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks + || container == DockerContainer::Cloak) { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)); + } else { + clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container)); + } error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile); if (error != ErrorCode::NoError) { logger.error() << "Failed to upload the clientsTable file to the server"; From 9371dd405e9fc78f634c5e8210bcf885293d9760 Mon Sep 17 00:00:00 2001 From: tiaga Date: Fri, 19 Jan 2024 00:16:19 +0700 Subject: [PATCH 36/65] Improve Linux installer Pack installer to a .tar archive in order to save executable bit for `AmneziaVPN_Linux_Installer.bin`. --- .github/workflows/deploy.yml | 10 ++++++++-- deploy/build_linux.sh | 4 +--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 99db8d8f..cd52e69d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -48,12 +48,16 @@ jobs: export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin bash deploy/build_linux.sh + - name: 'Pack installer' + run: cd deploy && tar -cf AmneziaVPN_Linux_Installer.tar AmneziaVPN_Linux_Installer.bin + - name: 'Upload installer artifact' uses: actions/upload-artifact@v4 with: - name: AmneziaVPN_Linux_installer - path: deploy/AmneziaVPN_Linux_Installer + name: AmneziaVPN_Linux_installer.tar + path: deploy/AmneziaVPN_Linux_Installer.tar retention-days: 7 + - name: 'Upload unpacked artifact' uses: actions/upload-artifact@v4 with: @@ -115,6 +119,7 @@ jobs: name: AmneziaVPN_Windows_installer path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe retention-days: 7 + - name: 'Upload unpacked artifact' uses: actions/upload-artifact@v4 with: @@ -260,6 +265,7 @@ jobs: name: AmneziaVPN_MacOS_installer path: AmneziaVPN.dmg retention-days: 7 + - name: 'Upload unpacked artifact' uses: actions/upload-artifact@v4 with: diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh index 1b9c698a..c90e781a 100755 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -83,6 +83,4 @@ ldd $CQTDEPLOYER_DIR/bin/binarycreator cp -r $PROJECT_DIR/deploy/installer $BUILD_DIR -$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer - - +$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer.bin From 51070635a5b1c91f4cc8a368af58f0c0396fd1ab Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Fri, 19 Jan 2024 03:15:27 +0300 Subject: [PATCH 37/65] Change to amneziawg-apple --- .gitmodules | 6 +++--- client/3rd/amneziawg-apple | 1 + client/3rd/awg-apple | 1 - client/cmake/ios.cmake | 2 +- client/ios/networkextension/CMakeLists.txt | 2 +- .../WireGuardNetworkExtension-Bridging-Header.h | 4 ++-- client/platforms/ios/WireGuard-Bridging-Header.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) create mode 160000 client/3rd/amneziawg-apple delete mode 160000 client/3rd/awg-apple diff --git a/.gitmodules b/.gitmodules index c96dd6bc..ff50f897 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,6 +22,6 @@ [submodule "client/3rd-prebuilt"] path = client/3rd-prebuilt url = https://github.com/amnezia-vpn/3rd-prebuilt -[submodule "client/3rd/awg-apple"] - path = client/3rd/awg-apple - url = https://github.com/amnezia-vpn/awg-apple +[submodule "client/3rd/amneziawg-apple"] + path = client/3rd/amneziawg-apple + url = https://github.com/amnezia-vpn/amneziawg-apple diff --git a/client/3rd/amneziawg-apple b/client/3rd/amneziawg-apple new file mode 160000 index 00000000..f23eee47 --- /dev/null +++ b/client/3rd/amneziawg-apple @@ -0,0 +1 @@ +Subproject commit f23eee4700ed4a2ef44a800d2c20466c9ab0222b diff --git a/client/3rd/awg-apple b/client/3rd/awg-apple deleted file mode 160000 index 09c0fc7f..00000000 --- a/client/3rd/awg-apple +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 09c0fc7f70bdf68f6953ea959d8a18952603fe94 diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index 7aa9f1a9..3234578e 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -97,7 +97,7 @@ target_compile_options(${PROJECT} PRIVATE -DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\" ) -set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/awg-apple/Sources) +set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources) target_sources(${PROJECT} PRIVATE # ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index 16769ea3..fb1bd3c1 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -58,7 +58,7 @@ target_link_libraries(networkextension PRIVATE ${FW_UI_KIT}) target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\") target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1) -set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/awg-apple/Sources) +set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources) target_sources(networkextension PRIVATE ${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift diff --git a/client/ios/networkextension/WireGuardNetworkExtension-Bridging-Header.h b/client/ios/networkextension/WireGuardNetworkExtension-Bridging-Header.h index 44d0b6b0..2cca0fc8 100644 --- a/client/ios/networkextension/WireGuardNetworkExtension-Bridging-Header.h +++ b/client/ios/networkextension/WireGuardNetworkExtension-Bridging-Header.h @@ -1,6 +1,6 @@ #include "wireguard-go-version.h" -#include "3rd/awg-apple/Sources/WireGuardKitGo/wireguard.h" -#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h" +#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h" +#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h" #include #include diff --git a/client/platforms/ios/WireGuard-Bridging-Header.h b/client/platforms/ios/WireGuard-Bridging-Header.h index fbccb2d4..0183367b 100644 --- a/client/platforms/ios/WireGuard-Bridging-Header.h +++ b/client/platforms/ios/WireGuard-Bridging-Header.h @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h" +#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h" #include #include From b8b423ca19da4c107487f57a5712dedd920c9057 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 16:02:35 +0300 Subject: [PATCH 38/65] Fix amnezia_application line-ending --- client/amnezia_application.cpp | 766 ++++++++++++++++----------------- client/amnezia_application.h | 254 +++++------ 2 files changed, 510 insertions(+), 510 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 0590b6d1..28b028ff 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -1,383 +1,383 @@ -#include "amnezia_application.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "logger.h" -#include "version.h" - -#include "platforms/ios/QRCodeReaderBase.h" -#if defined(Q_OS_ANDROID) - #include "platforms/android/android_controller.h" -#endif - -#include "protocols/qml_register_protocols.h" - -#if defined(Q_OS_IOS) - #include "platforms/ios/ios_controller.h" -#endif - -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) -AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv) -#else -AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, - int timeout, const QString &userData) - : SingleApplication(argc, argv, allowSecondary, options, timeout, userData) -#endif -{ - setQuitOnLastWindowClosed(false); - - // Fix config file permissions -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - { - QSettings s(ORGANIZATION_NAME, APPLICATION_NAME); - s.setValue("permFixed", true); - } - - QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" - + ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf"; - QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner); - - QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" - + ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf"; - QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner); -#endif - - m_settings = std::shared_ptr(new Settings); -} - -AmneziaApplication::~AmneziaApplication() -{ - m_vpnConnectionThread.quit(); - m_vpnConnectionThread.wait(3000); - - if (m_engine) { - QObject::disconnect(m_engine, 0, 0, 0); - delete m_engine; - } -} - -void AmneziaApplication::init() -{ - m_engine = new QQmlApplicationEngine; - - const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml")); - QObject::connect( - m_engine, &QQmlApplicationEngine::objectCreated, this, - [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, - Qt::QueuedConnection); - - m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance()); - - m_configurator = std::shared_ptr(new VpnConfigurator(m_settings, this)); - m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator)); - m_vpnConnection->moveToThread(&m_vpnConnectionThread); - m_vpnConnectionThread.start(); - - initModels(); - loadTranslator(); - initControllers(); - -#ifdef Q_OS_ANDROID - connect(AndroidController::instance(), &AndroidController::initConnectionState, this, - [this](Vpn::ConnectionState state) { - m_connectionController->onConnectionStateChanged(state); - if (m_vpnConnection) - m_vpnConnection->restoreConnection(); - }); - if (!AndroidController::instance()->initialize()) { - qCritical() << QString("Init failed"); - if (m_vpnConnection) - emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error); - return; - } - - connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { - m_pageController->replaceStartPage(); - m_importController->extractConfigFromData(data); - m_pageController->goToPageViewConfig(); - }); -#endif - -#ifdef Q_OS_IOS - IosController::Instance()->initialize(); - connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { - m_pageController->replaceStartPage(); - m_importController->extractConfigFromData(data); - m_pageController->goToPageViewConfig(); - }); - - connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) { - m_pageController->replaceStartPage(); - m_pageController->goToPageSettingsBackup(); - m_settingsController->importBackupFromOutside(filePath); - }); -#endif - - m_notificationHandler.reset(NotificationHandler::create(nullptr)); - - connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(), - &NotificationHandler::setConnectionState); - - connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), - &PageController::raiseMainWindow); - connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(), - &ConnectionController::openConnection); - connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), - &ConnectionController::closeConnection); - connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), - &NotificationHandler::onTranslationsUpdated); - - m_engine->load(url); - m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); - - if (m_settings->isSaveLogs()) { - if (!Logger::init()) { - qWarning() << "Initialization of debug subsystem failed"; - } - } - -#ifdef Q_OS_WIN - if (m_parser.isSet("a")) - m_pageController->showOnStartup(); - else - emit m_pageController->raiseMainWindow(); -#else - m_pageController->showOnStartup(); -#endif - - // TODO - fix -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - if (isPrimary()) { - QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() { - qDebug() << "Secondary instance started, showing this window instead"; - emit m_pageController->raiseMainWindow(); - }); - } -#endif - -// Android TextArea clipboard workaround -// Text from TextArea always has "text/html" mime-type: -// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865 -// Next, html is created for this mime-type: -// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885 -// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText: -// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46 -// So we catch all the copies to the clipboard and clear them from "text/html" -#ifdef Q_OS_ANDROID - connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() { - auto clipboard = QGuiApplication::clipboard(); - if (clipboard->mimeData()->hasHtml()) { - clipboard->setText(clipboard->text()); - } - }); -#endif -} - -void AmneziaApplication::registerTypes() -{ - qRegisterMetaType("ServerCredentials"); - - qRegisterMetaType("DockerContainer"); - qRegisterMetaType("TransportProto"); - qRegisterMetaType("Proto"); - qRegisterMetaType("ServiceType"); - - declareQmlProtocolEnum(); - declareQmlContainerEnum(); - - qmlRegisterType("QRCodeReader", 1, 0, "QRCodeReader"); - - m_containerProps.reset(new ContainerProps()); - qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get()); - - m_protocolProps.reset(new ProtocolProps()); - qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get()); - - qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0, - "ContainersModelFilters"); - - // - Vpn::declareQmlVpnConnectionStateEnum(); - PageLoader::declareQmlPageEnum(); -} - -void AmneziaApplication::loadFonts() -{ - QQuickStyle::setStyle("Basic"); - - QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf"); -} - -void AmneziaApplication::loadTranslator() -{ - auto locale = m_settings->getAppLanguage(); - m_translator.reset(new QTranslator()); - updateTranslator(locale); -} - -void AmneziaApplication::updateTranslator(const QLocale &locale) -{ - if (!m_translator->isEmpty()) { - QCoreApplication::removeTranslator(m_translator.get()); - } - - QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm"; - if (m_translator->load(strFileName)) { - if (QCoreApplication::installTranslator(m_translator.get())) { - m_settings->setAppLanguage(locale); - } - } else { - m_settings->setAppLanguage(QLocale::English); - } - - m_engine->retranslate(); - - emit translationsUpdated(); -} - -bool AmneziaApplication::parseCommands() -{ - m_parser.setApplicationDescription(APPLICATION_NAME); - m_parser.addHelpOption(); - m_parser.addVersionOption(); - - QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" }; - m_parser.addOption(c_autostart); - - QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" }; - m_parser.addOption(c_cleanup); - - m_parser.process(*this); - - if (m_parser.isSet(c_cleanup)) { - Logger::cleanUp(); - QTimer::singleShot(100, this, [this] { quit(); }); - exec(); - return false; - } - return true; -} - -QQmlApplicationEngine *AmneziaApplication::qmlEngine() const -{ - return m_engine; -} - -void AmneziaApplication::initModels() -{ - m_containersModel.reset(new ContainersModel(this)); - m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); - - m_serversModel.reset(new ServersModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); - connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), - &ContainersModel::updateModel); - connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(), - &ContainersModel::setDefaultContainer); - m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better? - - m_languageModel.reset(new LanguageModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); - connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator); - connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated); - - m_sitesModel.reset(new SitesModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); - - m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); - - m_openVpnConfigModel.reset(new OpenVpnConfigModel(this)); - m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get()); - - m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this)); - m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get()); - - m_cloakConfigModel.reset(new CloakConfigModel(this)); - m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get()); - - m_wireGuardConfigModel.reset(new WireGuardConfigModel(this)); - m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get()); - - m_awgConfigModel.reset(new AwgConfigModel(this)); - m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get()); - -#ifdef Q_OS_WINDOWS - m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this)); - m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get()); -#endif - - m_sftpConfigModel.reset(new SftpConfigModel(this)); - m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get()); - - m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); - m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); - connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, - m_serversModel.get(), &ServersModel::clearCachedProfile); - - connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this, - [this](const QString &clientId, const QString &clientName, const DockerContainer container, - ServerCredentials credentials) { - m_serversModel->reloadContainerConfig(); - m_clientManagementModel->appendClient(clientId, clientName, container, credentials); - emit m_configurator->clientModelUpdated(); - }); -} - -void AmneziaApplication::initControllers() -{ - m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); - m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); - - connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), - &ConnectionController::onTranslationsUpdated); - - m_pageController.reset(new PageController(m_serversModel, m_settings)); - m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); - - m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings)); - m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); - connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(), - &PageController::showPassphraseRequestDrawer); - connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(), - &InstallController::setEncryptedPassphrase); - connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(), - &ConnectionController::onCurrentContainerUpdated); - - m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); - m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); - - m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, - m_settings, m_configurator)); - m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); - - m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings)); - m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); - if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { - QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); - } - connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(), - &ServersModel::toggleAmneziaDns); - - m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); - m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); - - m_systemController.reset(new SystemController(m_settings)); - m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); - - m_cloudController.reset(new ApiController(m_serversModel, m_containersModel)); - m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get()); -} +#include "amnezia_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "logger.h" +#include "version.h" + +#include "platforms/ios/QRCodeReaderBase.h" +#if defined(Q_OS_ANDROID) + #include "platforms/android/android_controller.h" +#endif + +#include "protocols/qml_register_protocols.h" + +#if defined(Q_OS_IOS) + #include "platforms/ios/ios_controller.h" +#endif + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv) +#else +AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, + int timeout, const QString &userData) + : SingleApplication(argc, argv, allowSecondary, options, timeout, userData) +#endif +{ + setQuitOnLastWindowClosed(false); + + // Fix config file permissions +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) + { + QSettings s(ORGANIZATION_NAME, APPLICATION_NAME); + s.setValue("permFixed", true); + } + + QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + + QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner); +#endif + + m_settings = std::shared_ptr(new Settings); +} + +AmneziaApplication::~AmneziaApplication() +{ + m_vpnConnectionThread.quit(); + m_vpnConnectionThread.wait(3000); + + if (m_engine) { + QObject::disconnect(m_engine, 0, 0, 0); + delete m_engine; + } +} + +void AmneziaApplication::init() +{ + m_engine = new QQmlApplicationEngine; + + const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml")); + QObject::connect( + m_engine, &QQmlApplicationEngine::objectCreated, this, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + + m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance()); + + m_configurator = std::shared_ptr(new VpnConfigurator(m_settings, this)); + m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator)); + m_vpnConnection->moveToThread(&m_vpnConnectionThread); + m_vpnConnectionThread.start(); + + initModels(); + loadTranslator(); + initControllers(); + +#ifdef Q_OS_ANDROID + connect(AndroidController::instance(), &AndroidController::initConnectionState, this, + [this](Vpn::ConnectionState state) { + m_connectionController->onConnectionStateChanged(state); + if (m_vpnConnection) + m_vpnConnection->restoreConnection(); + }); + if (!AndroidController::instance()->initialize()) { + qCritical() << QString("Init failed"); + if (m_vpnConnection) + emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error); + return; + } + + connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { + m_pageController->replaceStartPage(); + m_importController->extractConfigFromData(data); + m_pageController->goToPageViewConfig(); + }); +#endif + +#ifdef Q_OS_IOS + IosController::Instance()->initialize(); + connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) { + m_pageController->replaceStartPage(); + m_importController->extractConfigFromData(data); + m_pageController->goToPageViewConfig(); + }); + + connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) { + m_pageController->replaceStartPage(); + m_pageController->goToPageSettingsBackup(); + m_settingsController->importBackupFromOutside(filePath); + }); +#endif + + m_notificationHandler.reset(NotificationHandler::create(nullptr)); + + connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(), + &NotificationHandler::setConnectionState); + + connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(), + &PageController::raiseMainWindow); + connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(), + &ConnectionController::openConnection); + connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), + &ConnectionController::closeConnection); + connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(), + &NotificationHandler::onTranslationsUpdated); + + m_engine->load(url); + m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); + + if (m_settings->isSaveLogs()) { + if (!Logger::init()) { + qWarning() << "Initialization of debug subsystem failed"; + } + } + +#ifdef Q_OS_WIN + if (m_parser.isSet("a")) + m_pageController->showOnStartup(); + else + emit m_pageController->raiseMainWindow(); +#else + m_pageController->showOnStartup(); +#endif + + // TODO - fix +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (isPrimary()) { + QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() { + qDebug() << "Secondary instance started, showing this window instead"; + emit m_pageController->raiseMainWindow(); + }); + } +#endif + +// Android TextArea clipboard workaround +// Text from TextArea always has "text/html" mime-type: +// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865 +// Next, html is created for this mime-type: +// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885 +// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText: +// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46 +// So we catch all the copies to the clipboard and clear them from "text/html" +#ifdef Q_OS_ANDROID + connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() { + auto clipboard = QGuiApplication::clipboard(); + if (clipboard->mimeData()->hasHtml()) { + clipboard->setText(clipboard->text()); + } + }); +#endif +} + +void AmneziaApplication::registerTypes() +{ + qRegisterMetaType("ServerCredentials"); + + qRegisterMetaType("DockerContainer"); + qRegisterMetaType("TransportProto"); + qRegisterMetaType("Proto"); + qRegisterMetaType("ServiceType"); + + declareQmlProtocolEnum(); + declareQmlContainerEnum(); + + qmlRegisterType("QRCodeReader", 1, 0, "QRCodeReader"); + + m_containerProps.reset(new ContainerProps()); + qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get()); + + m_protocolProps.reset(new ProtocolProps()); + qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get()); + + qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0, + "ContainersModelFilters"); + + // + Vpn::declareQmlVpnConnectionStateEnum(); + PageLoader::declareQmlPageEnum(); +} + +void AmneziaApplication::loadFonts() +{ + QQuickStyle::setStyle("Basic"); + + QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf"); +} + +void AmneziaApplication::loadTranslator() +{ + auto locale = m_settings->getAppLanguage(); + m_translator.reset(new QTranslator()); + updateTranslator(locale); +} + +void AmneziaApplication::updateTranslator(const QLocale &locale) +{ + if (!m_translator->isEmpty()) { + QCoreApplication::removeTranslator(m_translator.get()); + } + + QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm"; + if (m_translator->load(strFileName)) { + if (QCoreApplication::installTranslator(m_translator.get())) { + m_settings->setAppLanguage(locale); + } + } else { + m_settings->setAppLanguage(QLocale::English); + } + + m_engine->retranslate(); + + emit translationsUpdated(); +} + +bool AmneziaApplication::parseCommands() +{ + m_parser.setApplicationDescription(APPLICATION_NAME); + m_parser.addHelpOption(); + m_parser.addVersionOption(); + + QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" }; + m_parser.addOption(c_autostart); + + QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" }; + m_parser.addOption(c_cleanup); + + m_parser.process(*this); + + if (m_parser.isSet(c_cleanup)) { + Logger::cleanUp(); + QTimer::singleShot(100, this, [this] { quit(); }); + exec(); + return false; + } + return true; +} + +QQmlApplicationEngine *AmneziaApplication::qmlEngine() const +{ + return m_engine; +} + +void AmneziaApplication::initModels() +{ + m_containersModel.reset(new ContainersModel(this)); + m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get()); + + m_serversModel.reset(new ServersModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get()); + connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(), + &ContainersModel::updateModel); + connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(), + &ContainersModel::setDefaultContainer); + m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better? + + m_languageModel.reset(new LanguageModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); + connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator); + connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated); + + m_sitesModel.reset(new SitesModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); + + m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); + + m_openVpnConfigModel.reset(new OpenVpnConfigModel(this)); + m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get()); + + m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this)); + m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get()); + + m_cloakConfigModel.reset(new CloakConfigModel(this)); + m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get()); + + m_wireGuardConfigModel.reset(new WireGuardConfigModel(this)); + m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get()); + + m_awgConfigModel.reset(new AwgConfigModel(this)); + m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get()); + +#ifdef Q_OS_WINDOWS + m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this)); + m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get()); +#endif + + m_sftpConfigModel.reset(new SftpConfigModel(this)); + m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get()); + + m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); + connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked, + m_serversModel.get(), &ServersModel::clearCachedProfile); + + connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this, + [this](const QString &clientId, const QString &clientName, const DockerContainer container, + ServerCredentials credentials) { + m_serversModel->reloadContainerConfig(); + m_clientManagementModel->appendClient(clientId, clientName, container, credentials); + emit m_configurator->clientModelUpdated(); + }); +} + +void AmneziaApplication::initControllers() +{ + m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection)); + m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get()); + + connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(), + &ConnectionController::onTranslationsUpdated); + + m_pageController.reset(new PageController(m_serversModel, m_settings)); + m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); + + m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings)); + m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); + connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(), + &PageController::showPassphraseRequestDrawer); + connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(), + &InstallController::setEncryptedPassphrase); + connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(), + &ConnectionController::onCurrentContainerUpdated); + + m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings)); + m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); + + m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel, + m_settings, m_configurator)); + m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get()); + + m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings)); + m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); + if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { + QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); + } + connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(), + &ServersModel::toggleAmneziaDns); + + m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); + m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); + + m_systemController.reset(new SystemController(m_settings)); + m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get()); + + m_cloudController.reset(new ApiController(m_serversModel, m_containersModel)); + m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get()); +} diff --git a/client/amnezia_application.h b/client/amnezia_application.h index aff853a6..52427281 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -1,127 +1,127 @@ -#ifndef AMNEZIA_APPLICATION_H -#define AMNEZIA_APPLICATION_H - -#include -#include -#include -#include -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - #include -#else - #include -#endif - -#include "settings.h" -#include "vpnconnection.h" - -#include "configurators/vpn_configurator.h" - -#include "ui/controllers/connectionController.h" -#include "ui/controllers/exportController.h" -#include "ui/controllers/importController.h" -#include "ui/controllers/installController.h" -#include "ui/controllers/pageController.h" -#include "ui/controllers/settingsController.h" -#include "ui/controllers/sitesController.h" -#include "ui/controllers/systemController.h" -#include "ui/controllers/apiController.h" -#include "ui/models/containers_model.h" -#include "ui/models/languageModel.h" -#include "ui/models/protocols/cloakConfigModel.h" -#include "ui/notificationhandler.h" -#ifdef Q_OS_WINDOWS - #include "ui/models/protocols/ikev2ConfigModel.h" -#endif -#include "ui/models/protocols/awgConfigModel.h" -#include "ui/models/protocols/openvpnConfigModel.h" -#include "ui/models/protocols/shadowsocksConfigModel.h" -#include "ui/models/protocols/wireguardConfigModel.h" -#include "ui/models/protocols_model.h" -#include "ui/models/servers_model.h" -#include "ui/models/services/sftpConfigModel.h" -#include "ui/models/sites_model.h" -#include "ui/models/clientManagementModel.h" - -#define amnApp (static_cast(QCoreApplication::instance())) - -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - #define AMNEZIA_BASE_CLASS QGuiApplication -#else - #define AMNEZIA_BASE_CLASS SingleApplication - #define QAPPLICATION_CLASS QApplication - #include "singleapplication.h" -#endif - -class AmneziaApplication : public AMNEZIA_BASE_CLASS -{ - Q_OBJECT -public: -#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - AmneziaApplication(int &argc, char *argv[]); -#else - AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false, - SingleApplication::Options options = SingleApplication::User, int timeout = 1000, - const QString &userData = {}); -#endif - virtual ~AmneziaApplication(); - - void init(); - void registerTypes(); - void loadFonts(); - void loadTranslator(); - void updateTranslator(const QLocale &locale); - bool parseCommands(); - - QQmlApplicationEngine *qmlEngine() const; - -signals: - void translationsUpdated(); - -private: - void initModels(); - void initControllers(); - - QQmlApplicationEngine *m_engine {}; - std::shared_ptr m_settings; - std::shared_ptr m_configurator; - - QSharedPointer m_containerProps; - QSharedPointer m_protocolProps; - - QSharedPointer m_translator; - QCommandLineParser m_parser; - - QSharedPointer m_containersModel; - QSharedPointer m_serversModel; - QSharedPointer m_languageModel; - QSharedPointer m_protocolsModel; - QSharedPointer m_sitesModel; - QSharedPointer m_clientManagementModel; - - QScopedPointer m_openVpnConfigModel; - QScopedPointer m_shadowSocksConfigModel; - QScopedPointer m_cloakConfigModel; - QScopedPointer m_wireGuardConfigModel; - QScopedPointer m_awgConfigModel; -#ifdef Q_OS_WINDOWS - QScopedPointer m_ikev2ConfigModel; -#endif - - QScopedPointer m_sftpConfigModel; - - QSharedPointer m_vpnConnection; - QThread m_vpnConnectionThread; - QScopedPointer m_notificationHandler; - - QScopedPointer m_connectionController; - QScopedPointer m_pageController; - QScopedPointer m_installController; - QScopedPointer m_importController; - QScopedPointer m_exportController; - QScopedPointer m_settingsController; - QScopedPointer m_sitesController; - QScopedPointer m_systemController; - QScopedPointer m_cloudController; -}; - -#endif // AMNEZIA_APPLICATION_H +#ifndef AMNEZIA_APPLICATION_H +#define AMNEZIA_APPLICATION_H + +#include +#include +#include +#include +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + #include +#else + #include +#endif + +#include "settings.h" +#include "vpnconnection.h" + +#include "configurators/vpn_configurator.h" + +#include "ui/controllers/connectionController.h" +#include "ui/controllers/exportController.h" +#include "ui/controllers/importController.h" +#include "ui/controllers/installController.h" +#include "ui/controllers/pageController.h" +#include "ui/controllers/settingsController.h" +#include "ui/controllers/sitesController.h" +#include "ui/controllers/systemController.h" +#include "ui/controllers/apiController.h" +#include "ui/models/containers_model.h" +#include "ui/models/languageModel.h" +#include "ui/models/protocols/cloakConfigModel.h" +#include "ui/notificationhandler.h" +#ifdef Q_OS_WINDOWS + #include "ui/models/protocols/ikev2ConfigModel.h" +#endif +#include "ui/models/protocols/awgConfigModel.h" +#include "ui/models/protocols/openvpnConfigModel.h" +#include "ui/models/protocols/shadowsocksConfigModel.h" +#include "ui/models/protocols/wireguardConfigModel.h" +#include "ui/models/protocols_model.h" +#include "ui/models/servers_model.h" +#include "ui/models/services/sftpConfigModel.h" +#include "ui/models/sites_model.h" +#include "ui/models/clientManagementModel.h" + +#define amnApp (static_cast(QCoreApplication::instance())) + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + #define AMNEZIA_BASE_CLASS QGuiApplication +#else + #define AMNEZIA_BASE_CLASS SingleApplication + #define QAPPLICATION_CLASS QApplication + #include "singleapplication.h" +#endif + +class AmneziaApplication : public AMNEZIA_BASE_CLASS +{ + Q_OBJECT +public: +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + AmneziaApplication(int &argc, char *argv[]); +#else + AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false, + SingleApplication::Options options = SingleApplication::User, int timeout = 1000, + const QString &userData = {}); +#endif + virtual ~AmneziaApplication(); + + void init(); + void registerTypes(); + void loadFonts(); + void loadTranslator(); + void updateTranslator(const QLocale &locale); + bool parseCommands(); + + QQmlApplicationEngine *qmlEngine() const; + +signals: + void translationsUpdated(); + +private: + void initModels(); + void initControllers(); + + QQmlApplicationEngine *m_engine {}; + std::shared_ptr m_settings; + std::shared_ptr m_configurator; + + QSharedPointer m_containerProps; + QSharedPointer m_protocolProps; + + QSharedPointer m_translator; + QCommandLineParser m_parser; + + QSharedPointer m_containersModel; + QSharedPointer m_serversModel; + QSharedPointer m_languageModel; + QSharedPointer m_protocolsModel; + QSharedPointer m_sitesModel; + QSharedPointer m_clientManagementModel; + + QScopedPointer m_openVpnConfigModel; + QScopedPointer m_shadowSocksConfigModel; + QScopedPointer m_cloakConfigModel; + QScopedPointer m_wireGuardConfigModel; + QScopedPointer m_awgConfigModel; +#ifdef Q_OS_WINDOWS + QScopedPointer m_ikev2ConfigModel; +#endif + + QScopedPointer m_sftpConfigModel; + + QSharedPointer m_vpnConnection; + QThread m_vpnConnectionThread; + QScopedPointer m_notificationHandler; + + QScopedPointer m_connectionController; + QScopedPointer m_pageController; + QScopedPointer m_installController; + QScopedPointer m_importController; + QScopedPointer m_exportController; + QScopedPointer m_settingsController; + QScopedPointer m_sitesController; + QScopedPointer m_systemController; + QScopedPointer m_cloudController; +}; + +#endif // AMNEZIA_APPLICATION_H From 4176d0130a87520f940630d3f067f8bfe0f80161 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 20 Jan 2024 08:35:24 -0500 Subject: [PATCH 39/65] Fix import of some native WG configs --- client/ui/controllers/importController.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 44fc1cb9..734c17c3 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -239,7 +239,16 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) // && !configMap.value("PresharedKey").isEmpty() && !configMap.value("PublicKey").isEmpty()) { lastConfig[config_key::client_priv_key] = configMap.value("PrivateKey"); lastConfig[config_key::client_ip] = configMap.value("Address"); - lastConfig[config_key::psk_key] = configMap.value("PresharedKey"); + if (!configMap.value("PresharedKey").isEmpty()) { + lastConfig[config_key::psk_key] = configMap.value("PresharedKey"); + } else if (!configMap.value("PreSharedKey").isEmpty()) { + lastConfig[config_key::psk_key] = configMap.value("PreSharedKey"); + } else { + qDebug() << "Failed to import profile"; + emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); + return QJsonObject(); + } + lastConfig[config_key::server_pub_key] = configMap.value("PublicKey"); // } else { // qDebug() << "Failed to import profile"; From 3e02dfef634f516bc936541c392a320e04b41fd4 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 16:40:12 +0300 Subject: [PATCH 40/65] Refactoring Android logging (#511) Refactoring Android logging --- client/amnezia_application.cpp | 14 +- client/android/AndroidManifest.xml | 11 +- client/android/build.gradle.kts | 1 - .../vpn/protocol/openvpn/OpenVpnClient.kt | 78 +++--- client/android/res/xml/backup_content.xml | 4 + .../android/res/xml/data_extraction_rules.xml | 9 + .../src/org/amnezia/vpn/AmneziaActivity.kt | 60 +++-- .../src/org/amnezia/vpn/AmneziaApplication.kt | 6 + .../src/org/amnezia/vpn/AmneziaVpnService.kt | 47 ++-- .../org/amnezia/vpn/ImportConfigActivity.kt | 10 +- .../android/src/org/amnezia/vpn/IpcMessage.kt | 3 +- client/android/src/org/amnezia/vpn/Prefs.kt | 25 -- .../src/org/amnezia/vpn/VpnRequestActivity.kt | 2 +- client/android/utils/build.gradle.kts | 4 + client/android/utils/src/main/kotlin/Log.kt | 249 ++++++++++++++++-- client/android/utils/src/main/kotlin/Prefs.kt | 58 ++++ .../utils/src/main/kotlin/net/NetworkState.kt | 4 +- .../platforms/android/android_controller.cpp | 115 +++++++- client/platforms/android/android_controller.h | 16 +- client/settings.cpp | 4 +- client/settings.h | 2 +- client/ui/controllers/settingsController.cpp | 9 + 22 files changed, 577 insertions(+), 154 deletions(-) create mode 100644 client/android/res/xml/backup_content.xml create mode 100644 client/android/res/xml/data_extraction_rules.xml delete mode 100644 client/android/src/org/amnezia/vpn/Prefs.kt create mode 100644 client/android/utils/src/main/kotlin/Prefs.kt diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 28b028ff..f0a7769a 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -91,6 +91,13 @@ void AmneziaApplication::init() initControllers(); #ifdef Q_OS_ANDROID + if(!AndroidController::initLogging()) { + qFatal("Android logging initialization failed"); + } + AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs()); + connect(m_settings.get(), &Settings::saveLogsChanged, + AndroidController::instance(), &AndroidController::setSaveLogs); + connect(AndroidController::instance(), &AndroidController::initConnectionState, this, [this](Vpn::ConnectionState state) { m_connectionController->onConnectionStateChanged(state); @@ -98,10 +105,7 @@ void AmneziaApplication::init() m_vpnConnection->restoreConnection(); }); if (!AndroidController::instance()->initialize()) { - qCritical() << QString("Init failed"); - if (m_vpnConnection) - emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error); - return; + qFatal("Android controller initialization failed"); } connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) { @@ -143,11 +147,13 @@ void AmneziaApplication::init() m_engine->load(url); m_systemController->setQmlRoot(m_engine->rootObjects().value(0)); +#ifndef Q_OS_ANDROID if (m_settings->isSaveLogs()) { if (!Logger::init()) { qWarning() << "Initialization of debug subsystem failed"; } } +#endif #ifdef Q_OS_WIN if (m_parser.isSet("a")) diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 960dc87d..fb417f05 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -1,6 +1,8 @@ - + android:theme="@style/NoActionBar" + android:fullBackupContent="@xml/backup_content" + android:dataExtractionRules="@xml/data_extraction_rules" + tools:targetApi="s"> - + diff --git a/client/android/build.gradle.kts b/client/android/build.gradle.kts index 757989aa..0a1fe83f 100644 --- a/client/android/build.gradle.kts +++ b/client/android/build.gradle.kts @@ -108,7 +108,6 @@ dependencies { implementation(project(":cloak")) implementation(libs.androidx.core) implementation(libs.androidx.activity) - implementation(libs.androidx.security.crypto) implementation(libs.kotlinx.coroutines) implementation(libs.bundles.androidx.camera) implementation(libs.google.mlkit) diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt index c716a970..f489c980 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt @@ -52,7 +52,7 @@ class OpenVpnClient( // Callback to construct a new tun builder // Should be called first. override fun tun_builder_new(): Boolean { - Log.v(TAG, "tun_builder_new") + Log.d(TAG, "tun_builder_new") configBuilder.clearAddresses() return true } @@ -60,7 +60,7 @@ class OpenVpnClient( // Callback to set MTU of the VPN interface // Never called more than once per tun_builder session. override fun tun_builder_set_mtu(mtu: Int): Boolean { - Log.v(TAG, "tun_builder_set_mtu: $mtu") + Log.d(TAG, "tun_builder_set_mtu: $mtu") configBuilder.setMtu(mtu) return true } @@ -71,7 +71,7 @@ class OpenVpnClient( address: String, prefix_length: Int, gateway: String, ipv6: Boolean, net30: Boolean ): Boolean { - Log.v(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30") + Log.d(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30") configBuilder.addAddress(InetNetwork(address, prefix_length)) return true } @@ -80,7 +80,7 @@ class OpenVpnClient( // May be called more than once per tun_builder session // metric is optional and should be ignored if < 0 override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { - Log.v(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6") + Log.d(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6") if (address == "remote_host") return false configBuilder.addRoute(InetNetwork(address, prefix_length)) return true @@ -90,7 +90,7 @@ class OpenVpnClient( // May be called more than once per tun_builder session // metric is optional and should be ignored if < 0 override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { - Log.v(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6") + Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { configBuilder.excludeRoute(InetNetwork(address, prefix_length)) } @@ -104,7 +104,7 @@ class OpenVpnClient( // domain should be routed. // Guaranteed to be called after tun_builder_reroute_gw. override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean { - Log.v(TAG, "tun_builder_add_dns_server: $address, $ipv6") + Log.d(TAG, "tun_builder_add_dns_server: $address, $ipv6") configBuilder.addDnsServer(parseInetAddress(address)) return true } @@ -119,28 +119,28 @@ class OpenVpnClient( // ignored for that family // See also Android's VPNService.Builder.allowFamily method /* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean { - Log.v(TAG, "tun_builder_set_allow_family: $af, $allow") + Log.d(TAG, "tun_builder_set_allow_family: $af, $allow") return true } */ // Callback to set address of remote server // Never called more than once per tun_builder session. override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean { - Log.v(TAG, "tun_builder_set_remote_address: $address, $ipv6") + Log.d(TAG, "tun_builder_set_remote_address: $address, $ipv6") return true } // Optional callback that indicates OSI layer, should be 2 or 3. // Defaults to 3. override fun tun_builder_set_layer(layer: Int): Boolean { - Log.v(TAG, "tun_builder_set_layer: $layer") + Log.d(TAG, "tun_builder_set_layer: $layer") return layer == 3 } // Callback to set the session name // Never called more than once per tun_builder session. override fun tun_builder_set_session_name(name: String): Boolean { - Log.v(TAG, "tun_builder_set_session_name: $name") + Log.d(TAG, "tun_builder_set_session_name: $name") return true } @@ -149,7 +149,7 @@ class OpenVpnClient( // if the tunnel could not be established. // Always called last after tun_builder session has been configured. override fun tun_builder_establish(): Int { - Log.v(TAG, "tun_builder_establish") + Log.d(TAG, "tun_builder_establish") return establish(configBuilder) } @@ -159,7 +159,7 @@ class OpenVpnClient( // flags are defined in RGWFlags (rgwflags.hpp). // Never called more than once per tun_builder session. override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean { - Log.v(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags") + Log.d(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags") if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true if (ipv4) { configBuilder.addRoute(InetNetwork("0.0.0.0", 0)) @@ -176,7 +176,7 @@ class OpenVpnClient( // reroute_dns parameter. // Guaranteed to be called after tun_builder_reroute_gw. override fun tun_builder_add_search_domain(domain: String): Boolean { - Log.v(TAG, "tun_builder_add_search_domain: $domain") + Log.d(TAG, "tun_builder_add_search_domain: $domain") configBuilder.setSearchDomain(domain) return true } @@ -184,7 +184,7 @@ class OpenVpnClient( // Callback to set the HTTP proxy // Never called more than once per tun_builder session. override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean { - Log.v(TAG, "tun_builder_set_proxy_http: $host, $port") + Log.d(TAG, "tun_builder_set_proxy_http: $host, $port") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { try { configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port)) @@ -199,7 +199,7 @@ class OpenVpnClient( // Callback to set the HTTPS proxy // Never called more than once per tun_builder session. override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean { - Log.v(TAG, "tun_builder_set_proxy_https: $host, $port") + Log.d(TAG, "tun_builder_set_proxy_https: $host, $port") return false } @@ -208,7 +208,7 @@ class OpenVpnClient( // to exclude them from the VPN network are generated // This should be a list of CIDR networks (e.g. 192.168.0.0/24) override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec { - Log.v(TAG, "tun_builder_get_local_networks: $ipv6") + Log.d(TAG, "tun_builder_get_local_networks: $ipv6") val networks = ClientAPI_StringVec() for (address in getLocalNetworks(ipv6)) { networks.add(address.toString()) @@ -222,21 +222,21 @@ class OpenVpnClient( // tun_builder_reroute_gw. Route metric is ignored // if < 0. /* override fun tun_builder_set_route_metric_default(metric: Int): Boolean { - Log.v(TAG, "tun_builder_set_route_metric_default: $metric") + Log.d(TAG, "tun_builder_set_route_metric_default: $metric") return super.tun_builder_set_route_metric_default(metric) } */ // Callback to add a host which should bypass the proxy // May be called more than once per tun_builder session /* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean { - Log.v(TAG, "tun_builder_add_proxy_bypass: $bypass_host") + Log.d(TAG, "tun_builder_add_proxy_bypass: $bypass_host") return super.tun_builder_add_proxy_bypass(bypass_host) } */ // Callback to set the proxy "Auto Config URL" // Never called more than once per tun_builder session. /* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean { - Log.v(TAG, "tun_builder_set_proxy_auto_config_url: $url") + Log.d(TAG, "tun_builder_set_proxy_auto_config_url: $url") return super.tun_builder_set_proxy_auto_config_url(url) } */ @@ -245,7 +245,7 @@ class OpenVpnClient( // May be called more than once per tun_builder session. // Guaranteed to be called after tun_builder_reroute_gw. /* override fun tun_builder_add_wins_server(address: String): Boolean { - Log.v(TAG, "tun_builder_add_wins_server: $address") + Log.d(TAG, "tun_builder_add_wins_server: $address") return super.tun_builder_add_wins_server(address) } */ @@ -254,7 +254,7 @@ class OpenVpnClient( // set the "Connection-specific DNS Suffix" property on // the TAP driver. /* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean { - Log.v(TAG, "tun_builder_set_adapter_domain_suffix: $name") + Log.d(TAG, "tun_builder_set_adapter_domain_suffix: $name") return super.tun_builder_set_adapter_domain_suffix(name) } */ @@ -266,13 +266,13 @@ class OpenVpnClient( // tun_builder_establish_lite() will be called. Otherwise, // tun_builder_establish() will be called. /* override fun tun_builder_persist(): Boolean { - Log.v(TAG, "tun_builder_persist") + Log.d(TAG, "tun_builder_persist") return super.tun_builder_persist() } */ // Indicates a reconnection with persisted tun state. /* override fun tun_builder_establish_lite() { - Log.v(TAG, "tun_builder_establish_lite") + Log.d(TAG, "tun_builder_establish_lite") super.tun_builder_establish_lite() } */ @@ -280,7 +280,7 @@ class OpenVpnClient( // If disconnect == true, then the teardown is occurring // prior to final disconnect. /* override fun tun_builder_teardown(disconnect: Boolean) { - Log.v(TAG, "tun_builder_teardown: $disconnect") + Log.d(TAG, "tun_builder_teardown: $disconnect") super.tun_builder_teardown(disconnect) } */ @@ -290,7 +290,7 @@ class OpenVpnClient( // Parse OpenVPN configuration file. override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig { - Log.v(TAG, "eval_config") + Log.d(TAG, "eval_config") return super.eval_config(arg0) } @@ -299,7 +299,7 @@ class OpenVpnClient( // to event() and log() functions. Make sure to call eval_config() // and possibly provide_creds() as well before this function. override fun connect(): ClientAPI_Status { - Log.v(TAG, "connect") + Log.d(TAG, "connect") return super.connect() } @@ -307,7 +307,7 @@ class OpenVpnClient( // Will be called from the thread executing connect(). // The remote and ipv6 are the remote host this socket will connect to override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean { - Log.v(TAG, "socket_protect: $socket, $remote, $ipv6") + Log.d(TAG, "socket_protect: $socket, $remote, $ipv6") return protect(socket) } @@ -315,7 +315,7 @@ class OpenVpnClient( // May be called asynchronously from a different thread // when connect() is running. override fun stop() { - Log.v(TAG, "stop") + Log.d(TAG, "stop") super.stop() } @@ -323,21 +323,21 @@ class OpenVpnClient( // when network is down. May be called from a different thread // when connect() is running. override fun pause(reason: String) { - Log.v(TAG, "pause: $reason") + Log.d(TAG, "pause: $reason") super.pause(reason) } // Resume the client after it has been paused. May be called from a // different thread when connect() is running. override fun resume() { - Log.v(TAG, "resume") + Log.d(TAG, "resume") super.resume() } // Do a disconnect/reconnect cycle n seconds from now. May be called // from a different thread when connect() is running. override fun reconnect(seconds: Int) { - Log.v(TAG, "reconnect") + Log.d(TAG, "reconnect: $seconds") super.reconnect(seconds) } @@ -346,14 +346,14 @@ class OpenVpnClient( // CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE // state. override fun pause_on_connection_timeout(): Boolean { - Log.v(TAG, "pause_on_connection_timeout") + Log.d(TAG, "pause_on_connection_timeout") return false } // Return information about the most recent connection. Should be called // after an event of type "CONNECTED". /* override fun connection_info(): ClientAPI_ConnectionInfo { - Log.v(TAG, "connection_info") + Log.d(TAG, "connection_info") return super.connection_info() } */ @@ -366,7 +366,7 @@ class OpenVpnClient( override fun event(event: ClientAPI_Event) { val name = event.name val info = event.info - Log.v(TAG, "OpenVpn event: $name: $info") + Log.d(TAG, "OpenVpn event: $name: $info") when (name) { "COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info") "CONNECTED" -> state.value = CONNECTED @@ -398,31 +398,31 @@ class OpenVpnClient( // return transport stats only override fun transport_stats(): ClientAPI_TransportStats { - Log.v(TAG, "transport_stats") + Log.d(TAG, "transport_stats") return super.transport_stats() } // return a stats value, index should be >= 0 and < stats_n() /* override fun stats_value(index: Int): Long { - Log.v(TAG, "stats_value: $index") + Log.d(TAG, "stats_value: $index") return super.stats_value(index) } */ // return all stats in a bundle /* override fun stats_bundle(): ClientAPI_LLVector { - Log.v(TAG, "stats_bundle") + Log.d(TAG, "stats_bundle") return super.stats_bundle() } */ // return tun stats only /* override fun tun_stats(): ClientAPI_InterfaceStats { - Log.v(TAG, "tun_stats") + Log.d(TAG, "tun_stats") return super.tun_stats() } */ // post control channel message /* override fun post_cc_msg(msg: String) { - Log.v(TAG, "post_cc_msg: $msg") + Log.d(TAG, "post_cc_msg: $msg") super.post_cc_msg(msg) } */ } diff --git a/client/android/res/xml/backup_content.xml b/client/android/res/xml/backup_content.xml new file mode 100644 index 00000000..33260809 --- /dev/null +++ b/client/android/res/xml/backup_content.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/client/android/res/xml/data_extraction_rules.xml b/client/android/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..f5f079cc --- /dev/null +++ b/client/android/res/xml/data_extraction_rules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 0c0ec0f9..2ed2c7f7 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -143,7 +143,7 @@ class AmneziaActivity : QtActivity() { */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.v(TAG, "Create Amnezia activity: $intent") + Log.d(TAG, "Create Amnezia activity: $intent") mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) vpnServiceMessenger = IpcMessenger( onDeadObjectException = ::doUnbindService, @@ -154,7 +154,7 @@ class AmneziaActivity : QtActivity() { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - Log.v(TAG, "onNewIntent: $intent") + Log.d(TAG, "onNewIntent: $intent") intent?.let(::processIntent) } @@ -174,7 +174,7 @@ class AmneziaActivity : QtActivity() { override fun onStart() { super.onStart() - Log.v(TAG, "Start Amnezia activity") + Log.d(TAG, "Start Amnezia activity") mainScope.launch { qtInitialized.await() doBindService() @@ -182,13 +182,13 @@ class AmneziaActivity : QtActivity() { } override fun onStop() { - Log.v(TAG, "Stop Amnezia activity") + Log.d(TAG, "Stop Amnezia activity") doUnbindService() super.onStop() } override fun onDestroy() { - Log.v(TAG, "Destroy Amnezia activity") + Log.d(TAG, "Destroy Amnezia activity") mainScope.cancel() super.onDestroy() } @@ -217,7 +217,7 @@ class AmneziaActivity : QtActivity() { CHECK_VPN_PERMISSION_ACTION_CODE -> { when (resultCode) { RESULT_OK -> { - Log.v(TAG, "Vpn permission granted") + Log.d(TAG, "Vpn permission granted") Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show() checkVpnPermissionCallbacks?.run { onSuccess() } } @@ -240,7 +240,7 @@ class AmneziaActivity : QtActivity() { */ @MainThread private fun doBindService() { - Log.v(TAG, "Bind service") + Log.d(TAG, "Bind service") Intent(this, AmneziaVpnService::class.java).also { bindService(it, serviceConnection, BIND_ABOVE_CLIENT) } @@ -251,7 +251,7 @@ class AmneziaActivity : QtActivity() { @MainThread private fun doUnbindService() { if (isInBoundState) { - Log.v(TAG, "Unbind service") + Log.d(TAG, "Unbind service") isWaitingStatus = true QtAndroidController.onServiceDisconnected() vpnServiceMessenger.reset() @@ -286,7 +286,7 @@ class AmneziaActivity : QtActivity() { @MainThread private fun checkVpnPermission(onSuccess: () -> Unit, onFail: () -> Unit) { - Log.v(TAG, "Check VPN permission") + Log.d(TAG, "Check VPN permission") VpnService.prepare(applicationContext)?.let { checkVpnPermissionCallbacks = CheckVpnPermissionCallbacks(onSuccess, onFail) startActivityForResult(it, CHECK_VPN_PERMISSION_ACTION_CODE) @@ -307,7 +307,7 @@ class AmneziaActivity : QtActivity() { } private fun connectToVpn(vpnConfig: String) { - Log.v(TAG, "Connect to VPN") + Log.d(TAG, "Connect to VPN") vpnServiceMessenger.send { Action.CONNECT.packToMessage { putString(VPN_CONFIG, vpnConfig) @@ -316,7 +316,7 @@ class AmneziaActivity : QtActivity() { } private fun startVpnService(vpnConfig: String) { - Log.v(TAG, "Start VPN service") + Log.d(TAG, "Start VPN service") Intent(this, AmneziaVpnService::class.java).apply { putExtra(VPN_CONFIG, vpnConfig) }.also { @@ -325,7 +325,7 @@ class AmneziaActivity : QtActivity() { } private fun disconnectFromVpn() { - Log.v(TAG, "Disconnect from VPN") + Log.d(TAG, "Disconnect from VPN") vpnServiceMessenger.send(Action.DISCONNECT) } @@ -369,7 +369,7 @@ class AmneziaActivity : QtActivity() { @Suppress("unused") fun saveFile(fileName: String, data: String) { - Log.v(TAG, "Save file $fileName") + Log.d(TAG, "Save file $fileName") mainScope.launch { tmpFileContentToSave = data @@ -397,7 +397,7 @@ class AmneziaActivity : QtActivity() { Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) - Log.d(TAG, "File mimyType filter: $mimeTypes") + Log.v(TAG, "File mimyType filter: $mimeTypes") when (mimeTypes.size) { 1 -> type = mimeTypes.first() @@ -416,13 +416,6 @@ class AmneziaActivity : QtActivity() { @Suppress("unused") fun setNotificationText(title: String, message: String, timerSec: Int) { Log.v(TAG, "Set notification text") - Log.w(TAG, "Not yet implemented") - } - - @Suppress("unused") - fun cleanupLogs() { - Log.v(TAG, "Cleanup logs") - Log.w(TAG, "Not yet implemented") } @Suppress("unused") @@ -432,4 +425,29 @@ class AmneziaActivity : QtActivity() { startActivity(it) } } + + @Suppress("unused") + fun setSaveLogs(enabled: Boolean) { + Log.d(TAG, "Set save logs: $enabled") + mainScope.launch { + Log.saveLogs = enabled + vpnServiceMessenger.send { + Action.SET_SAVE_LOGS.packToMessage { + putBoolean(SAVE_LOGS, enabled) + } + } + } + } + + @Suppress("unused") + fun exportLogsFile(fileName: String) { + Log.v(TAG, "Export logs file") + saveFile(fileName, Log.getLogs()) + } + + @Suppress("unused") + fun clearLogs() { + Log.v(TAG, "Clear logs") + Log.clearLogs() + } } diff --git a/client/android/src/org/amnezia/vpn/AmneziaApplication.kt b/client/android/src/org/amnezia/vpn/AmneziaApplication.kt index e9d8fdfb..33182887 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaApplication.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaApplication.kt @@ -5,14 +5,20 @@ import androidx.camera.core.CameraSelector import androidx.camera.core.CameraXConfig import androidx.core.app.NotificationChannelCompat.Builder import androidx.core.app.NotificationManagerCompat +import org.amnezia.vpn.util.Log +import org.amnezia.vpn.util.Prefs import org.qtproject.qt.android.bindings.QtApplication +private const val TAG = "AmneziaApplication" const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification" class AmneziaApplication : QtApplication(), CameraXConfig.Provider { override fun onCreate() { super.onCreate() + Prefs.init(this) + Log.init(this) + Log.d(TAG, "Create Amnezia application") createNotificationChannel() } diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt index 094874c7..ab753716 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -50,6 +50,7 @@ import org.amnezia.vpn.protocol.putStatistics import org.amnezia.vpn.protocol.putStatus import org.amnezia.vpn.protocol.wireguard.Wireguard import org.amnezia.vpn.util.Log +import org.amnezia.vpn.util.Prefs import org.amnezia.vpn.util.net.NetworkState import org.json.JSONException import org.json.JSONObject @@ -58,6 +59,8 @@ private const val TAG = "AmneziaVpnService" const val VPN_CONFIG = "VPN_CONFIG" const val ERROR_MSG = "ERROR_MSG" +const val SAVE_LOGS = "SAVE_LOGS" + const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK" private const val PREFS_CONFIG_KEY = "LAST_CONF" private const val NOTIFICATION_ID = 1337 @@ -118,7 +121,7 @@ class AmneziaVpnService : VpnService() { Action.CONNECT -> { val vpnConfig = msg.data.getString(VPN_CONFIG) - saveConfigToPrefs(vpnConfig) + Prefs.save(PREFS_CONFIG_KEY, vpnConfig) connect(vpnConfig) } @@ -135,6 +138,10 @@ class AmneziaVpnService : VpnService() { } } } + + Action.SET_SAVE_LOGS -> { + Log.saveLogs = msg.data.getBoolean(SAVE_LOGS) + } } } } @@ -179,7 +186,7 @@ class AmneziaVpnService : VpnService() { */ override fun onCreate() { super.onCreate() - Log.v(TAG, "Create Amnezia VPN service") + Log.d(TAG, "Create Amnezia VPN service") mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) connectionScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + connectionExceptionHandler) clientMessenger = IpcMessenger(messengerName = "Client") @@ -193,15 +200,15 @@ class AmneziaVpnService : VpnService() { else intent?.component?.packageName != packageName if (isAlwaysOnCompat) { - Log.v(TAG, "Start service via Always-on") - connect(loadConfigFromPrefs()) + Log.d(TAG, "Start service via Always-on") + connect(Prefs.load(PREFS_CONFIG_KEY)) } else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) { - Log.v(TAG, "Start service after permission check") - connect(loadConfigFromPrefs()) + Log.d(TAG, "Start service after permission check") + connect(Prefs.load(PREFS_CONFIG_KEY)) } else { - Log.v(TAG, "Start service") + Log.d(TAG, "Start service") val vpnConfig = intent?.getStringExtra(VPN_CONFIG) - saveConfigToPrefs(vpnConfig) + Prefs.save(PREFS_CONFIG_KEY, vpnConfig) connect(vpnConfig) } ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, foregroundServiceTypeCompat) @@ -237,7 +244,7 @@ class AmneziaVpnService : VpnService() { } override fun onRevoke() { - Log.v(TAG, "onRevoke") + Log.d(TAG, "onRevoke") // Calls to onRevoke() method may not happen on the main thread of the process mainScope.launch { disconnect() @@ -245,7 +252,7 @@ class AmneziaVpnService : VpnService() { } override fun onDestroy() { - Log.v(TAG, "Destroy service") + Log.d(TAG, "Destroy service") runBlocking { disconnect() disconnectionJob?.join() @@ -256,7 +263,7 @@ class AmneziaVpnService : VpnService() { } private fun stopService() { - Log.v(TAG, "Stop service") + Log.d(TAG, "Stop service") // the coroutine below will be canceled during the onDestroy call mainScope.launch { delay(STOP_SERVICE_TIMEOUT) @@ -272,7 +279,7 @@ class AmneziaVpnService : VpnService() { private fun launchProtocolStateHandler() { mainScope.launch { protocolState.collect { protocolState -> - Log.d(TAG, "Protocol state: $protocolState") + Log.d(TAG, "Protocol state changed: $protocolState") when (protocolState) { CONNECTED -> { clientMessenger.send(ServiceEvent.CONNECTED) @@ -305,7 +312,7 @@ class AmneziaVpnService : VpnService() { @MainThread private fun launchSendingStatistics() { - if (isServiceBound && isConnected) { + /* if (isServiceBound && isConnected) { statisticsSendingJob = mainScope.launch { while (true) { clientMessenger.send { @@ -316,7 +323,7 @@ class AmneziaVpnService : VpnService() { delay(STATISTICS_SENDING_TIMEOUT) } } - } + } */ } @MainThread @@ -328,7 +335,7 @@ class AmneziaVpnService : VpnService() { private fun connect(vpnConfig: String?) { if (isConnected || protocolState.value == CONNECTING) return - Log.v(TAG, "Start VPN connection") + Log.d(TAG, "Start VPN connection") protocolState.value = CONNECTING @@ -357,7 +364,7 @@ class AmneziaVpnService : VpnService() { private fun disconnect() { if (isUnknown || isDisconnected || protocolState.value == DISCONNECTING) return - Log.v(TAG, "Stop VPN connection") + Log.d(TAG, "Stop VPN connection") protocolState.value = DISCONNECTING @@ -383,7 +390,7 @@ class AmneziaVpnService : VpnService() { private fun reconnect() { if (!isConnected) return - Log.v(TAG, "Reconnect VPN") + Log.d(TAG, "Reconnect VPN") protocolState.value = RECONNECTING @@ -439,10 +446,4 @@ class AmneziaVpnService : VpnService() { } else { true } - - private fun loadConfigFromPrefs(): String? = - Prefs.get(this).getString(PREFS_CONFIG_KEY, null) - - private fun saveConfigToPrefs(config: String?) = - Prefs.get(this).edit().putString(PREFS_CONFIG_KEY, config).apply() } diff --git a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt index 309333ef..cae7ab75 100644 --- a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt +++ b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt @@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.v(TAG, "Create Import Config Activity: $intent") + Log.d(TAG, "Create Import Config Activity: $intent") intent?.let(::readConfig) } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - Log.v(TAG, "onNewIntent: $intent") + Log.d(TAG, "onNewIntent: $intent") intent?.let(::readConfig) } private fun readConfig(intent: Intent) { when (intent.action) { ACTION_SEND -> { - Log.v(TAG, "Process SEND action, type: ${intent.type}") + Log.d(TAG, "Process SEND action, type: ${intent.type}") when (intent.type) { "application/octet-stream" -> { intent.getUriCompat()?.let { uri -> @@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() { } ACTION_VIEW -> { - Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}") + Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}") when (intent.scheme) { "file", "content" -> { intent.data?.let { uri -> @@ -128,7 +128,7 @@ class ImportConfigActivity : ComponentActivity() { private fun startMainActivity(config: String) { if (config.isNotBlank()) { - Log.v(TAG, "startMainActivity") + Log.d(TAG, "startMainActivity") Intent(applicationContext, AmneziaActivity::class.java).apply { action = ACTION_IMPORT_CONFIG addCategory(CATEGORY_DEFAULT) diff --git a/client/android/src/org/amnezia/vpn/IpcMessage.kt b/client/android/src/org/amnezia/vpn/IpcMessage.kt index c9d2bd3f..26c3b9de 100644 --- a/client/android/src/org/amnezia/vpn/IpcMessage.kt +++ b/client/android/src/org/amnezia/vpn/IpcMessage.kt @@ -32,7 +32,8 @@ enum class Action : IpcMessage { REGISTER_CLIENT, CONNECT, DISCONNECT, - REQUEST_STATUS + REQUEST_STATUS, + SET_SAVE_LOGS } fun T.packToMessage(): Message diff --git a/client/android/src/org/amnezia/vpn/Prefs.kt b/client/android/src/org/amnezia/vpn/Prefs.kt deleted file mode 100644 index 9b4cb2ba..00000000 --- a/client/android/src/org/amnezia/vpn/Prefs.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.amnezia.vpn - -import android.content.Context -import android.content.SharedPreferences -import androidx.security.crypto.EncryptedSharedPreferences -import androidx.security.crypto.MasterKey -import org.amnezia.vpn.util.Log - -private const val TAG = "Prefs" -private const val PREFS_FILE = "org.amnezia.vpn.prefs" -private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure" - -object Prefs { - fun get(context: Context, appContext: Context = context.applicationContext): SharedPreferences = - try { - EncryptedSharedPreferences( - appContext, - SECURE_PREFS_FILE, - MasterKey(appContext) - ) - } catch (e: Exception) { - Log.e(TAG, "Getting Encryption Storage failed: ${e.message}, plaintext fallback") - appContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE) - } -} diff --git a/client/android/src/org/amnezia/vpn/VpnRequestActivity.kt b/client/android/src/org/amnezia/vpn/VpnRequestActivity.kt index 9dce3d78..c5abbc39 100644 --- a/client/android/src/org/amnezia/vpn/VpnRequestActivity.kt +++ b/client/android/src/org/amnezia/vpn/VpnRequestActivity.kt @@ -25,7 +25,7 @@ class VpnRequestActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.v(TAG, "Start request activity") + Log.d(TAG, "Start request activity") val requestIntent = VpnService.prepare(applicationContext) if (requestIntent != null) { if (getSystemService()!!.isKeyguardLocked) { diff --git a/client/android/utils/build.gradle.kts b/client/android/utils/build.gradle.kts index ac5d1efb..2ad03d61 100644 --- a/client/android/utils/build.gradle.kts +++ b/client/android/utils/build.gradle.kts @@ -15,3 +15,7 @@ android { buildConfig = true } } + +dependencies { + implementation(libs.androidx.security.crypto) +} diff --git a/client/android/utils/src/main/kotlin/Log.kt b/client/android/utils/src/main/kotlin/Log.kt index 82180c3d..03da4507 100644 --- a/client/android/utils/src/main/kotlin/Log.kt +++ b/client/android/utils/src/main/kotlin/Log.kt @@ -1,33 +1,252 @@ package org.amnezia.vpn.util +import android.content.Context +import android.icu.text.DateFormat +import android.icu.text.SimpleDateFormat +import android.os.Build +import android.os.Process +import java.io.File +import java.io.IOException +import java.io.RandomAccessFile +import java.nio.channels.FileChannel +import java.nio.channels.FileLock +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.Date +import java.util.Locale +import java.util.concurrent.locks.ReentrantLock +import org.amnezia.vpn.util.Log.Priority.D +import org.amnezia.vpn.util.Log.Priority.E +import org.amnezia.vpn.util.Log.Priority.F +import org.amnezia.vpn.util.Log.Priority.I +import org.amnezia.vpn.util.Log.Priority.V +import org.amnezia.vpn.util.Log.Priority.W import android.util.Log as NativeLog -class Log { - companion object { - fun v(tag: String, msg: String) = debugLog(tag, msg, NativeLog::v) +private const val TAG = "Log" +private const val LOG_FILE_NAME = "amneziaVPN.log" +private const val ROTATE_LOG_FILE_NAME = "amneziaVPN.rotate.log" +private const val LOCK_FILE_NAME = ".lock" +private const val DATE_TIME_PATTERN = "MM-dd HH:mm:ss.SSS" +private const val PREFS_SAVE_LOGS_KEY = "SAVE_LOGS" +private const val LOG_MAX_FILE_SIZE = 1024 * 1024 - fun d(tag: String, msg: String) = debugLog(tag, msg, NativeLog::d) +/** + * | Priority | Save to file | Logcat logging | + * |-------------------|--------------|----------------------------------------------| + * | Verbose | Don't save | Only in Debug build | + * | Debug | Save | In Debug build or if log saving is enabled | + * | Info, Warn, Error | Save | Enabled | + * | Fatal (Assert) | Save | Enabled. Depending on system configuration, | + * | | | create a report and/or terminate the process | + */ +object Log { + private val dateTimeFormat: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN) + else object : ThreadLocal() { + override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US) + } - fun i(tag: String, msg: String) = log(tag, msg, NativeLog::i) + private lateinit var logDir: File + private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) } + private val rotateLogFile: File by lazy { File(logDir, ROTATE_LOG_FILE_NAME) } - fun w(tag: String, msg: String) = log(tag, msg, NativeLog::w) + private val fileLock: FileChannel by lazy { RandomAccessFile(File(logDir, LOCK_FILE_NAME).path, "rw").channel } + private val threadLock: ReentrantLock by lazy { ReentrantLock() } - fun e(tag: String, msg: String) = log(tag, msg, NativeLog::e) + @Volatile + private var _saveLogs: Boolean = false + var saveLogs: Boolean + get() = _saveLogs + set(value) { + if (_saveLogs != value) { + if (value && !logDir.exists() && !logDir.mkdir()) { + NativeLog.e(TAG, "Failed to create dir: $logDir") + return + } + _saveLogs = value + Prefs.save(PREFS_SAVE_LOGS_KEY, value) + } + } - fun v(tag: String, msg: Any?) = v(tag, msg.toString()) + @JvmStatic + fun v(tag: String, msg: String) = log(tag, msg, V) - fun d(tag: String, msg: Any?) = d(tag, msg.toString()) + @JvmStatic + fun d(tag: String, msg: String) = log(tag, msg, D) - fun i(tag: String, msg: Any?) = i(tag, msg.toString()) + @JvmStatic + fun i(tag: String, msg: String) = log(tag, msg, I) - fun w(tag: String, msg: Any?) = w(tag, msg.toString()) + @JvmStatic + fun w(tag: String, msg: String) = log(tag, msg, W) - fun e(tag: String, msg: Any?) = e(tag, msg.toString()) + @JvmStatic + fun e(tag: String, msg: String) = log(tag, msg, E) - private inline fun log(tag: String, msg: String, delegate: (String, String) -> Unit) = delegate(tag, msg) + @JvmStatic + fun f(tag: String, msg: String) = log(tag, msg, F) - private inline fun debugLog(tag: String, msg: String, delegate: (String, String) -> Unit) { - if (BuildConfig.DEBUG) delegate(tag, msg) + fun v(tag: String, msg: Any?) = v(tag, msg.toString()) + + fun d(tag: String, msg: Any?) = d(tag, msg.toString()) + + fun i(tag: String, msg: Any?) = i(tag, msg.toString()) + + fun w(tag: String, msg: Any?) = w(tag, msg.toString()) + + fun e(tag: String, msg: Any?) = e(tag, msg.toString()) + + fun f(tag: String, msg: Any?) = f(tag, msg.toString()) + + fun init(context: Context) { + v(TAG, "Init Log") + logDir = File(context.cacheDir, "logs") + saveLogs = Prefs.load(PREFS_SAVE_LOGS_KEY) + } + + fun getLogs(): String = + "${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}" + + fun clearLogs() { + withLock { + logFile.delete() + rotateLogFile.delete() } } + + private fun log(tag: String, msg: String, priority: Priority) { + if (saveLogs && priority != V) saveLogMsg(formatLogMsg(tag, msg, priority)) + + if (priority == F) { + NativeLog.wtf(tag, msg) + } else if ( + (priority != V && priority != D) || + (priority == V && BuildConfig.DEBUG) || + (priority == D && (BuildConfig.DEBUG || saveLogs)) + ) { + NativeLog.println(priority.level, tag, msg) + } + } + + private fun saveLogMsg(msg: String) { + withTryLock(condition = { logFile.length() > LOG_MAX_FILE_SIZE }) { + logFile.renameTo(rotateLogFile) + } + try { + logFile.appendText(msg) + } catch (e: IOException) { + NativeLog.e(TAG, "Failed to write log: $e") + } + } + + private fun formatLogMsg(tag: String, msg: String, priority: Priority): String { + val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter) + } else { + @Suppress("UNCHECKED_CAST") + (dateTimeFormat as ThreadLocal).get()?.format(Date()) + } + return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " + + "$tag: $msg\n" + } + + private fun deviceInfo(): String { + val sb = StringBuilder() + sb.append("Model: ").appendLine(Build.MODEL) + sb.append("Brand: ").appendLine(Build.BRAND) + sb.append("Product: ").appendLine(Build.PRODUCT) + sb.append("Device: ").appendLine(Build.DEVICE) + sb.append("Codename: ").appendLine(Build.VERSION.CODENAME) + sb.append("Release: ").appendLine(Build.VERSION.RELEASE) + sb.append("SDK: ").appendLine(Build.VERSION.SDK_INT) + sb.append("ABI: ").appendLine(Build.SUPPORTED_ABIS.joinToString()) + return sb.toString() + } + + private fun readLogs(): String { + var logText = "" + withLock { + try { + if (rotateLogFile.exists()) logText = rotateLogFile.readText() + if (logFile.exists()) logText += logFile.readText() + } catch (e: IOException) { + val errorMsg = "Failed to read log: $e" + NativeLog.e(TAG, errorMsg) + logText += errorMsg + } + } + return logText + } + + private fun getLogcat(): String { + try { + val process = ProcessBuilder("logcat", "-d").redirectErrorStream(true).start() + return process.inputStream.reader().readText() + } catch (e: IOException) { + val errorMsg = "Failed to get logcat log: $e" + NativeLog.e(TAG, errorMsg) + return errorMsg + } + } + + private fun withLock(block: () -> Unit) { + threadLock.lock() + try { + var l: FileLock? = null + try { + l = fileLock.lock() + block() + } catch (e: IOException) { + NativeLog.e(TAG, "Failed to get file lock: $e") + } finally { + try { + l?.release() + } catch (e: IOException) { + NativeLog.e(TAG, "Failed to release file lock: $e") + } + } + } finally { + threadLock.unlock() + } + } + + private fun withTryLock(condition: () -> Boolean, block: () -> Unit) { + if (condition()) { + if (threadLock.tryLock()) { + try { + if (condition()) { + var l: FileLock? = null + try { + l = fileLock.tryLock() + if (l != null) { + if (condition()) { + block() + } + } + } catch (e: IOException) { + NativeLog.e(TAG, "Failed to get file tryLock: $e") + } finally { + try { + l?.release() + } catch (e: IOException) { + NativeLog.e(TAG, "Failed to release file tryLock: $e") + } + } + } + } finally { + threadLock.unlock() + } + } + } + } + + private enum class Priority(val level: Int) { + V(2), + D(3), + I(4), + W(5), + E(6), + F(7) + } } diff --git a/client/android/utils/src/main/kotlin/Prefs.kt b/client/android/utils/src/main/kotlin/Prefs.kt new file mode 100644 index 00000000..9afbc7be --- /dev/null +++ b/client/android/utils/src/main/kotlin/Prefs.kt @@ -0,0 +1,58 @@ +package org.amnezia.vpn.util + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import kotlin.reflect.typeOf + +private const val TAG = "Prefs" +private const val PREFS_FILE = "org.amnezia.vpn.prefs" +private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure" + +object Prefs { + private lateinit var app: Application + val prefs: SharedPreferences + get() = try { + EncryptedSharedPreferences( + app, + SECURE_PREFS_FILE, + MasterKey(app) + ) + } catch (e: Exception) { + Log.e(TAG, "Getting Encryption Storage failed: $e, plaintext fallback") + app.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE) + } + + fun init(app: Application) { + Log.v(TAG, "Init Prefs") + this.app = app + } + + fun save(key: String, value: Boolean) = + prefs.edit().putBoolean(key, value).apply() + + fun save(key: String, value: String?) = + prefs.edit().putString(key, value).apply() + + fun save(key: String, value: Int) = + prefs.edit().putInt(key, value).apply() + + fun save(key: String, value: Long) = + prefs.edit().putLong(key, value).apply() + + fun save(key: String, value: Float) = + prefs.edit().putFloat(key, value).apply() + + inline fun load(key: String): T { + return when (typeOf()) { + typeOf() -> prefs.getBoolean(key, false) + typeOf() -> prefs.getString(key, "") + typeOf() -> prefs.getInt(key, 0) + typeOf() -> prefs.getLong(key, 0L) + typeOf() -> prefs.getFloat(key, 0f) + else -> throw IllegalArgumentException("SharedPreferences does not support type: ${typeOf()}") + } as T + } +} diff --git a/client/android/utils/src/main/kotlin/net/NetworkState.kt b/client/android/utils/src/main/kotlin/net/NetworkState.kt index 42d7baee..957fc3cb 100644 --- a/client/android/utils/src/main/kotlin/net/NetworkState.kt +++ b/client/android/utils/src/main/kotlin/net/NetworkState.kt @@ -82,7 +82,7 @@ class NetworkState( fun bindNetworkListener() { if (isListenerBound) return - Log.v(TAG, "Bind network listener") + Log.d(TAG, "Bind network listener") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -95,7 +95,7 @@ class NetworkState( fun unbindNetworkListener() { if (!isListenerBound) return - Log.v(TAG, "Unbind network listener") + Log.d(TAG, "Unbind network listener") connectivityManager.unregisterNetworkCallback(networkCallback) isListenerBound = false currentNetwork = null diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 225ceebe..5d442b81 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -12,13 +12,15 @@ namespace AndroidController *s_instance = nullptr; constexpr auto QT_ANDROID_CONTROLLER_CLASS = "org/amnezia/vpn/qt/QtAndroidController"; + constexpr auto ANDROID_LOG_CLASS = "org/amnezia/vpn/util/Log"; + constexpr auto TAG = "AmneziaQt"; } // namespace AndroidController::AndroidController() : QObject() { connect(this, &AndroidController::status, this, [this](AndroidController::ConnectionState state) { - qDebug() << "Android event: status; state:" << textConnectionState(state); + qDebug() << "Android event: status =" << textConnectionState(state); if (isWaitingStatus) { qDebug() << "Initialization by service status"; isWaitingStatus = false; @@ -126,24 +128,19 @@ bool AndroidController::initialize() // static template -auto AndroidController::callActivityMethod(const char *methodName, const char *signature, - const std::function &defValue, Args &&...args) +auto AndroidController::callActivityMethod(const char *methodName, const char *signature, Args &&...args) { qDebug() << "Call activity method:" << methodName; QJniObject activity = AndroidUtils::getActivity(); - if (activity.isValid()) { - return activity.callMethod(methodName, signature, std::forward(args)...); - } else { - qCritical() << "Activity is not valid"; - return defValue(); - } + Q_ASSERT(activity.isValid()); + return activity.callMethod(methodName, signature, std::forward(args)...); } // static template void AndroidController::callActivityMethod(const char *methodName, const char *signature, Args &&...args) { - callActivityMethod(methodName, signature, [] {}, std::forward(args)...); + callActivityMethod(methodName, signature, std::forward(args)...); } ErrorCode AndroidController::start(const QJsonObject &vpnConfig) @@ -199,6 +196,104 @@ void AndroidController::startQrReaderActivity() callActivityMethod("startQrCodeReader", "()V"); } +void AndroidController::setSaveLogs(bool enabled) +{ + callActivityMethod("setSaveLogs", "(Z)V", enabled); +} + +void AndroidController::exportLogsFile(const QString &fileName) +{ + callActivityMethod("exportLogsFile", "(Ljava/lang/String;)V", + QJniObject::fromString(fileName).object()); +} + +void AndroidController::clearLogs() +{ + callActivityMethod("clearLogs", "()V"); +} + +// Moving log processing to the Android side +jclass AndroidController::log; +jmethodID AndroidController::logDebug; +jmethodID AndroidController::logInfo; +jmethodID AndroidController::logWarning; +jmethodID AndroidController::logError; +jmethodID AndroidController::logFatal; + +// static +bool AndroidController::initLogging() +{ + QJniEnvironment env; + + log = env.findClass(ANDROID_LOG_CLASS); + if (log == nullptr) { + qCritical() << "Android log class" << ANDROID_LOG_CLASS << "not found"; + return false; + } + + auto logMethodSignature = "(Ljava/lang/String;Ljava/lang/String;)V"; + + logDebug = env.findStaticMethod(log, "d", logMethodSignature); + if (logDebug == nullptr) { + qCritical() << "Android debug log method not found"; + return false; + } + + logInfo = env.findStaticMethod(log, "i", logMethodSignature); + if (logInfo == nullptr) { + qCritical() << "Android info log method not found"; + return false; + } + + logWarning = env.findStaticMethod(log, "w", logMethodSignature); + if (logWarning == nullptr) { + qCritical() << "Android warning log method not found"; + return false; + } + + logError = env.findStaticMethod(log, "e", logMethodSignature); + if (logError == nullptr) { + qCritical() << "Android error log method not found"; + return false; + } + + logFatal = env.findStaticMethod(log, "f", logMethodSignature); + if (logFatal == nullptr) { + qCritical() << "Android fatal log method not found"; + return false; + } + + qInstallMessageHandler(messageHandler); + return true; +} + +// static +void AndroidController::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) +{ + jmethodID logMethod = logDebug; + switch (type) { + case QtDebugMsg: + logMethod = logDebug; + break; + case QtInfoMsg: + logMethod = logInfo; + break; + case QtWarningMsg: + logMethod = logWarning; + break; + case QtCriticalMsg: + logMethod = logError; + break; + case QtFatalMsg: + logMethod = logFatal; + break; + } + QString formattedMessage = qFormatLogMessage(type, context, message); + QJniObject::callStaticMethod(log, logMethod, + QJniObject::fromString(TAG).object(), + QJniObject::fromString(formattedMessage).object()); +} + void AndroidController::qtAndroidControllerInitialized() { callActivityMethod("qtAndroidControllerInitialized", "()V"); diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 481f4b49..48044544 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -34,6 +34,12 @@ public: void saveFile(const QString &fileName, const QString &data); QString openFile(const QString &filter); void startQrReaderActivity(); + void setSaveLogs(bool enabled); + void exportLogsFile(const QString &fileName); + void clearLogs(); + + static bool initLogging(); + static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); signals: void connectionStateChanged(Vpn::ConnectionState state); @@ -53,6 +59,13 @@ signals: private: bool isWaitingStatus = true; + static jclass log; + static jmethodID logDebug; + static jmethodID logInfo; + static jmethodID logWarning; + static jmethodID logError; + static jmethodID logFatal; + void qtAndroidControllerInitialized(); static Vpn::ConnectionState convertState(ConnectionState state); @@ -72,8 +85,7 @@ private: static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data); template - static auto callActivityMethod(const char *methodName, const char *signature, - const std::function &defValue, Args &&...args); + static auto callActivityMethod(const char *methodName, const char *signature, Args &&...args); template static void callActivityMethod(const char *methodName, const char *signature, Args &&...args); }; diff --git a/client/settings.cpp b/client/settings.cpp index f3fb57a7..475c52e7 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -216,6 +216,7 @@ QString Settings::nextAvailableServerName() const void Settings::setSaveLogs(bool enabled) { setValue("Conf/saveLogs", enabled); +#ifndef Q_OS_ANDROID if (!isSaveLogs()) { Logger::deInit(); } else { @@ -223,7 +224,8 @@ void Settings::setSaveLogs(bool enabled) qWarning() << "Initialization of debug subsystem failed"; } } - emit saveLogsChanged(); +#endif + emit saveLogsChanged(enabled); } QString Settings::routeModeString(RouteMode mode) const diff --git a/client/settings.h b/client/settings.h index 50a28dc4..ed302653 100644 --- a/client/settings.h +++ b/client/settings.h @@ -190,7 +190,7 @@ public: void clearSettings(); signals: - void saveLogsChanged(); + void saveLogsChanged(bool enabled); private: QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index f7345608..8ac4cca0 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -8,6 +8,7 @@ #include "version.h" #ifdef Q_OS_ANDROID #include "platforms/android/android_utils.h" + #include "platforms/android/android_controller.h" #include #endif @@ -91,13 +92,21 @@ void SettingsController::openLogsFolder() void SettingsController::exportLogsFile(const QString &fileName) { +#ifdef Q_OS_ANDROID + AndroidController::instance()->exportLogsFile(fileName); +#else SystemController::saveFile(fileName, Logger::getLogFile()); +#endif } void SettingsController::clearLogs() { +#ifdef Q_OS_ANDROID + AndroidController::instance()->clearLogs(); +#else Logger::clearLogs(); Logger::clearServiceLogs(); +#endif } void SettingsController::backupAppConfig(const QString &fileName) From 47b03f2bf4b5dfcb05d1bf4d3e0c3d8cdec3a799 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 20 Jan 2024 08:44:48 -0500 Subject: [PATCH 41/65] PSK paramater is not mandatory --- client/ui/controllers/importController.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 734c17c3..59aab046 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -243,10 +243,6 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::psk_key] = configMap.value("PresharedKey"); } else if (!configMap.value("PreSharedKey").isEmpty()) { lastConfig[config_key::psk_key] = configMap.value("PreSharedKey"); - } else { - qDebug() << "Failed to import profile"; - emit importErrorOccurred(errorString(ErrorCode::ImportInvalidConfigError)); - return QJsonObject(); } lastConfig[config_key::server_pub_key] = configMap.value("PublicKey"); From 2b61c48303d97917c1101c8d4df99013b3a8dd70 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 18:16:45 +0300 Subject: [PATCH 42/65] Hide "QR-code" button from config import menu for Android devices without camera --- client/android/src/org/amnezia/vpn/AmneziaActivity.kt | 4 ++++ client/platforms/android/android_controller.cpp | 5 +++++ client/platforms/android/android_controller.h | 1 + client/ui/controllers/settingsController.cpp | 11 +++++++++++ client/ui/controllers/settingsController.h | 2 ++ client/ui/qml/Pages2/PageSetupWizardConfigSource.qml | 4 ++-- 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 2ed2c7f7..3583a383 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.Intent.EXTRA_MIME_TYPES import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY import android.content.ServiceConnection +import android.content.pm.PackageManager import android.net.Uri import android.net.VpnService import android.os.Bundle @@ -418,6 +419,9 @@ class AmneziaActivity : QtActivity() { Log.v(TAG, "Set notification text") } + @Suppress("unused") + fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) + @Suppress("unused") fun startQrCodeReader() { Log.v(TAG, "Start camera") diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 5d442b81..767004fc 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -191,6 +191,11 @@ void AndroidController::setNotificationText(const QString &title, const QString (jint) timerSec); } +bool AndroidController::isCameraPresent() +{ + return callActivityMethod("isCameraPresent", "()Z"); +} + void AndroidController::startQrReaderActivity() { callActivityMethod("startQrCodeReader", "()V"); diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 48044544..86b117f7 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -33,6 +33,7 @@ public: void setNotificationText(const QString &title, const QString &message, int timerSec); void saveFile(const QString &fileName, const QString &data); QString openFile(const QString &filter); + bool isCameraPresent(); void startQrReaderActivity(); void setSaveLogs(bool enabled); void exportLogsFile(const QString &fileName); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 8ac4cca0..c7306a10 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -206,3 +206,14 @@ void SettingsController::toggleScreenshotsEnabled(bool enable) }); #endif } + +bool SettingsController::isCameraPresent() +{ +#if defined Q_OS_IOS + return true; +#elif defined Q_OS_ANDROID + return AndroidController::instance()->isCameraPresent(); +#else + return false; +#endif +} diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index a6cbc587..5b30b095 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -59,6 +59,8 @@ public slots: bool isScreenshotsEnabled(); void toggleScreenshotsEnabled(bool enable); + bool isCameraPresent(); + signals: void primaryDnsChanged(); void secondaryDnsChanged(); diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 07189eb7..519aa6cc 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -90,7 +90,7 @@ It's okay as long as it's from someone you trust.") LabelWithButtonType { Layout.fillWidth: true - visible: GC.isMobile() + visible: SettingsController.isCameraPresent() text: qsTr("QR-code") rightImageSource: "qrc:/images/controls/chevron-right.svg" @@ -105,7 +105,7 @@ It's okay as long as it's from someone you trust.") } DividerType { - visible: GC.isMobile() + visible: SettingsController.isCameraPresent() } LabelWithButtonType { From 9b7914538fb5b7a2e387334ab4a82ce8f0aa1b41 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 18:42:21 +0300 Subject: [PATCH 43/65] set wg PresharedKey parameter as optional --- .../org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt index 1e74e6ff..76ccd905 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt @@ -11,7 +11,7 @@ open class WireguardConfig protected constructor( val endpoint: InetEndpoint, val persistentKeepalive: Int, val publicKeyHex: String, - val preSharedKeyHex: String, + val preSharedKeyHex: String?, val privateKeyHex: String ) : ProtocolConfig(protocolConfigBuilder) { @@ -43,7 +43,8 @@ open class WireguardConfig protected constructor( appendLine("endpoint=$endpoint") if (persistentKeepalive != 0) appendLine("persistent_keepalive_interval=$persistentKeepalive") - appendLine("preshared_key=$preSharedKeyHex") + if (preSharedKeyHex != null) + appendLine("preshared_key=$preSharedKeyHex") } open class Builder : ProtocolConfig.Builder(true) { @@ -56,7 +57,7 @@ open class WireguardConfig protected constructor( internal lateinit var publicKeyHex: String private set - internal lateinit var preSharedKeyHex: String + internal var preSharedKeyHex: String? = null private set internal lateinit var privateKeyHex: String From 9dfc95bac0caaf188d9aaa93da17d0bd425ee217 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 20 Jan 2024 23:28:20 +0700 Subject: [PATCH 44/65] fixed easy setup default container selection --- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index c393d0ce..f16eec40 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -117,12 +117,14 @@ PageType { } } } + } - Component.onCompleted: { - if (index === containers.currentIndex) { - card.checked = true - card.clicked() - } + Component.onCompleted: { + var item = containers.itemAtIndex(containers.currentIndex) + if (item !== null) { + var button = item.children[0].children[0] + button.checked = true + button.clicked() } } } From 0103c1722e8d29a72cd435d79a6882f5bf293e52 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 21:37:52 +0300 Subject: [PATCH 45/65] Fix wg address parameter parsing --- .../kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index e1f96fa3..87d5e249 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -99,7 +99,10 @@ open class Wireguard : Protocol() { } protected fun WireguardConfig.Builder.configWireguard(configData: Map) { - configData["Address"]?.let { addAddress(InetNetwork.parse(it)) } + configData["Address"]?.split(",")?.map { address -> + InetNetwork.parse(address.trim()) + }?.forEach(::addAddress) + configData["DNS"]?.split(",")?.map { dns -> parseInetAddress(dns.trim()) }?.forEach(::addDnsServer) From adeff3efb975d5529263f7ee865f227cceb432e0 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sat, 20 Jan 2024 18:29:38 +0000 Subject: [PATCH 46/65] Version bump 4.2.1.0 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c149b3ea..06e92993 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.2.0.1 +project(${PROJECT} VERSION 4.2.1.0 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 39) +set(APP_ANDROID_VERSION_CODE 42) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") From 802b708232204a6a68ac201b4dae98aaaae49f29 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 20 Jan 2024 21:49:37 +0300 Subject: [PATCH 47/65] Enable AAB build for all branches --- .github/workflows/deploy.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cd52e69d..9f556627 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -283,7 +283,6 @@ jobs: ANDROID_BUILD_PLATFORM: android-34 QT_VERSION: 6.6.1 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' - BUILD_AAB: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }} steps: - name: 'Install desktop Qt' @@ -382,7 +381,7 @@ jobs: ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} shell: bash - run: ./deploy/build_android.sh ${{ env.BUILD_AAB == 'true' && '--aab' || '' }} --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} + run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} - name: 'Upload x86_64 apk' uses: actions/upload-artifact@v4 @@ -417,7 +416,6 @@ jobs: retention-days: 7 - name: 'Upload aab' - if: ${{ env.BUILD_AAB == 'true' }} uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android From 19a60ea85698cbd2b305ab1e3076d05ebb9439d7 Mon Sep 17 00:00:00 2001 From: KsZnak Date: Sat, 20 Jan 2024 22:13:00 +0200 Subject: [PATCH 48/65] Update README.md del signal link --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 21b01865..88ec574c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English) [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian) -[https://signal.group/...](https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh) - Signal channel ## Tech From 8495124bc8c0e4ced986577cf3980e779f7fd4a0 Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Sun, 21 Jan 2024 17:48:06 +0300 Subject: [PATCH 49/65] Remove useless 'Open folder with logs' button (iOS) --- client/ui/qml/Pages2/PageSettingsLogging.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 840c41d4..d00bdeb0 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -66,7 +66,8 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: root.width / 3 + Layout.preferredWidth: Qt.platform.os === "ios"? 0 : root.width / 3 + visible: Qt.platform.os !== "ios" ImageButtonType { Layout.alignment: Qt.AlignHCenter @@ -90,7 +91,7 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: root.width / 3 + Layout.preferredWidth: root.width / ( Qt.platform.os === "ios" ? 2 : 3 ) ImageButtonType { Layout.alignment: Qt.AlignHCenter @@ -131,7 +132,7 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: root.width / 3 + Layout.preferredWidth: root.width / ( Qt.platform.os === "ios" ? 2 : 3 ) ImageButtonType { Layout.alignment: Qt.AlignHCenter From 401784414a63e17c473f1d47fed1562b9ce872d0 Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Sun, 21 Jan 2024 19:05:24 +0300 Subject: [PATCH 50/65] Fix: 'OpenVPN' is recorded to the log instead of 'WireGuard' --- client/platforms/ios/ios_controller.mm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 79eddefd..c29e2114 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -352,6 +352,15 @@ bool IosController::startWireGuard(const QString &config) void IosController::startTunnel() { + NSString *protocolName = @"Unknown"; + + NETunnelProviderProtocol *tunnelProtocol = (NETunnelProviderProtocol *)m_currentTunnel.protocolConfiguration; + if (tunnelProtocol.providerConfiguration[@"wireguard"] != nil) { + protocolName = @"WireGuard"; + } else if (tunnelProtocol.providerConfiguration[@"ovpn"] != nil) { + protocolName = @"OpenVPN"; + } + m_rxBytes = 0; m_txBytes = 0; @@ -373,7 +382,7 @@ void IosController::startTunnel() [m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) { if (loadError) { - qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Load Error" << loadError.localizedDescription.UTF8String; + qDebug().nospace() << "IosController::start" << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String; emit connectionStateChanged(Vpn::ConnectionState::Error); return; } @@ -401,11 +410,11 @@ void IosController::startTunnel() BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError]; if (!started || startError) { - qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Start Error" + qDebug().nospace() << "IosController::start" << protocolName << " : Connect " << protocolName << " Tunnel Start Error" << (startError ? startError.localizedDescription.UTF8String : ""); emit connectionStateChanged(Vpn::ConnectionState::Error); } else { - qDebug() << "IosController::startOpenVPN : Starting the tunnel succeeded"; + qDebug().nospace() << "IosController::start" << protocolName << " : Starting the tunnel succeeded"; } }]; }); From 2e4908c557453998355493752f1085a55b13b56d Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Sun, 21 Jan 2024 20:41:06 +0300 Subject: [PATCH 51/65] Records detailed disconnect error info to log --- client/platforms/ios/ios_controller.mm | 47 ++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index c29e2114..c27badda 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -249,8 +249,51 @@ void IosController::vpnStatusDidChange(void *pNotification) NETunnelProviderSession *session = (NETunnelProviderSession *)pNotification; if (session /* && session == TunnelManager.session */ ) { - qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session; - emit connectionStateChanged(iosStatusToState(session.status)); + qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session; + + if (session.status == NEVPNStatusDisconnected) { + if (@available(iOS 16.0, *)) { + [session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) { + if (error != nil) { + qDebug() << "Disconnect error" << error.domain << error.localizedDescription; + + if ([error.domain isEqualToString:NEVPNConnectionErrorDomain]) { + switch (error.code) { + case 1: + break; + default: + break; + } + } + + NSError *underlyingError = error.userInfo[@"NSUnderlyingError"]; + if (underlyingError != nil) { + qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.localizedDescription; + + if ([underlyingError.domain isEqualToString:@"NEAgentErrorDomain"]) { + switch (underlyingError.code) { + case 1: + qDebug() << "Disconnect underlying error" << "General. Use sysdiagnose."; + break; + case 2: + qDebug() << "Disconnect underlying error" << "Plug-in unavailable. Use sysdiagnose."; + break; + default: + qDebug() << "Disconnect underlying error" << "Unknown code. Use sysdiagnose."; + break; + } + } + } + } else { + qDebug() << "Disconnect error is absent"; + } + }]; + } else { + qDebug() << "Disconnect error is unavailable on iOS < 16.0"; + } + } + + emit connectionStateChanged(iosStatusToState(session.status)); } } From 0e8f85057d86b26ad81f61e755360ffa301c2a54 Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Sun, 21 Jan 2024 21:15:34 +0300 Subject: [PATCH 52/65] Remove distracting 'stale focus object' records from log --- client/logger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/logger.cpp b/client/logger.cpp index 46ce53f0..75c2d652 100644 --- a/client/logger.cpp +++ b/client/logger.cpp @@ -29,7 +29,7 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons } // Skip annoying messages from Qt - if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font")) { + if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font") || msg.startsWith("stale focus object")) { return; } From ec4574bfcfc8c2fd2a08e8895562962158fbf55f Mon Sep 17 00:00:00 2001 From: KsZnak Date: Mon, 22 Jan 2024 01:52:14 +0200 Subject: [PATCH 53/65] Update amneziavpn_ru.ts --- client/translations/amneziavpn_ru.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts index ed580ec0..ddd7b0e9 100644 --- a/client/translations/amneziavpn_ru.ts +++ b/client/translations/amneziavpn_ru.ts @@ -416,7 +416,7 @@ Already installed containers were found on the server. All installed containers OpenVPN settings - Настройки OpenVPN + OpenVPN настройки @@ -1192,7 +1192,7 @@ Already installed containers were found on the server. All installed containers If AmneziaDNS is not used or installed - Эти серверы будут использоваться, если не включен AmneziaDNS + Эти адреса будут использоваться, если не включен AmneziaDNS @@ -2047,7 +2047,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Rename - + Переименовать @@ -2062,12 +2062,12 @@ and will not be shared or disclosed to the Amnezia or any third parties Revoke - + Отозвать Revoke the config for a user - %1? - + Отозвать доступ для пользователя - %1? From 4d88eb8e79c583ea93ff4ad9be96e5118b4a8c7a Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Tue, 23 Jan 2024 18:41:33 +0300 Subject: [PATCH 54/65] Try to expand 'internal error' --- client/platforms/ios/ios_controller.mm | 62 ++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index c27badda..2ff65775 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -255,20 +255,76 @@ void IosController::vpnStatusDidChange(void *pNotification) if (@available(iOS 16.0, *)) { [session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { - qDebug() << "Disconnect error" << error.domain << error.localizedDescription; + qDebug() << "Disconnect error" << error.domain << error.code << error.localizedDescription; if ([error.domain isEqualToString:NEVPNConnectionErrorDomain]) { switch (error.code) { - case 1: + case NEVPNConnectionErrorOverslept: + qDebug() << "Disconnect error info" << "The VPN connection was terminated because the system slept for an extended period of time."; + break; + case NEVPNConnectionErrorNoNetworkAvailable: + qDebug() << "Disconnect error info" << "The VPN connection could not be established because the system is not connected to a network."; + break; + case NEVPNConnectionErrorUnrecoverableNetworkChange: + qDebug() << "Disconnect error info" << "The VPN connection was terminated because the network conditions changed in such a way that the VPN connection could not be maintained."; + break; + case NEVPNConnectionErrorConfigurationFailed: + qDebug() << "Disconnect error info" << "The VPN connection could not be established because the configuration is invalid. "; + break; + case NEVPNConnectionErrorServerAddressResolutionFailed: + qDebug() << "Disconnect error info" << "The address of the VPN server could not be determined."; + break; + case NEVPNConnectionErrorServerNotResponding: + qDebug() << "Disconnect error info" << "Network communication with the VPN server has failed."; + break; + case NEVPNConnectionErrorServerDead: + qDebug() << "Disconnect error info" << "The VPN server is no longer functioning."; + break; + case NEVPNConnectionErrorAuthenticationFailed: + qDebug() << "Disconnect error info" << "The user credentials were rejected by the VPN server."; + break; + case NEVPNConnectionErrorClientCertificateInvalid: + qDebug() << "Disconnect error info" << "The client certificate is invalid."; + break; + case NEVPNConnectionErrorClientCertificateNotYetValid: + qDebug() << "Disconnect error info" << "The client certificate will not be valid until some future point in time."; + break; + case NEVPNConnectionErrorClientCertificateExpired: + qDebug() << "Disconnect error info" << "The validity period of the client certificate has passed."; + break; + case NEVPNConnectionErrorPluginFailed: + qDebug() << "Disconnect error info" << "The VPN plugin died unexpectedly."; + break; + case NEVPNConnectionErrorConfigurationNotFound: + qDebug() << "Disconnect error info" << "The VPN configuration could not be found."; + break; + case NEVPNConnectionErrorPluginDisabled: + qDebug() << "Disconnect error info" << "The VPN plugin could not be found or needed to be updated."; + break; + case NEVPNConnectionErrorNegotiationFailed: + qDebug() << "Disconnect error info" << "The VPN protocol negotiation failed."; + break; + case NEVPNConnectionErrorServerDisconnected: + qDebug() << "Disconnect error info" << "The VPN server terminated the connection."; + break; + case NEVPNConnectionErrorServerCertificateInvalid: + qDebug() << "Disconnect error info" << "The server certificate is invalid."; + break; + case NEVPNConnectionErrorServerCertificateNotYetValid: + qDebug() << "Disconnect error info" << "The server certificate will not be valid until some future point in time."; + break; + case NEVPNConnectionErrorServerCertificateExpired: + qDebug() << "Disconnect error info" << "The validity period of the server certificate has passed."; break; default: + qDebug() << "Disconnect error info" << "Unknown code."; break; } } NSError *underlyingError = error.userInfo[@"NSUnderlyingError"]; if (underlyingError != nil) { - qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.localizedDescription; + qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.code << underlyingError.localizedDescription; if ([underlyingError.domain isEqualToString:@"NEAgentErrorDomain"]) { switch (underlyingError.code) { From f55bd5e1a11a0d17c9116c7a9d5e652bf1815837 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 23 Jan 2024 23:56:16 +0700 Subject: [PATCH 55/65] fixed first container selection on HomeContainersListView after server cleanup --- client/ui/qml/Components/HomeContainersListView.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index 2c07ca65..3043e97f 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -30,7 +30,9 @@ ListView { target: ServersModel function onCurrentlyProcessedServerIndexChanged() { - menuContent.checkCurrentItem() + if (ContainersModel.getDefaultContainer()) { + menuContent.checkCurrentItem() + } } } @@ -76,9 +78,8 @@ ListView { } if (checked) { - ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index)) - containersDropDown.menuVisible = false + ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index)) } else { if (!isSupported && isInstalled) { PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform")) From 0160b0f9dcd3c8acb078569439b739073afe1c51 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 23 Jan 2024 23:57:14 +0700 Subject: [PATCH 56/65] editServer now does not update the whole model, but only the modified element --- client/ui/models/servers_model.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 1922e188..ad9b0059 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -251,10 +251,9 @@ void ServersModel::addServer(const QJsonObject &server) void ServersModel::editServer(const QJsonObject &server) { - beginResetModel(); m_settings->editServer(m_currentlyProcessedServerIndex, server); - m_servers = m_settings->serversArray(); - endResetModel(); + m_servers.replace(m_currentlyProcessedServerIndex, m_settings->serversArray().at(m_currentlyProcessedServerIndex)); + emit dataChanged(index(m_currentlyProcessedServerIndex, 0), index(m_currentlyProcessedServerIndex, 0)); updateContainersModel(); } From 83ec073734da436564064ff3003d9a20b95e87a9 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 22 Jan 2024 16:14:59 +0700 Subject: [PATCH 57/65] fixed adding admin user to client management --- client/vpnconnection.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 75483d89..d3588fe4 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -253,10 +253,13 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser m_settings->setProtocolConfig(serverIndex, container, proto, protoObject); } - QEventLoop wait; - emit m_configurator->newVpnConfigCreated(clientId, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), container, credentials); - QObject::connect(m_configurator.get(), &VpnConfigurator::clientModelUpdated, &wait, &QEventLoop::quit); - wait.exec(); + if ((container != DockerContainer::Cloak && container != DockerContainer::ShadowSocks) || + ((container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) && proto == Proto::OpenVpn)) { + QEventLoop wait; + emit m_configurator->newVpnConfigCreated(clientId, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), container, credentials); + QObject::connect(m_configurator.get(), &VpnConfigurator::clientModelUpdated, &wait, &QEventLoop::quit); + wait.exec(); + } } return configData; From 885e22be7c1a9bdd3c8da82931f57f2a5e5cc82c Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 23 Jan 2024 15:16:28 -0500 Subject: [PATCH 58/65] Fix autostart for Linux Desktop --- client/ui/qautostart.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ui/qautostart.cpp b/client/ui/qautostart.cpp index a7f49b2d..b0165dc4 100644 --- a/client/ui/qautostart.cpp +++ b/client/ui/qautostart.cpp @@ -124,8 +124,13 @@ void Autostart::setAutostart(bool autostart) { if (file.open(QIODevice::ReadWrite)) { QTextStream stream(&file); stream << "[Desktop Entry]" << Qt::endl; - stream << "Exec=" << appPath() << Qt::endl; + stream << "Exec=AmneziaVPN" << Qt::endl; stream << "Type=Application" << Qt::endl; + stream << "Name=AmneziaVPN" << Qt::endl; + stream << "Comment=Client of your self-hosted VPN" << Qt::endl; + stream << "Icon=/usr/share/pixmaps/AmneziaVPN.png" << Qt::endl; + stream << "Categories=Network;Qt;Security;" << Qt::endl; + stream << "Terminal=false" << Qt::endl; } } } From b3eda4106db64d7a384ca35888ddb00dbeef0e7c Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Tue, 23 Jan 2024 23:41:08 +0300 Subject: [PATCH 59/65] Fix: Share view is not showing on iPadOS --- client/platforms/ios/MobileUtils.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/client/platforms/ios/MobileUtils.mm b/client/platforms/ios/MobileUtils.mm index fbf26ffd..58faad88 100644 --- a/client/platforms/ios/MobileUtils.mm +++ b/client/platforms/ios/MobileUtils.mm @@ -43,6 +43,7 @@ bool MobileUtils::shareText(const QStringList& filesToSend) { UIPopoverPresentationController *popController = activityController.popoverPresentationController; if (popController) { popController.sourceView = qtController.view; + popController.sourceRect = CGRectMake(100, 100, 100, 100); } QEventLoop wait; From 140b5c429201fc1848666634a21d74279929a66b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 24 Jan 2024 12:27:11 +0700 Subject: [PATCH 60/65] random ports are now used for easy setup --- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index f16eec40..95951507 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -112,7 +112,7 @@ PageType { var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer) containers.dockerContainer = dockerContainer - containers.containerDefaultPort = ProtocolProps.defaultPort(defaultContainerProto) + containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto) containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto) } } From 6f02d4eb62b34a6045599c6b88ed43ae176cad11 Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 24 Jan 2024 16:34:37 +0300 Subject: [PATCH 61/65] Remove useless 'Open folder with logs' button for Adnroid too --- client/ui/qml/Pages2/PageSettingsLogging.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index d00bdeb0..b302bffc 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -66,8 +66,8 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: Qt.platform.os === "ios"? 0 : root.width / 3 - visible: Qt.platform.os !== "ios" + Layout.preferredWidth: GC.isMobile() ? 0 : root.width / 3 + visible: !GC.isMobile() ImageButtonType { Layout.alignment: Qt.AlignHCenter @@ -91,7 +91,7 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: root.width / ( Qt.platform.os === "ios" ? 2 : 3 ) + Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 ) ImageButtonType { Layout.alignment: Qt.AlignHCenter @@ -132,7 +132,7 @@ PageType { ColumnLayout { Layout.alignment: Qt.AlignBaseline - Layout.preferredWidth: root.width / ( Qt.platform.os === "ios" ? 2 : 3 ) + Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 ) ImageButtonType { Layout.alignment: Qt.AlignHCenter From b68c9cc1457a86159a72394227534adc14eebf3c Mon Sep 17 00:00:00 2001 From: albexk Date: Wed, 24 Jan 2024 17:00:48 +0300 Subject: [PATCH 62/65] Fix the double extension of the config file name: 'amnezia_config.vpn.vpn' --- client/ui/qml/Components/ShareConnectionDrawer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index e354e951..45ef84a6 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -25,7 +25,7 @@ DrawerType { property string configExtension: ".vpn" property string configCaption: qsTr("Save AmneziaVPN config") - property string configFileName: "amnezia_config.vpn" + property string configFileName: "amnezia_config" width: parent.width height: parent.height * 0.9 From 3afbc248b14164d68570a78dca7288d9762f1d75 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 27 Jan 2024 16:55:05 +0300 Subject: [PATCH 63/65] Refactor split-tunneling: separate site addresses from routes --- .../android/awg/src/main/kotlin/AwgConfig.kt | 2 +- .../amnezia/vpn/protocol/openvpn/OpenVpn.kt | 13 +--- .../vpn/protocol/openvpn/OpenVpnConfig.kt | 2 +- .../protocolApi/src/main/kotlin/Protocol.kt | 44 +++--------- .../src/main/kotlin/ProtocolConfig.kt | 67 +++++++++++++++++-- .../vpn/protocol/wireguard/WireguardConfig.kt | 2 +- 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/client/android/awg/src/main/kotlin/AwgConfig.kt b/client/android/awg/src/main/kotlin/AwgConfig.kt index 372747f2..014c6e0a 100644 --- a/client/android/awg/src/main/kotlin/AwgConfig.kt +++ b/client/android/awg/src/main/kotlin/AwgConfig.kt @@ -99,7 +99,7 @@ class AwgConfig private constructor( fun setH3(h3: Long) = apply { this.h3 = h3 } fun setH4(h4: Long) = apply { this.h4 = h4 } - override fun build(): AwgConfig = AwgConfig(this) + override fun build(): AwgConfig = configBuild().run { AwgConfig(this@Builder) } } companion object { diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index 34069a0d..34f2934b 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -2,7 +2,6 @@ package org.amnezia.vpn.protocol.openvpn import android.content.Context import android.net.VpnService.Builder -import android.os.Build import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -14,7 +13,6 @@ import org.amnezia.vpn.protocol.ProtocolState import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.VpnStartException -import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.getLocalNetworks import org.json.JSONObject @@ -79,16 +77,7 @@ open class OpenVpn : Protocol() { if (evalConfig.error) { throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}") } - configBuilder.apply { - // fix for split tunneling - // The exclude split tunneling OpenVpn configuration does not contain a default route. - // It is required for split tunneling in newer versions of Android. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - addRoute(InetNetwork("0.0.0.0", 0)) - addRoute(InetNetwork("::", 0)) - } - configSplitTunneling(config) - } + configBuilder.configSplitTunneling(config) scope.launch { val status = client.connect() diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnConfig.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnConfig.kt index 36d8d93b..9554f978 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnConfig.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnConfig.kt @@ -11,7 +11,7 @@ class OpenVpnConfig private constructor( class Builder : ProtocolConfig.Builder(false) { override var mtu: Int = OPENVPN_DEFAULT_MTU - override fun build(): OpenVpnConfig = OpenVpnConfig(this) + override fun build(): OpenVpnConfig = configBuild().run { OpenVpnConfig(this@Builder) } } companion object { diff --git a/client/android/protocolApi/src/main/kotlin/Protocol.kt b/client/android/protocolApi/src/main/kotlin/Protocol.kt index b2c52e9a..b729f9f7 100644 --- a/client/android/protocolApi/src/main/kotlin/Protocol.kt +++ b/client/android/protocolApi/src/main/kotlin/Protocol.kt @@ -14,8 +14,6 @@ import java.util.zip.ZipFile import kotlinx.coroutines.flow.MutableStateFlow import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.net.InetNetwork -import org.amnezia.vpn.util.net.IpRange -import org.amnezia.vpn.util.net.IpRangeSet import org.json.JSONObject private const val TAG = "Protocol" @@ -53,40 +51,16 @@ abstract class Protocol { val splitTunnelType = config.optInt("splitTunnelType") if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return val splitTunnelSites = config.getJSONArray("splitTunnelSites") - when (splitTunnelType) { - SPLIT_TUNNEL_INCLUDE -> { - // remove default routes, if any - removeRoute(InetNetwork("0.0.0.0", 0)) - removeRoute(InetNetwork("::", 0)) - // add routes from config - for (i in 0 until splitTunnelSites.length()) { - val address = InetNetwork.parse(splitTunnelSites.getString(i)) - addRoute(address) - } - } + val addressHandlerFunc = when (splitTunnelType) { + SPLIT_TUNNEL_INCLUDE -> ::includeAddress + SPLIT_TUNNEL_EXCLUDE -> ::excludeAddress - SPLIT_TUNNEL_EXCLUDE -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // exclude routes from config - for (i in 0 until splitTunnelSites.length()) { - val address = InetNetwork.parse(splitTunnelSites.getString(i)) - excludeRoute(address) - } - } else { - // For older versions of Android, build a list of subnets without excluded addresses - val ipRangeSet = IpRangeSet() - ipRangeSet.remove(IpRange("127.0.0.0", 8)) - for (i in 0 until splitTunnelSites.length()) { - val address = InetNetwork.parse(splitTunnelSites.getString(i)) - ipRangeSet.remove(IpRange(address)) - } - // remove default routes, if any - removeRoute(InetNetwork("0.0.0.0", 0)) - removeRoute(InetNetwork("::", 0)) - ipRangeSet.subnets().forEach(::addRoute) - addRoute(InetNetwork("2000::", 3)) - } - } + else -> throw BadConfigException("Unexpected value of the 'splitTunnelType' parameter: $splitTunnelType") + } + + for (i in 0 until splitTunnelSites.length()) { + val address = InetNetwork.parse(splitTunnelSites.getString(i)) + addressHandlerFunc(address) } } diff --git a/client/android/protocolApi/src/main/kotlin/ProtocolConfig.kt b/client/android/protocolApi/src/main/kotlin/ProtocolConfig.kt index a4d7683e..75ba1abf 100644 --- a/client/android/protocolApi/src/main/kotlin/ProtocolConfig.kt +++ b/client/android/protocolApi/src/main/kotlin/ProtocolConfig.kt @@ -5,6 +5,8 @@ import android.os.Build import androidx.annotation.RequiresApi import java.net.InetAddress import org.amnezia.vpn.util.net.InetNetwork +import org.amnezia.vpn.util.net.IpRange +import org.amnezia.vpn.util.net.IpRangeSet open class ProtocolConfig protected constructor( val addresses: Set, @@ -12,6 +14,8 @@ open class ProtocolConfig protected constructor( val searchDomain: String?, val routes: Set, val excludedRoutes: Set, + val includedAddresses: Set, + val excludedAddresses: Set, val excludedApplications: Set, val httpProxy: ProxyInfo?, val allowAllAF: Boolean, @@ -25,6 +29,8 @@ open class ProtocolConfig protected constructor( builder.searchDomain, builder.routes, builder.excludedRoutes, + builder.includedAddresses, + builder.excludedAddresses, builder.excludedApplications, builder.httpProxy, builder.allowAllAF, @@ -37,6 +43,8 @@ open class ProtocolConfig protected constructor( internal val dnsServers: MutableSet = hashSetOf() internal val routes: MutableSet = hashSetOf() internal val excludedRoutes: MutableSet = hashSetOf() + internal val includedAddresses: MutableSet = hashSetOf() + internal val excludedAddresses: MutableSet = hashSetOf() internal val excludedApplications: MutableSet = hashSetOf() internal var searchDomain: String? = null @@ -71,12 +79,15 @@ open class ProtocolConfig protected constructor( fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) } fun clearRoutes() = apply { this.routes.clear() } - @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route } - - @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun excludeRoutes(routes: Collection) = apply { this.excludedRoutes += routes } + fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr } + fun includeAddresses(addresses: Collection) = apply { this.includedAddresses += addresses } + + fun excludeAddress(addr: InetNetwork) = apply { this.excludedAddresses += addr } + fun excludeAddresses(addresses: Collection) = apply { this.excludedAddresses += addresses } + fun excludeApplication(application: String) = apply { this.excludedApplications += application } fun excludeApplications(applications: Collection) = apply { this.excludedApplications += applications } @@ -91,6 +102,48 @@ open class ProtocolConfig protected constructor( fun setMtu(mtu: Int) = apply { this.mtu = mtu } + private fun processSplitTunneling() { + if (includedAddresses.isNotEmpty() && excludedAddresses.isNotEmpty()) { + throw BadConfigException("Config contains addresses for inclusive and exclusive split tunneling at the same time") + } + + if (includedAddresses.isNotEmpty()) { + // remove default routes, if any + removeRoute(InetNetwork("0.0.0.0", 0)) + removeRoute(InetNetwork("::", 0)) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // for older versions of Android, add the default route to the excluded routes + // to correctly build the excluded subnets list later + excludeRoute(InetNetwork("0.0.0.0", 0)) + } + addRoutes(includedAddresses) + } else if (excludedAddresses.isNotEmpty()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // default routes are required for split tunneling in newer versions of Android + addRoute(InetNetwork("0.0.0.0", 0)) + addRoute(InetNetwork("::", 0)) + } + excludeRoutes(excludedAddresses) + } + } + + private fun processExcludedRoutes() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // for older versions of Android, build a list of subnets without excluded routes + // and add them to routes + val ipRangeSet = IpRangeSet() + ipRangeSet.remove(IpRange("127.0.0.0", 8)) + excludedRoutes.forEach { + ipRangeSet.remove(IpRange(it)) + } + // remove default routes, if any + removeRoute(InetNetwork("0.0.0.0", 0)) + removeRoute(InetNetwork("::", 0)) + ipRangeSet.subnets().forEach(::addRoute) + addRoute(InetNetwork("2000::", 3)) + } + } + private fun validate() { val errorMessage = StringBuilder() @@ -103,7 +156,13 @@ open class ProtocolConfig protected constructor( if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString()) } - open fun build(): ProtocolConfig = validate().run { ProtocolConfig(this@Builder) } + protected fun configBuild() { + processSplitTunneling() + processExcludedRoutes() + validate() + } + + open fun build(): ProtocolConfig = configBuild().run { ProtocolConfig(this@Builder) } } companion object { diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt index 76ccd905..0e303f0e 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt @@ -75,7 +75,7 @@ open class WireguardConfig protected constructor( fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex } - override fun build(): WireguardConfig = WireguardConfig(this) + override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) } } companion object { From cbd6755aa5b78eb52636612f0f5ef401ff5370d6 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 27 Jan 2024 17:30:56 +0300 Subject: [PATCH 64/65] Fix OpenVpn over Cloak --- client/android/cloak/src/main/kotlin/Cloak.kt | 10 ++++++++++ .../kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt | 3 +++ .../org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt | 4 +--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/android/cloak/src/main/kotlin/Cloak.kt b/client/android/cloak/src/main/kotlin/Cloak.kt index 5a549130..651e353b 100644 --- a/client/android/cloak/src/main/kotlin/Cloak.kt +++ b/client/android/cloak/src/main/kotlin/Cloak.kt @@ -3,6 +3,9 @@ package org.amnezia.vpn.protocol.cloak import android.util.Base64 import net.openvpn.ovpn3.ClientAPI_Config import org.amnezia.vpn.protocol.openvpn.OpenVpn +import org.amnezia.vpn.protocol.openvpn.OpenVpnConfig +import org.amnezia.vpn.util.net.InetNetwork +import org.amnezia.vpn.util.net.parseInetAddress import org.json.JSONObject /** @@ -51,6 +54,13 @@ class Cloak : OpenVpn() { return openVpnConfig } + override fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) { + // exclude remote server ip from vpn routes + val remoteServer = config.getString("hostName") + val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer)) + configBuilder.excludeRoute(remoteServerAddress) + } + private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject { cloakConfigJson.put("NumConn", 1) cloakConfigJson.put("ProxyMethod", "openvpn") diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index 34f2934b..9e1c62cc 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -77,6 +77,7 @@ open class OpenVpn : Protocol() { if (evalConfig.error) { throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}") } + configPluggableTransport(configBuilder, config) configBuilder.configSplitTunneling(config) scope.launch { @@ -111,6 +112,8 @@ open class OpenVpn : Protocol() { return openVpnConfig } + protected open fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {} + private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder -> val openVpnConfig = configBuilder.build() buildVpnInterface(openVpnConfig, vpnBuilder) diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt index f489c980..4f0f1796 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpnClient.kt @@ -91,9 +91,7 @@ class OpenVpnClient( // metric is optional and should be ignored if < 0 override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - configBuilder.excludeRoute(InetNetwork(address, prefix_length)) - } + configBuilder.excludeRoute(InetNetwork(address, prefix_length)) return true } From 0b6dc5bcfc6a4246dde5e7929ca2e43551c98711 Mon Sep 17 00:00:00 2001 From: albexk Date: Sat, 27 Jan 2024 17:34:57 +0300 Subject: [PATCH 65/65] Add unbinding and destroying vpn service after disconnection --- client/android/src/org/amnezia/vpn/AmneziaActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 3583a383..9a813626 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -64,6 +64,7 @@ class AmneziaActivity : QtActivity() { ServiceEvent.DISCONNECTED -> { QtAndroidController.onVpnDisconnected() + doUnbindService() } ServiceEvent.RECONNECTING -> {