Merge branch 'dev' into feature/killswitch-strict-mode
This commit is contained in:
commit
8edc60e574
16 changed files with 985 additions and 424 deletions
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.4.2
|
project(${PROJECT} VERSION 4.8.4.3
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2079)
|
set(APP_ANDROID_VERSION_CODE 2080)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||||
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
auto replyProcessingFunction = [&encryptedResponseBody, &reply, &sslErrors, &key, &iv, &salt,
|
||||||
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
this](QNetworkReply *nestedReply, const QList<QSslError> &nestedSslErrors) {
|
||||||
encryptedResponseBody = nestedReply->readAll();
|
encryptedResponseBody = nestedReply->readAll();
|
||||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
reply = nestedReply;
|
||||||
|
if (!sslErrors.isEmpty() || shouldBypassProxy(nestedReply, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
sslErrors = nestedSslErrors;
|
sslErrors = nestedSslErrors;
|
||||||
reply = nestedReply;
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
bypassProxy(endpoint, reply, requestFunction, replyProcessingFunction);
|
||||||
|
|
@ -212,45 +212,45 @@ QStringList GatewayController::getProxyUrls()
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
break;
|
auto encryptedResponseBody = reply->readAll();
|
||||||
}
|
reply->deleteLater();
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto encryptedResponseBody = reply->readAll();
|
EVP_PKEY *privateKey = nullptr;
|
||||||
reply->deleteLater();
|
QByteArray responseBody;
|
||||||
|
try {
|
||||||
|
if (!m_isDevEnvironment) {
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha512);
|
||||||
|
hash.addData(key);
|
||||||
|
QByteArray hashResult = hash.result().toHex();
|
||||||
|
|
||||||
EVP_PKEY *privateKey = nullptr;
|
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
||||||
QByteArray responseBody;
|
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
||||||
try {
|
|
||||||
if (!m_isDevEnvironment) {
|
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha512);
|
|
||||||
hash.addData(key);
|
|
||||||
QByteArray hashResult = hash.result().toHex();
|
|
||||||
|
|
||||||
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
||||||
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
|
||||||
|
|
||||||
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
||||||
|
} else {
|
||||||
|
responseBody = encryptedResponseBody;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
Utils::logException();
|
||||||
|
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||||
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
|
||||||
|
QStringList endpoints;
|
||||||
|
for (const auto &endpoint : endpointsArray) {
|
||||||
|
endpoints.push_back(endpoint.toString());
|
||||||
|
}
|
||||||
|
return endpoints;
|
||||||
} else {
|
} else {
|
||||||
responseBody = encryptedResponseBody;
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
} catch (...) {
|
|
||||||
Utils::logException();
|
|
||||||
qCritical() << "error loading private key from environment variables or decrypting payload" << encryptedResponseBody;
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
|
||||||
|
|
||||||
QStringList endpoints;
|
|
||||||
for (const auto &endpoint : endpointsArray) {
|
|
||||||
endpoints.push_back(endpoint.toString());
|
|
||||||
}
|
|
||||||
return endpoints;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key,
|
||||||
|
|
@ -262,7 +262,7 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
|
||||||
} else if (responseBody.contains("html")) {
|
} else if (responseBody.contains("html")) {
|
||||||
qDebug() << "The response contains an html tag";
|
qDebug() << "The response contains an html tag";
|
||||||
return true;
|
return true;
|
||||||
} else if (checkEncryption) {
|
} else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
|
||||||
try {
|
try {
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||||
|
|
@ -296,7 +296,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
||||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
wait.exec();
|
wait.exec();
|
||||||
|
|
||||||
if (!replyProcessingFunction(reply, sslErrors)) {
|
if (replyProcessingFunction(reply, sslErrors)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,15 @@ extension UIApplication {
|
||||||
var keyWindows: [UIWindow] {
|
var keyWindows: [UIWindow] {
|
||||||
connectedScenes
|
connectedScenes
|
||||||
.compactMap {
|
.compactMap {
|
||||||
|
guard let windowScene = $0 as? UIWindowScene else { return nil }
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
($0 as? UIWindowScene)?.keyWindow
|
guard let keywindow = windowScene.keyWindow else {
|
||||||
|
windowScene.windows.first?.makeKey()
|
||||||
|
return windowScene.windows.first
|
||||||
|
}
|
||||||
|
return keywindow
|
||||||
} else {
|
} else {
|
||||||
($0 as? UIWindowScene)?.windows.first { $0.isKeyWindow }
|
return windowScene.windows.first { $0.isKeyWindow }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -407,7 +407,7 @@ bool ApiConfigsController::isConfigValid()
|
||||||
return updateServiceFromGateway(serverIndex, "", "");
|
return updateServiceFromGateway(serverIndex, "", "");
|
||||||
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
|
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
|
||||||
qDebug() << "attempt to update api config by expires_at event";
|
qDebug() << "attempt to update api config by expires_at event";
|
||||||
if (configSource == apiDefs::ConfigSource::Telegram) {
|
if (configSource == apiDefs::ConfigSource::AmneziaGateway) {
|
||||||
return updateServiceFromGateway(serverIndex, "", "");
|
return updateServiceFromGateway(serverIndex, "", "");
|
||||||
} else {
|
} else {
|
||||||
m_serversModel->removeApiConfig(serverIndex);
|
m_serversModel->removeApiConfig(serverIndex);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ namespace
|
||||||
ConfigTypes checkConfigFormat(const QString &config)
|
ConfigTypes checkConfigFormat(const QString &config)
|
||||||
{
|
{
|
||||||
const QString openVpnConfigPatternCli = "client";
|
const QString openVpnConfigPatternCli = "client";
|
||||||
const QString openVpnConfigPatternProto1 = "proto tcp";
|
|
||||||
const QString openVpnConfigPatternProto2 = "proto udp";
|
|
||||||
const QString openVpnConfigPatternDriver1 = "dev tun";
|
const QString openVpnConfigPatternDriver1 = "dev tun";
|
||||||
const QString openVpnConfigPatternDriver2 = "dev tap";
|
const QString openVpnConfigPatternDriver2 = "dev tap";
|
||||||
|
|
||||||
|
|
@ -53,14 +51,13 @@ namespace
|
||||||
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
|
|| (config.contains(amneziaConfigPatternHostName) && config.contains(amneziaConfigPatternUserName)
|
||||||
&& config.contains(amneziaConfigPatternPassword))) {
|
&& config.contains(amneziaConfigPatternPassword))) {
|
||||||
return ConfigTypes::Amnezia;
|
return ConfigTypes::Amnezia;
|
||||||
} else if (config.contains(openVpnConfigPatternCli)
|
|
||||||
&& (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2))
|
|
||||||
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
|
||||||
return ConfigTypes::OpenVpn;
|
|
||||||
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
|
} else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) {
|
||||||
return ConfigTypes::WireGuard;
|
return ConfigTypes::WireGuard;
|
||||||
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
||||||
return ConfigTypes::Xray;
|
return ConfigTypes::Xray;
|
||||||
|
} else if (config.contains(openVpnConfigPatternCli)
|
||||||
|
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||||
|
return ConfigTypes::OpenVpn;
|
||||||
}
|
}
|
||||||
return ConfigTypes::Invalid;
|
return ConfigTypes::Invalid;
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +342,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
||||||
arr.push_back(containers);
|
arr.push_back(containers);
|
||||||
|
|
||||||
QString hostName;
|
QString hostName;
|
||||||
const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*");
|
const static QRegularExpression hostNameRegExp("remote\\s+([^\\s]+)");
|
||||||
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data);
|
||||||
if (hostNameMatch.hasMatch()) {
|
if (hostNameMatch.hasMatch()) {
|
||||||
hostName = hostNameMatch.captured(1);
|
hostName = hostNameMatch.captured(1);
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ void SitesController::addSite(QString hostname)
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||||
Q_ARG(QStringList, QStringList() << hostname));
|
Q_ARG(QStringList, QStringList() << hostname));
|
||||||
}
|
}
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
|
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
|
||||||
|
|
@ -75,7 +74,6 @@ void SitesController::removeSite(int index)
|
||||||
|
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
|
||||||
Q_ARG(QStringList, QStringList() << hostname));
|
Q_ARG(QStringList, QStringList() << hostname));
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
|
||||||
|
|
||||||
emit finished(tr("Site removed: %1").arg(hostname));
|
emit finished(tr("Site removed: %1").arg(hostname));
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +122,6 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||||
m_sitesModel->addSites(sites, replaceExisting);
|
m_sitesModel->addSites(sites, replaceExisting);
|
||||||
|
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
|
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
|
||||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "flushDns", Qt::QueuedConnection);
|
|
||||||
|
|
||||||
emit finished(tr("Import completed"));
|
emit finished(tr("Import completed"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||||
}
|
}
|
||||||
case ServiceDescriptionRole: {
|
case ServiceDescriptionRole: {
|
||||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||||
return tr("Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to 200 "
|
return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. "
|
||||||
"Mb/s");
|
"Speeds up to 200 Mbps");
|
||||||
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||||
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
|
return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and "
|
||||||
"more. YouTube is not included in the free plan.");
|
"more. YouTube is not included in the free plan.");
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||||
case CardDescriptionRole: {
|
case CardDescriptionRole: {
|
||||||
auto speed = apiServiceData.serviceInfo.speed;
|
auto speed = apiServiceData.serviceInfo.speed;
|
||||||
if (serviceType == serviceType::amneziaPremium) {
|
if (serviceType == serviceType::amneziaPremium) {
|
||||||
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. "
|
||||||
"Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.")
|
"Access all websites and online resources. Speeds up to %1 Mbps.")
|
||||||
.arg(speed);
|
.arg(speed);
|
||||||
} else if (serviceType == serviceType::amneziaFree) {
|
} else if (serviceType == serviceType::amneziaFree) {
|
||||||
QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
||||||
|
|
@ -79,8 +79,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||||
}
|
}
|
||||||
case ServiceDescriptionRole: {
|
case ServiceDescriptionRole: {
|
||||||
if (serviceType == serviceType::amneziaPremium) {
|
if (serviceType == serviceType::amneziaPremium) {
|
||||||
return tr("Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. "
|
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
|
||||||
"Works for any sites with no restrictions.");
|
"Access all websites and online resources.");
|
||||||
} else {
|
} else {
|
||||||
return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ Rectangle {
|
||||||
Layout.rightMargin: 10
|
Layout.rightMargin: 10
|
||||||
Layout.leftMargin: 10
|
Layout.leftMargin: 10
|
||||||
|
|
||||||
text: qsTr("Amnezia Premium - for access to any website")
|
text: qsTr("Amnezia Premium - for access to all websites and online resources")
|
||||||
color: AmneziaStyle.color.pearlGray
|
color: AmneziaStyle.color.pearlGray
|
||||||
|
|
||||||
lineHeight: 18
|
lineHeight: 18
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ PageType {
|
||||||
actionButtonImage: "qrc:/images/controls/settings.svg"
|
actionButtonImage: "qrc:/images/controls/settings.svg"
|
||||||
|
|
||||||
headerText: root.processedServer.name
|
headerText: root.processedServer.name
|
||||||
descriptionText: qsTr("Locations for connection")
|
descriptionText: qsTr("Location for connection")
|
||||||
|
|
||||||
actionButtonFunction: function() {
|
actionButtonFunction: function() {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ PageType {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Connected devices")
|
headerText: qsTr("Active Devices")
|
||||||
descriptionText: qsTr("To manage connected devices")
|
descriptionText: qsTr("Manage currently connected devices")
|
||||||
}
|
}
|
||||||
|
|
||||||
WarningType {
|
WarningType {
|
||||||
|
|
@ -71,8 +71,13 @@ PageType {
|
||||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
var headerText = qsTr("Deactivate the subscription on selected device")
|
if (isCurrentDevice && ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||||
var descriptionText = qsTr("The next time the “Connect” button is pressed, the device will be activated again")
|
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerText = qsTr("Are you sure you want to unlink this device?")
|
||||||
|
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ PageType {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("How to connect on another device")
|
headerText: qsTr("How to connect on another device")
|
||||||
descriptionText: qsTr("Instructions on the Amnezia website")
|
descriptionText: qsTr("Setup guides on the Amnezia website")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ PageType {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Configuration files")
|
headerText: qsTr("Configuration Files")
|
||||||
descriptionText: qsTr("To connect a router or AmneziaWG application")
|
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,13 +123,13 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: 16
|
Layout.margins: 16
|
||||||
|
|
||||||
headerText: qsTr("Configuration file ") + moreOptionsDrawer.countryName
|
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("Create a new")
|
text: qsTr("Generate a new configuration file")
|
||||||
descriptionText: qsTr("The previously created one will stop working")
|
descriptionText: qsTr("The previously created one will stop working")
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|
@ -193,9 +193,15 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||||
var headerText = qsTr("Revoke the actual %1 configuration file?").arg(countryName)
|
var headerText
|
||||||
var descriptionText = qsTr("The previously created file will no longer be valid. It will not be possible to connect using it.")
|
if (isConfigIssue) {
|
||||||
var yesButtonText = qsTr("Continue")
|
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||||
|
} else {
|
||||||
|
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||||
|
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
var yesButtonFunction = function() {
|
var yesButtonFunction = function() {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: statusObject
|
id: statusObject
|
||||||
|
|
||||||
readonly property string title: qsTr("Subscription status")
|
readonly property string title: qsTr("Subscription Status")
|
||||||
readonly property string contentKey: "subscriptionStatus"
|
readonly property string contentKey: "subscriptionStatus"
|
||||||
readonly property string objectImageSource: "qrc:/images/controls/info.svg"
|
readonly property string objectImageSource: "qrc:/images/controls/info.svg"
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +34,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: endDateObject
|
id: endDateObject
|
||||||
|
|
||||||
readonly property string title: qsTr("Valid until")
|
readonly property string title: qsTr("Valid Until")
|
||||||
readonly property string contentKey: "endDate"
|
readonly property string contentKey: "endDate"
|
||||||
readonly property string objectImageSource: "qrc:/images/controls/history.svg"
|
readonly property string objectImageSource: "qrc:/images/controls/history.svg"
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: deviceCountObject
|
id: deviceCountObject
|
||||||
|
|
||||||
readonly property string title: qsTr("Connected devices")
|
readonly property string title: qsTr("Active Connections")
|
||||||
readonly property string contentKey: "connectedDevices"
|
readonly property string contentKey: "connectedDevices"
|
||||||
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
|
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +183,7 @@ PageType {
|
||||||
|
|
||||||
visible: false //footer.isVisibleForAmneziaFree
|
visible: false //footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("Subscription key")
|
text: qsTr("Subscription Key")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|
@ -191,7 +191,7 @@ PageType {
|
||||||
|
|
||||||
shareConnectionDrawer.openTriggered()
|
shareConnectionDrawer.openTriggered()
|
||||||
shareConnectionDrawer.isSelfHostedConfig = false;
|
shareConnectionDrawer.isSelfHostedConfig = false;
|
||||||
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key to file")
|
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file")
|
||||||
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
|
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -213,9 +213,9 @@ PageType {
|
||||||
|
|
||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("Configuration files")
|
text: qsTr("Configuration Files")
|
||||||
|
|
||||||
descriptionText: qsTr("To connect a router or AmneziaWG application")
|
descriptionText: qsTr("Manage configuration files")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|
@ -233,9 +233,9 @@ PageType {
|
||||||
|
|
||||||
visible: footer.isVisibleForAmneziaFree
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("Connected devices")
|
text: qsTr("Active Devices")
|
||||||
|
|
||||||
descriptionText: qsTr("To manage connected devices")
|
descriptionText: qsTr("Manage currently connected devices")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
|
@ -265,6 +265,8 @@ PageType {
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
|
||||||
text: qsTr("How to connect on another device")
|
text: qsTr("How to connect on another device")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
|
@ -273,7 +275,9 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {
|
||||||
|
visible: footer.isVisibleForAmneziaFree
|
||||||
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: resetButton
|
id: resetButton
|
||||||
|
|
@ -325,17 +329,17 @@ PageType {
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
pressedColor: AmneziaStyle.color.sheerWhite
|
||||||
textColor: AmneziaStyle.color.vibrantRed
|
textColor: AmneziaStyle.color.vibrantRed
|
||||||
|
|
||||||
text: qsTr("Deactivate the subscription on this device")
|
text: qsTr("Unlink this device")
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
var headerText = qsTr("Deactivate the subscription on this device?")
|
var headerText = qsTr("Are you sure you want to unlink this device?")
|
||||||
var descriptionText = qsTr("The next time the “Connect” button is pressed, the device will be activated again")
|
var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
var noButtonText = qsTr("Cancel")
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
var yesButtonFunction = function() {
|
var yesButtonFunction = function() {
|
||||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||||
PageController.showNotificationMessage(qsTr("Cannot deactivate subscription during active connection"))
|
PageController.showNotificationMessage(qsTr("Cannot unlink device during active connection"))
|
||||||
} else {
|
} else {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
if (ApiConfigsController.deactivateDevice()) {
|
if (ApiConfigsController.deactivateDevice()) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: techSupport
|
id: techSupport
|
||||||
|
|
||||||
readonly property string title: qsTr("For technical support")
|
readonly property string title: qsTr("Email")
|
||||||
readonly property string description: qsTr("support@amnezia.org")
|
readonly property string description: qsTr("support@amnezia.org")
|
||||||
readonly property string link: "mailto:support@amnezia.org"
|
readonly property string link: "mailto:support@amnezia.org"
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: paymentSupport
|
id: paymentSupport
|
||||||
|
|
||||||
readonly property string title: qsTr("For payment issues")
|
readonly property string title: qsTr("Email Billing & Orders")
|
||||||
readonly property string description: qsTr("help@vpnpay.io")
|
readonly property string description: qsTr("help@vpnpay.io")
|
||||||
readonly property string link: "mailto:help@vpnpay.io"
|
readonly property string link: "mailto:help@vpnpay.io"
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ PageType {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: site
|
id: site
|
||||||
|
|
||||||
readonly property string title: qsTr("Site")
|
readonly property string title: qsTr("Website")
|
||||||
readonly property string description: qsTr("amnezia.org")
|
readonly property string description: qsTr("amnezia.org")
|
||||||
readonly property string link: LanguageModel.getCurrentSiteUrl()
|
readonly property string link: LanguageModel.getCurrentSiteUrl()
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ PageType {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Support")
|
headerText: qsTr("Support")
|
||||||
descriptionText: qsTr("Our technical support specialists are ready to help you at any time")
|
descriptionText: qsTr("Our technical support specialists are available to assist you at any time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue