mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-17 08:16:06 +03:00
Compare commits
16 Commits
user-check
...
feature/em
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
341faac4f2 | ||
|
|
f0d841d649 | ||
|
|
3e47a41985 | ||
|
|
b8702f6946 | ||
|
|
9d571a4c71 | ||
|
|
f283858490 | ||
|
|
76fe203767 | ||
|
|
b9a47f2f50 | ||
|
|
27cb17c640 | ||
|
|
ef8fb89eb3 | ||
|
|
f1b045f8a8 | ||
|
|
050066132b | ||
|
|
2a6e6a1e24 | ||
|
|
92689d084c | ||
|
|
00f314039d | ||
|
|
fcb75e837d |
6
.github/workflows/deploy.yml
vendored
6
.github/workflows/deploy.yml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
Build-Linux-Ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.6.2
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
- name: 'Build project'
|
||||
run: |
|
||||
sudo apt-get install libxkbcommon-x11-0
|
||||
sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev
|
||||
export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin
|
||||
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
|
||||
bash deploy/build_linux.sh
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
- name: 'Install go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22.1'
|
||||
go-version: '1.24'
|
||||
cache: false
|
||||
|
||||
- name: 'Setup gomobile'
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.4.3
|
||||
project(${PROJECT} VERSION 4.8.5.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 2080)
|
||||
set(APP_ANDROID_VERSION_CODE 2082)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "coreController.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QTranslator>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
@@ -238,7 +239,23 @@ void CoreController::updateTranslator(const QLocale &locale)
|
||||
QCoreApplication::removeTranslator(m_translator.get());
|
||||
}
|
||||
|
||||
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
|
||||
QStringList availableTranslations;
|
||||
QDirIterator it(":/translations", QStringList("amneziavpn_*.qm"), QDir::Files);
|
||||
while (it.hasNext()) {
|
||||
availableTranslations << it.next();
|
||||
}
|
||||
|
||||
// This code allow to load translation for the language only, without country code
|
||||
const QString lang = locale.name().split("_").first();
|
||||
const QString translationFilePrefix = QString(":/translations/amneziavpn_") + lang;
|
||||
QString strFileName = QString(":/translations/amneziavpn_%1.qm").arg(locale.name());
|
||||
for (const QString &translation : availableTranslations) {
|
||||
if (translation.contains(translationFilePrefix)) {
|
||||
strFileName = translation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_translator->load(strFileName)) {
|
||||
if (QCoreApplication::installTranslator(m_translator.get())) {
|
||||
m_settings->setAppLanguage(locale);
|
||||
|
||||
@@ -709,7 +709,7 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||
|
||||
// TODO reimplement with netstat
|
||||
QString script = QString("which lsof &>/dev/null || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
QString script = QString("which lsof > /dev/null 2>&1 || true && sudo lsof -i -P -n 2>/dev/null | grep -E ':%1 ").arg(port);
|
||||
for (auto &port : fixedPorts) {
|
||||
script = script.append("|:%1").arg(port);
|
||||
}
|
||||
@@ -771,7 +771,7 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
|
||||
return ErrorCode::SudoPackageIsNotPreinstalled;
|
||||
return ErrorCode::ServerSudoPackageIsNotPreinstalled;
|
||||
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
|
||||
return ErrorCode::ServerUserNotInSudo;
|
||||
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace amnezia
|
||||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
SudoPackageIsNotPreinstalled = 207,
|
||||
ServerSudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserDirectoryNotAccessible = 208,
|
||||
ServerUserNotAllowedInSudoers = 209,
|
||||
ServerUserPasswordRequired = 210,
|
||||
|
||||
@@ -22,7 +22,7 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
|
||||
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
|
||||
case(ErrorCode::ServerSudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed on the server"); break;
|
||||
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
|
||||
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
|
||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||
|
||||
@@ -4,7 +4,7 @@ elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
|
||||
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
|
||||
else pm="uname"; opt="-a";\
|
||||
fi;\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///');\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
|
||||
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
|
||||
sudo -K;\
|
||||
cd ~;\
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///');\
|
||||
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
|
||||
sudo mkdir -p $DOCKERFILE_FOLDER;\
|
||||
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
|
||||
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace
|
||||
constexpr char cloak[] = "cloak";
|
||||
constexpr char awg[] = "awg";
|
||||
|
||||
constexpr char apiEdnpoint[] = "api_endpoint";
|
||||
constexpr char apiEndpoint[] = "api_endpoint";
|
||||
constexpr char accessToken[] = "api_key";
|
||||
constexpr char certificate[] = "certificate";
|
||||
constexpr char publicKey[] = "public_key";
|
||||
@@ -251,7 +251,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
|
||||
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
||||
newServerConfig.insert(configKey::authData, authData);
|
||||
// newServerConfig.insert(
|
||||
|
||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||
if (reloadServiceConfig) {
|
||||
@@ -270,54 +269,37 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
|
||||
bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
|
||||
{
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
IosController::Instance()->requestInetAccess();
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(apiDefs::requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||
request.setUrl(endpoint);
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
|
||||
|
||||
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
|
||||
auto installationUuid = m_settings->getInstallationUuid(true);
|
||||
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||
QString serviceProtocol = serverConfig.value(configKey::protocol).toString();
|
||||
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
|
||||
|
||||
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString();
|
||||
apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString();
|
||||
|
||||
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody);
|
||||
|
||||
QNetworkReply *reply = amnApp->networkManager()->post(request, requestBody);
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
|
||||
|
||||
QEventLoop wait;
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
|
||||
QList<QSslError> sslErrors;
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
|
||||
auto errorCode = apiUtils::checkNetworkReplyErrors(sslErrors, reply);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
reply->deleteLater();
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto apiResponseBody = reply->readAll();
|
||||
reply->deleteLater();
|
||||
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
|
||||
m_serversModel->editServer(serverConfig, serverIndex);
|
||||
emit updateServerFromApiFinished();
|
||||
return true;
|
||||
} else {
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApiConfigsController::deactivateDevice()
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace
|
||||
} else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) {
|
||||
return ConfigTypes::Xray;
|
||||
} else if (config.contains(openVpnConfigPatternCli)
|
||||
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||
&& (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) {
|
||||
return ConfigTypes::OpenVpn;
|
||||
}
|
||||
return ConfigTypes::Invalid;
|
||||
@@ -94,6 +94,8 @@ bool ImportController::extractConfigFromFile(const QString &fileName)
|
||||
|
||||
bool ImportController::extractConfigFromData(QString data)
|
||||
{
|
||||
m_maliciousWarningText.clear();
|
||||
|
||||
QString config = data;
|
||||
QString prefix;
|
||||
QString errormsg;
|
||||
@@ -658,6 +660,7 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
|
||||
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
|
||||
|
||||
QString protocolConfig =
|
||||
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
|
||||
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString();
|
||||
@@ -679,8 +682,11 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
|
||||
}
|
||||
}
|
||||
|
||||
m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious "
|
||||
"scripts, so only add it if you fully trust the provider of this config. ");
|
||||
|
||||
if (maliciousStrings.size() >= dangerousTagsMaxCount) {
|
||||
m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:");
|
||||
m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:"));
|
||||
for (const auto &string : maliciousStrings) {
|
||||
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "languageModel.h"
|
||||
|
||||
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: m_settings(settings), QAbstractListModel(parent)
|
||||
LanguageModel::LanguageModel(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent)
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<LanguageSettings::AvailableLanguageEnum>();
|
||||
for (int i = 0; i < metaEnum.keyCount(); i++) {
|
||||
m_availableLanguages.push_back(
|
||||
LanguageModelData {getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
||||
m_availableLanguages.push_back(LanguageModelData { getLocalLanguageName(static_cast<LanguageSettings::AvailableLanguageEnum>(i)),
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(i) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +48,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
|
||||
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return strLanguage;
|
||||
@@ -104,11 +101,12 @@ QString LanguageModel::getCurrentLanguageName()
|
||||
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
||||
}
|
||||
|
||||
QString LanguageModel::getCurrentSiteUrl()
|
||||
QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
||||
{
|
||||
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||
switch (language) {
|
||||
case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/amnezia/amnezia.org";
|
||||
default: return "https://amnezia.org";
|
||||
case LanguageSettings::AvailableLanguageEnum::Russian:
|
||||
return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
|
||||
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public slots:
|
||||
int getCurrentLanguageIndex();
|
||||
int getLineHeightAppend();
|
||||
QString getCurrentLanguageName();
|
||||
QString getCurrentSiteUrl();
|
||||
QString getCurrentSiteUrl(const QString &path = "");
|
||||
|
||||
signals:
|
||||
void updateTranslations(const QLocale &locale);
|
||||
|
||||
@@ -29,7 +29,7 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/premium")
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ PageType {
|
||||
text: qsTr("Privacy Policy")
|
||||
|
||||
clickedFunc: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("policy"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ PageType {
|
||||
leftImageSource: "qrc:/images/controls/help-circle.svg"
|
||||
|
||||
onClicked: {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide")
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ PageType {
|
||||
height: containers.contentItem.height
|
||||
spacing: 16
|
||||
|
||||
currentIndex: 1
|
||||
currentIndex: 0
|
||||
clip: true
|
||||
interactive: false
|
||||
model: proxyContainersModel
|
||||
|
||||
@@ -351,8 +351,10 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
sitesJsonArray.append(site);
|
||||
}
|
||||
|
||||
// Allow traffic to Amnezia DNS
|
||||
if (sitesRouteMode == Settings::VpnOnlyForwardSites) {
|
||||
if (sitesJsonArray.isEmpty()) {
|
||||
sitesRouteMode = Settings::RouteMode::VpnAllSites;
|
||||
} else if (sitesRouteMode == Settings::VpnOnlyForwardSites) {
|
||||
// Allow traffic to Amnezia DNS
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
|
||||
}
|
||||
@@ -371,6 +373,10 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
for (const auto &app : apps) {
|
||||
appsJsonArray.append(app.appPath.isEmpty() ? app.packageName : app.appPath);
|
||||
}
|
||||
|
||||
if (appsJsonArray.isEmpty()) {
|
||||
appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
|
||||
}
|
||||
}
|
||||
|
||||
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);
|
||||
|
||||
Reference in New Issue
Block a user