Compare commits

..

19 Commits

Author SHA1 Message Date
Vladyslav Miachkov
dad2dc0ab0 Hide error on clear server button 2024-04-06 23:07:46 +03:00
Nethius
a22c08a41d added prohibition of using "dangerous" options on the server management page, when the connection is active (#726) 2024-04-06 11:42:41 -07:00
Nethius
10ea9b418a supported container on connection (#736) 2024-04-06 11:42:17 -07:00
Nethius
e39efb1d68 app split tunneling search field (#727) 2024-04-06 08:29:51 -07:00
pokamest
7db84122f9 Merge pull request #737 from amnezia-vpn/bugfix/api-server-rename
fixed api server rename
2024-04-06 04:38:07 -07:00
vladimir.kuznetsov
84ad167ab4 fixed api server rename 2024-04-05 22:00:23 +05:00
isamnezia
ed7e217a6b Add required privacy manifest files (#731)
Add required privacy manifest files
2024-04-05 17:03:30 +01:00
pokamest
c1b0d4a4a7 Merge pull request #735 from amnezia-vpn/fix/andoird-open-config
Fix opening configs
2024-04-05 07:53:23 -07:00
albexk
2f84e24353 Fix opening configs 2024-04-05 14:06:40 +03:00
Mykola Baibuz
f73586185b Add Ukrainian translation (#722)
Add Ukrainian translation
2024-04-04 19:25:39 +01:00
pokamest
653ffb9a68 Merge pull request #728 from amnezia-vpn/bugfix/fix-macos-tray-icon-color
[macOS] Fix tray icon color states
2024-04-04 05:53:05 -07:00
pokamest
fe8c2d157a Merge pull request #732 from amnezia-vpn/bugfix/fix-inverted-switches
Fix inverted switches
2024-04-04 05:47:20 -07:00
pokamest
86367a1276 Merge pull request #725 from amnezia-vpn/bugfix/server-header-on-page-home
fixed the display of server name on the home page
2024-04-04 03:31:10 -07:00
Vladyslav Miachkov
b0fcf92ada Fix inverted switches 2024-04-04 10:40:03 +03:00
pokamest
283b6ebf81 Merge pull request #730 from amnezia-vpn/fix/ovpn-cloak-ios
Fix OpenVPN over Cloak (iOS)
2024-04-03 16:30:06 -07:00
Igor Sorokin
d0a7fc5116 Fix OpenVPN over Cloak (iOS) 2024-04-04 01:56:27 +03:00
Vladyslav Miachkov
9851aacba7 [macOS] Fix tray icon color states 2024-04-03 22:41:26 +03:00
vladimir.kuznetsov
51f9fb9e0a fixed the display of server name on the home page 2024-04-03 13:02:31 +05:00
KsZnak
0325761f3e Update amneziavpn_ru_RU.ts (#723)
Update amneziavpn_ru_RU.ts
2024-04-02 20:39:39 +01:00
36 changed files with 4022 additions and 133 deletions

View File

@@ -68,6 +68,7 @@ set(AMNEZIAVPN_TS_FILES
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_fa_IR.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar_EG.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_my_MM.ts
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_uk_UA.ts
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)

View File

@@ -406,25 +406,29 @@ class AmneziaActivity : QtActivity() {
Log.v(TAG, "Open file with filter: $filter")
val mimeTypes = if (!filter.isNullOrEmpty()) {
val extensionRegex = "\\*\\.[a-z .]+".toRegex(IGNORE_CASE)
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
val mime = MimeTypeMap.getSingleton()
extensionRegex.findAll(filter).map {
mime.getMimeTypeFromExtension(it.value.drop(2))
}.filterNotNull().toSet()
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
}.toSet()
} else emptySet()
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
Log.v(TAG, "File mimyType filter: $mimeTypes")
when (mimeTypes.size) {
1 -> type = mimeTypes.first()
if ("*/*" in mimeTypes) {
type = "*/*"
} else {
when (mimeTypes.size) {
1 -> type = mimeTypes.first()
in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}
else -> type = "*/*"
}
else -> type = "*/*"
}
}.also {
startActivityForResult(it, OPEN_FILE_ACTION_CODE)

View File

@@ -113,11 +113,13 @@ target_sources(${PROJECT} PRIVATE
target_sources(${PROJECT} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
)
add_subdirectory(ios/networkextension)

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -90,6 +90,14 @@ target_sources(networkextension PRIVATE
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
)
target_sources(networkextension PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
)
set_property(TARGET networkextension APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
)
## Build wireguard-go-version.h
execute_process(
COMMAND go list -m golang.zx2c4.com/wireguard

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -424,6 +424,8 @@ bool IosController::setupCloak()
openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu);
}
openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
for(int index = 0; index < splitTunnelSites.count(); index++) {

View File

@@ -256,6 +256,16 @@ Settings::RouteMode Settings::routeMode() const
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
}
bool Settings::getSitesSplitTunnelingEnabled() const
{
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
}
void Settings::setSitesSplitTunnelingEnabled(bool enabled)
{
setValue("Conf/sitesSplitTunnelingEnabled", enabled);
}
bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
{
QVariantMap sites = vpnSites(mode);
@@ -403,6 +413,16 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
m_settings.sync();
}
bool Settings::getAppsSplitTunnelingEnabled() const
{
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
}
void Settings::setAppsSplitTunnelingEnabled(bool enabled)
{
setValue("Conf/appsSplitTunnelingEnabled", enabled);
}
ServerCredentials Settings::defaultServerCredentials() const
{
return serverCredentials(defaultServerIndex());

View File

@@ -115,6 +115,9 @@ public:
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
bool getSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
QVariantMap vpnSites(RouteMode mode) const
{
return value("Conf/" + routeModeString(mode)).toMap();
@@ -208,6 +211,9 @@ public:
QVector<InstalledAppInfo> getVpnApps(AppsRouteMode mode) const;
void setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &apps);
bool getAppsSplitTunnelingEnabled() const;
void setAppsSplitTunnelingEnabled(bool enabled);
signals:
void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled);

View File

@@ -2906,7 +2906,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах.
Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке.
* Доступно в AmneziaVPN только для Windows.
* Доступен в AmneziaVPN только для Windows
* Низкое энергопотребление, на мобильных устройствах
* Минимальная конфигурация
* Распознается системами DPI-анализа
@@ -2974,9 +2974,9 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Recognised by DPI analysis systems and therefore susceptible to blocking
* Can operate over both TCP and UDP network protocols.</source>
<translation>OpenVPN однин из самых популярных и проверенных временем VPN-протоколов.
В нем используется уникальный протокол безопасности, опирающийся на протокол SSL/TLS для шифрования и обмена ключами. Кроме того, поддержка OpenVPN множества методов аутентификации делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Благодаря оптимальному соотношению производительности, безопасности и совместимости OpenVPN остается лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности.
В нем используется уникальный протокол безопасности, опирающийся на протокол SSL/TLS для шифрования и обмена ключами. Кроме того, OpenVPN поддерживает множество методов аутентификации, что делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Благодаря оптимальному соотношению производительности, безопасности и совместимости OpenVPN остается лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности.
* Доступность AmneziaVPN для всех платформ
* Доступен в AmneziaVPN для всех платформ
* Нормальное энергопотребление на мобильных устройствах
* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами
* Распознается системами DPI-анализа и поэтому подвержен блокировке
@@ -2994,7 +2994,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Works over TCP network protocol.</source>
<translation>Shadowsocks, создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Однако некоторые системы анализа трафика все же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG, или OpenVPN over Cloak.
* Доступен в AmneziaVPN только на ПК ноутбуках.
* Доступен в AmneziaVPN только для ПК и ноутбуков
* Настраиваемый протокол шифрования
* Обнаруживается некоторыми DPI-системами
* Работает по сетевому протоколу TCP.</translation>
@@ -3037,19 +3037,19 @@ 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.
</source>
<translation type="vanished">OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от блокировок.
<translation type="vanished">OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от обнаружения и блокировок.
OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером.
Протокол OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером.
Cloak защищает OpenVPN от обнаружения и блокировок.
Плагин Cloak защищает OpenVPN от обнаружения и блокировок.
Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению
Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем.
Если в вашем регионе существует экстремальный уровень цензуры в Интернете, мы советуем вам при первом подключении использовать только OpenVPN через Cloak
Если в вашем регионе экстремальный уровень цензуры в Интернете, мы советуем вам с первого подключения использовать только OpenVPN over Cloak
* Доступность AmneziaVPN на всех платформах
* Доступен в AmneziaVPN для всех платформ
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами DPI-анализа
@@ -3070,7 +3070,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures.
Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных.
WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга.
* Доступность AmneziaVPN для всех платформ
* Доступен в AmneziaVPN для всех платформ
* Низкое энергопотребление
* Минимальное количество настроек
* Легко распознается системами DPI-анализа, подвержен блокировке
@@ -3091,7 +3091,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком.
Таким образом, AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступность AmneziaVPN на всех платформах
* Доступен в AmneziaVPN для всех платформ
* Низкое энергопотребление
* Минимальное количество настроек
* Не распознается системами DPI-анализа, устойчив к блокировке

File diff suppressed because it is too large Load Diff

View File

@@ -62,6 +62,11 @@ void ConnectionController::openConnection()
DockerContainer container =
qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
emit connectionErrorOccurred(tr("The selected protocol is not supported on the current platform"));
return;
}
if (container == DockerContainer::None) {
emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
return;

View File

@@ -188,19 +188,24 @@ void InstallController::installServer(const DockerContainer container, const QMa
VpnConfigurationsController vpnConfigurationController(m_settings);
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
auto containerConfig = iterator.value();
auto errorCode =
vpnConfigurationController.createProtocolConfigForContainer(m_processedServerCredentials, iterator.key(), containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
containerConfigs.append(containerConfig);
errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
auto errorCode =
vpnConfigurationController.createProtocolConfigForContainer(m_processedServerCredentials, iterator.key(), containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
containerConfigs.append(containerConfig);
errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
} else {
containerConfigs.append(containerConfig);
}
}
@@ -222,18 +227,23 @@ void InstallController::installContainer(const DockerContainer container, const
QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key());
if (containerConfig.isEmpty()) {
containerConfig = iterator.value();
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
m_serversModel->addContainerConfig(iterator.key(), containerConfig);
errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
auto errorCode = vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
m_serversModel->addContainerConfig(iterator.key(), containerConfig);
errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
} else {
m_serversModel->addContainerConfig(iterator.key(), containerConfig);
}
if (container != iterator.key()) { // skip the newly installed container
@@ -279,22 +289,28 @@ void InstallController::scanServerForInstalledContainers()
VpnConfigurationsController vpnConfigurationController(m_settings);
for (auto iterator = installedContainers.begin(); iterator != installedContainers.end(); iterator++) {
QJsonObject containerConfig = m_containersModel->getContainerConfig(iterator.key());
auto container = iterator.key();
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
if (containerConfig.isEmpty()) {
containerConfig = iterator.value();
auto errorCode =
vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, iterator.key(), containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
m_serversModel->addContainerConfig(iterator.key(), containerConfig);
errorCode = m_clientManagementModel->appendClient(iterator.key(), serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
if (ContainerProps::isSupportedByCurrentPlatform(container)) {
auto errorCode =
vpnConfigurationController.createProtocolConfigForContainer(serverCredentials, container, containerConfig);
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
m_serversModel->addContainerConfig(container, containerConfig);
errorCode = m_clientManagementModel->appendClient(container, serverCredentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()));
if (errorCode) {
emit installationErrorOccurred(errorString(errorCode));
return;
}
} else {
m_serversModel->addContainerConfig(container, containerConfig);
}
isInstalledContainerAddedToGui = true;
@@ -517,7 +533,8 @@ void InstallController::removeAllContainers()
emit removeAllContainersFinished(tr("All containers from server '%1' have been removed").arg(serverName));
return;
}
emit installationErrorOccurred(errorString(errorCode));
qDebug() << errorString(errorCode);
}
void InstallController::removeProcessedContainer()

View File

@@ -5,14 +5,8 @@
AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
auto routeMode = m_settings->getAppsRouteMode();
if (routeMode == Settings::AppsRouteMode::VpnAllApps) {
m_isSplitTunnelingEnabled = false;
m_currentRouteMode = Settings::AppsRouteMode::VpnAllExceptApps;
} else {
m_isSplitTunnelingEnabled = true;
m_currentRouteMode = routeMode;
}
m_isSplitTunnelingEnabled = m_settings->getAppsSplitTunnelingEnabled();
m_currentRouteMode = m_settings->getAppsRouteMode();
m_apps = m_settings->getVpnApps(m_currentRouteMode);
}
@@ -84,11 +78,7 @@ bool AppSplitTunnelingModel::isSplitTunnelingEnabled()
void AppSplitTunnelingModel::toggleSplitTunneling(bool enabled)
{
if (enabled) {
setRouteMode(m_currentRouteMode);
} else {
m_settings->setAppsRouteMode(Settings::AppsRouteMode::VpnAllApps);
}
m_settings->setAppsSplitTunnelingEnabled(enabled);
m_isSplitTunnelingEnabled = enabled;
emit splitTunnelingToggled();
}

View File

@@ -83,6 +83,16 @@ QJsonObject ContainersModel::getContainerConfig(const int containerIndex)
return qvariant_cast<QJsonObject>(data(index(containerIndex), ConfigRole));
}
bool ContainersModel::isSupportedByCurrentPlatform(const int containerIndex)
{
return qvariant_cast<bool>(data(index(containerIndex), IsSupportedRole));
}
bool ContainersModel::isServiceContainer(const int containerIndex)
{
return qvariant_cast<amnezia::ServiceType>(data(index(containerIndex), ServiceTypeRole) == ServiceType::Other);
}
QHash<int, QByteArray> ContainersModel::roleNames() const
{
QHash<int, QByteArray> roles;

View File

@@ -49,6 +49,9 @@ public slots:
QJsonObject getContainerConfig(const int containerIndex);
bool isSupportedByCurrentPlatform(const int containerIndex);
bool isServiceContainer(const int containerIndex);
protected:
QHash<int, QByteArray> roleNames() const override;

View File

@@ -64,6 +64,7 @@ QVector<QPair<QString, QString>> InstalledAppsModel::getSelectedAppsInfo()
appsInfo.push_back({ appName, packageName });
}
m_selectedAppIndexes.clear();
return appsInfo;
}

View File

@@ -43,6 +43,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
switch (language) {
case LanguageSettings::AvailableLanguageEnum::English: strLanguage = "English"; break;
case LanguageSettings::AvailableLanguageEnum::Russian: strLanguage = "Русский"; break;
case LanguageSettings::AvailableLanguageEnum::Ukrainian: strLanguage = "Українська"; break;
case LanguageSettings::AvailableLanguageEnum::China_cn: strLanguage = "\347\256\200\344\275\223\344\270\255\346\226\207"; break;
case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break;
case LanguageSettings::AvailableLanguageEnum::Arabic: strLanguage = "العربية"; break;
@@ -60,6 +61,7 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum
case LanguageSettings::AvailableLanguageEnum::English: emit updateTranslations(QLocale::English); break;
case LanguageSettings::AvailableLanguageEnum::Russian: emit updateTranslations(QLocale::Russian); break;
case LanguageSettings::AvailableLanguageEnum::China_cn: emit updateTranslations(QLocale::Chinese); break;
case LanguageSettings::AvailableLanguageEnum::Ukrainian: emit updateTranslations(QLocale::Ukrainian); break;
case LanguageSettings::AvailableLanguageEnum::Persian: emit updateTranslations(QLocale::Persian); break;
case LanguageSettings::AvailableLanguageEnum::Arabic: emit updateTranslations(QLocale::Arabic); break;
case LanguageSettings::AvailableLanguageEnum::Burmese: emit updateTranslations(QLocale::Burmese); break;
@@ -74,6 +76,7 @@ int LanguageModel::getCurrentLanguageIndex()
case QLocale::English: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
case QLocale::Russian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Russian); break;
case QLocale::Chinese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::China_cn); break;
case QLocale::Ukrainian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Ukrainian); break;
case QLocale::Persian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Persian); break;
case QLocale::Arabic: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Arabic); break;
case QLocale::Burmese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Burmese); break;

View File

@@ -13,6 +13,7 @@ namespace LanguageSettings
English,
Russian,
China_cn,
Ukrainian,
Persian,
Arabic,
Burmese

View File

@@ -105,12 +105,12 @@ void OpenVpnConfigModel::updateModel(const QJsonObject &config)
m_protocolConfig.insert(config_key::hash,
protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash));
m_protocolConfig.insert(config_key::block_outside_dns,
protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth));
protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns));
m_protocolConfig.insert(config_key::port,
protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort));
m_protocolConfig.insert(
config_key::tls_auth,
protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns));
protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth));
m_protocolConfig.insert(config_key::additional_client_config,
protocolConfig.value(config_key::additional_client_config)
.toString(protocols::openvpn::defaultAdditionalClientConfig));

View File

@@ -31,10 +31,15 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
}
QJsonObject server = m_servers.at(index.row()).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
switch (role) {
case NameRole: {
server.insert(config_key::description, value.toString());
if (configVersion) {
server.insert(config_key::name, value.toString());
} else {
server.insert(config_key::description, value.toString());
}
m_settings->editServer(index.row(), server);
m_servers.replace(index.row(), server);
if (index.row() == m_defaultServerIndex) {
@@ -400,8 +405,9 @@ void ServersModel::addContainerConfig(const int containerIndex, const QJsonObjec
server.insert(config_key::containers, containers);
auto defaultContainer = server.value(config_key::defaultContainer).toString();
if ((ContainerProps::containerFromString(defaultContainer) == DockerContainer::None
|| ContainerProps::containerService(container) != ServiceType::Other)) {
if (ContainerProps::containerFromString(defaultContainer) == DockerContainer::None
&& ContainerProps::containerService(container) != ServiceType::Other
&& ContainerProps::isSupportedByCurrentPlatform(container)) {
server.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
}

View File

@@ -3,14 +3,8 @@
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
auto routeMode = m_settings->routeMode();
if (routeMode == Settings::RouteMode::VpnAllSites) {
m_isSplitTunnelingEnabled = false;
m_currentRouteMode = Settings::RouteMode::VpnOnlyForwardSites;
} else {
m_isSplitTunnelingEnabled = true;
m_currentRouteMode = routeMode;
}
m_isSplitTunnelingEnabled = m_settings->getSitesSplitTunnelingEnabled();
m_currentRouteMode = m_settings->routeMode();
fillSites();
}
@@ -107,11 +101,7 @@ bool SitesModel::isSplitTunnelingEnabled()
void SitesModel::toggleSplitTunneling(bool enabled)
{
if (enabled) {
setRouteMode(m_currentRouteMode);
} else {
m_settings->setRouteMode(Settings::RouteMode::VpnAllSites);
}
m_settings->setSitesSplitTunnelingEnabled(enabled);
m_isSplitTunnelingEnabled = enabled;
emit splitTunnelingToggled();
}

View File

@@ -51,7 +51,7 @@ ListView {
imageSource: "qrc:/images/controls/download.svg"
showImage: !isInstalled
checkable: isInstalled && !ConnectionController.isConnected && isSupported
checkable: isInstalled && !ConnectionController.isConnected
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersModel.getDefaultServerData("defaultContainer")
onClicked: {
@@ -64,11 +64,6 @@ ListView {
containersDropDown.close()
ServersModel.setDefaultContainer(ServersModel.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index))
} else {
if (!isSupported && isInstalled) {
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
return
}
ContainersModel.setProcessedContainerIndex(proxyDefaultServerContainersModel.mapToSource(index))
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)

View File

@@ -59,8 +59,6 @@ DrawerType2 {
Layout.fillWidth: true
Layout.topMargin: 16
enabled: !ServersModel.isDefaultServerDefaultContainerHasSplitTunneling || !ServersModel.getDefaultServerData("isServerFromApi")
text: qsTr("Site-based split tunneling")
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"

View File

@@ -5,6 +5,8 @@ import QtQuick.Layouts
import "../Controls2"
import "../Controls2/TextTypes"
import SortFilterProxyModel 0.2
import InstalledAppsModel 1.0
DrawerType2 {
@@ -34,7 +36,7 @@ DrawerType2 {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: addButton.top
anchors.bottom: searchField.top
anchors.topMargin: 16
BackButtonType {
@@ -66,7 +68,15 @@ DrawerType2 {
clip: true
interactive: true
model: installedAppsModel
model: SortFilterProxyModel {
id: proxyInstalledAppsModel
sourceModel: installedAppsModel
filters: RegExpFilter {
roleName: "appName"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
}
ScrollBar.vertical: ScrollBar {
id: scrollBar
@@ -93,7 +103,7 @@ DrawerType2 {
text: appName
onCheckedChanged: {
listView.model.selectedStateChanged(index, checked)
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
}
}
@@ -113,6 +123,21 @@ DrawerType2 {
}
}
TextFieldWithHeaderType {
id: searchField
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: addButton.top
anchors.bottomMargin: 16
anchors.rightMargin: 16
anchors.leftMargin: 16
backgroundColor: "#2C2D30"
textFieldPlaceholderText: qsTr("application name")
}
BasicButtonType {
id: addButton
@@ -127,7 +152,7 @@ DrawerType2 {
clickedFunc: function() {
PageController.showBusyIndicator(true)
AppSplitTunnelingController.addApps(listView.model.getSelectedAppsInfo())
AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo())
PageController.showBusyIndicator(false)
root.close()
}

View File

@@ -170,7 +170,7 @@ PageType {
Header1TextType {
id: collapsedButtonHeader
Layout.maximumWidth: drawer.isCollapsed ? drawer.width - 48 - 18 - 12 : drawer.width// todo
Layout.maximumWidth: drawer.width - 48 - 18 - 12
maximumLineCount: 2
elide: Qt.ElideRight

View File

@@ -68,8 +68,14 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& SettingsController.isAmneziaDnsEnabled()) {
PageController.showNotificationMessage(qsTr("Cannot remove Amnezia DNS from running server"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
}

View File

@@ -20,6 +20,17 @@ import "../Components"
PageType {
id: root
property bool pageEnabled
Component.onCompleted: {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection"))
root.pageEnabled = false
} else {
root.pageEnabled = true
}
}
QtObject {
id: routeMode
property int allApps: 0
@@ -70,6 +81,8 @@ PageType {
Layout.leftMargin: 16
headerText: qsTr("App split tunneling")
enabled: root.pageEnabled
}
SwitcherType {
@@ -78,6 +91,8 @@ PageType {
Layout.fillWidth: true
Layout.rightMargin: 16
enabled: root.pageEnabled
checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
@@ -99,7 +114,7 @@ PageType {
headerText: qsTr("Mode")
enabled: Qt.platform.os === "android"
enabled: Qt.platform.os === "android" && root.pageEnabled
listView: ListViewWithRadioButtonType {
rootWidth: root.width
@@ -139,6 +154,8 @@ PageType {
anchors.topMargin: 16
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
enabled: root.pageEnabled
Column {
id: col
anchors.top: parent.top
@@ -213,6 +230,8 @@ PageType {
RowLayout {
id: addAppButton
enabled: root.pageEnabled
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right

View File

@@ -169,8 +169,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
SettingsController.clearSettings()
PageController.replaceStartPage()
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset settings during active connection"))
} else
{
SettingsController.clearSettings()
PageController.replaceStartPage()
}
}
var noButtonFunction = function() {
}

View File

@@ -135,9 +135,14 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot restore backup settings during active connection"))
} else
{
PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
}

View File

@@ -116,12 +116,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
}
@@ -147,12 +148,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
}
@@ -177,11 +179,12 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
ConnectionController.closeConnection()
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers()
}
InstallController.removeAllContainers()
}
var noButtonFunction = function() {
}
@@ -208,9 +211,13 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
InstallController.removeApiConfig()
PageController.showBusyIndicator(false)
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeApiConfig()
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
}

View File

@@ -147,8 +147,14 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
}

View File

@@ -24,9 +24,7 @@ PageType {
defaultActiveFocusItem: searchField.textField
property bool pageEnabled: {
return !ConnectionController.isConnected && !isServerFromApi
}
property bool pageEnabled
Component.onCompleted: {
if (ConnectionController.isConnected) {

View File

@@ -25,8 +25,9 @@ PageType {
target: InstallController
function onInstallContainerFinished(finishedMessage, isServiceInstall) {
if (!ConnectionController.isConnected && !isServiceInstall) {
ServersModel.setDefaultContainer(ServersModel.processedIndex, ContainersModel.getProcessedContainerIndex())
var containerIndex = ContainersModel.getProcessedContainerIndex()
if (!ConnectionController.isConnected && !ContainersModel.isServiceContainer(containerIndex)) {
ServersModel.setDefaultContainer(ServersModel.processedIndex, containerIndex)
}
PageController.closePage() // close installing page

View File

@@ -67,7 +67,9 @@ void SystemTrayNotificationHandler::onTranslationsUpdated()
void SystemTrayNotificationHandler::setTrayIcon(const QString &iconPath)
{
QIcon trayIconMask(QPixmap(iconPath).scaled(128,128));
#ifndef Q_OS_MAC
trayIconMask.setIsMask(true);
#endif
m_systemTrayIcon.setIcon(trayIconMask);
}

View File

@@ -301,7 +301,11 @@ void VpnConnection::appendSplitTunnelingConfig()
}
}
auto routeMode = m_settings->routeMode();
Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites;
if (m_settings->getSitesSplitTunnelingEnabled()) {
routeMode = m_settings->routeMode();
}
auto sites = m_settings->getVpnIps(routeMode);
QJsonArray sitesJsonArray;
@@ -318,7 +322,11 @@ void VpnConnection::appendSplitTunnelingConfig()
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
auto appsRouteMode = m_settings->getAppsRouteMode();
Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
if (m_settings->getAppsSplitTunnelingEnabled()) {
appsRouteMode = m_settings->getAppsRouteMode();
}
auto apps = m_settings->getVpnApps(appsRouteMode);
QJsonArray appsJsonArray;