mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-07-03 14:07:39 +02:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d85e4a413a | |||
| fbf11c1689 | |||
| b382257460 | |||
| 8ec4232a96 | |||
| 43fd9d5d90 | |||
| ae681ad6d2 | |||
| 6c22a7372d | |||
| e7b25719e4 | |||
| 7183e8541c | |||
| 9e71e64cbd | |||
| 4f3bae4a9a | |||
| 990059f8a6 | |||
| af55af5e76 | |||
| 82d96a9691 | |||
| 9f3f215452 | |||
| 2dfc6a87b8 | |||
| 7261a86c48 | |||
| 2946dd2278 | |||
| 5065262aac | |||
| 4685d3b543 | |||
| 7a389e8755 | |||
| 4e5daf22a3 | |||
| 928c4f18c9 |
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.8.2.0
|
||||
project(${PROJECT} VERSION 4.8.2.3
|
||||
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 2067)
|
||||
set(APP_ANDROID_VERSION_CODE 2069)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -10,21 +10,17 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
|
||||
|
||||
<br>
|
||||
|
||||
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_Linux_4.7.0.0.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.7.0.0"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
|
||||
|
||||
<br>
|
||||
|
||||
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download.png" width="150" style="max-width: 100%;"></a>
|
||||
<a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/play.png" width="150" style="max-width: 100%;"></a>
|
||||
<a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/apl.png" width="150" style="max-width: 100%;"></a>
|
||||
|
||||
[Alternative download link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org/downloads)
|
||||
|
||||
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||
|
||||
<br>
|
||||
|
||||
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
|
||||
|
||||
## Features
|
||||
|
||||
@@ -37,7 +33,7 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
|
||||
|
||||
## Links
|
||||
|
||||
- [https://amnezia.org](https://amnezia.org) - project website
|
||||
- [https://amnezia.org](https://amnezia.org) - project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org)
|
||||
- [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_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
|
||||
|
||||
@@ -25,7 +25,6 @@ execute_process(
|
||||
add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||
|
||||
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
|
||||
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
|
||||
add_definitions(-DPROD_S3_ENDPOINT="$ENV{PROD_S3_ENDPOINT}")
|
||||
|
||||
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace
|
||||
constexpr char userCountryCode[] = "user_country_code";
|
||||
constexpr char serverCountryCode[] = "server_country_code";
|
||||
constexpr char serviceType[] = "service_type";
|
||||
constexpr char serviceInfo[] = "service_info";
|
||||
|
||||
constexpr char aesKey[] = "aes_key";
|
||||
constexpr char aesIv[] = "aes_iv";
|
||||
@@ -163,6 +164,11 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
|
||||
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||
|
||||
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
|
||||
}
|
||||
|
||||
serverConfig[configKey::apiConfig] = apiConfig;
|
||||
|
||||
return;
|
||||
@@ -383,7 +389,6 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||
QThread::msleep(10);
|
||||
#endif
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(7000);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
@@ -399,7 +404,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||
}
|
||||
apiPayload[configKey::serviceType] = serviceType;
|
||||
apiPayload[configKey::uuid] = installationUuid;
|
||||
apiPayload[configKey::authData] = authData;
|
||||
if (!authData.isEmpty()) {
|
||||
apiPayload[configKey::authData] = authData;
|
||||
}
|
||||
|
||||
QSimpleCrypto::QBlockCipher blockCipher;
|
||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||
@@ -441,7 +448,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||
|
||||
QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||
QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
|
||||
QEventLoop wait;
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
@@ -452,7 +459,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||
|
||||
auto encryptedResponseBody = reply->readAll();
|
||||
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true)) {
|
||||
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
m_proxyUrls = getProxyUrls();
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
@@ -461,14 +468,14 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
||||
qDebug() << "Go to the next endpoint";
|
||||
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
reply = manager.post(request, QJsonDocument(requestBody).toJson());
|
||||
reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
|
||||
encryptedResponseBody = reply->readAll();
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, false)) {
|
||||
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,6 +770,12 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||
|
||||
if (!stdOut.contains("sudo"))
|
||||
return ErrorCode::ServerUserNotInSudo;
|
||||
if (stdOut.contains("command not found"))
|
||||
return ErrorCode::SudoPackageIsNotPreinstalled;
|
||||
if (stdOut.contains("sudoers"))
|
||||
return ErrorCode::ServerUserNotListedInSudoers;
|
||||
if (stdOut.contains("password is required"))
|
||||
return ErrorCode::ServerUserPasswordRequired;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ namespace amnezia
|
||||
ServerCancelInstallation = 204,
|
||||
ServerUserNotInSudo = 205,
|
||||
ServerPacketManagerError = 206,
|
||||
SudoPackageIsNotPreinstalled = 207,
|
||||
ServerUserNotListedInSudoers = 208,
|
||||
ServerUserPasswordRequired = 209,
|
||||
|
||||
// Ssh connection errors
|
||||
SshRequestDeniedError = 300,
|
||||
|
||||
@@ -21,6 +21,9 @@ QString errorString(ErrorCode code) {
|
||||
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
|
||||
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
|
||||
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
|
||||
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
|
||||
case(ErrorCode::ServerUserNotListedInSudoers): errorMessage = QObject::tr("The user is not listed in sudoers"); break;
|
||||
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
|
||||
|
||||
// Libssh errors
|
||||
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
echo $LC_MESSAGES | grep -qE "en_US.UTF-8|C.UTF-8" || export LC_MESSAGES=C.UTF-8;\
|
||||
CUR_USER=$(whoami);\
|
||||
groups $CUR_USER
|
||||
groups $CUR_USER | grep sudo && sudo -nu $CUR_USER sudo -n uname > /dev/null
|
||||
|
||||
@@ -847,6 +847,8 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
|
||||
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
|
||||
|
||||
newServerConfig.insert(configKey::apiConfig, newApiConfig);
|
||||
newServerConfig.insert(configKey::authData, authData);
|
||||
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
|
||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||
|
||||
if (reloadServiceConfig) {
|
||||
|
||||
@@ -39,6 +39,9 @@ QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
||||
case CountryNameRole: {
|
||||
return countryInfo.value(configKey::serverCountryName).toString();
|
||||
}
|
||||
case CountryImageCodeRole: {
|
||||
return countryInfo.value(configKey::serverCountryCode).toString().toUpper();
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -76,5 +79,6 @@ QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[CountryNameRole] = "countryName";
|
||||
roles[CountryCodeRole] = "countryCode";
|
||||
roles[CountryImageCodeRole] = "countryImageCode";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ class ApiCountryModel : public QAbstractListModel
|
||||
public:
|
||||
enum Roles {
|
||||
CountryNameRole = Qt::UserRole + 1,
|
||||
CountryCodeRole
|
||||
CountryCodeRole,
|
||||
CountryImageCodeRole
|
||||
};
|
||||
|
||||
explicit ApiCountryModel(QObject *parent = nullptr);
|
||||
|
||||
@@ -771,5 +771,5 @@ const QString ServersModel::getDefaultServerImagePathCollapsed()
|
||||
if (countryCode.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode);
|
||||
return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper());
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ PageType {
|
||||
Layout.rightMargin: 32
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
source: "qrc:/countriesFlags/images/flagKit/" + countryCode + ".svg"
|
||||
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -772,7 +772,8 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
width: root.width
|
||||
height: root.height
|
||||
|
||||
expandedContent: ColumnLayout {
|
||||
id: expandedContent
|
||||
@@ -783,8 +784,6 @@ PageType {
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 8
|
||||
|
||||
onImplicitHeightChanged: {
|
||||
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
|
||||
}
|
||||
@@ -797,57 +796,54 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: clientName
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: textColumn
|
||||
property string textColor: AmneziaStyle.color.mutedGray
|
||||
Header2TextType {
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: creationDate
|
||||
Layout.fillWidth: true
|
||||
text: clientName
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.Wrap
|
||||
elide: Qt.ElideRight
|
||||
}
|
||||
|
||||
text: qsTr("Creation date: %1").arg(creationDate)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: creationDate
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: latestHandshake
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Creation date: %1").arg(creationDate)
|
||||
}
|
||||
|
||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: latestHandshake
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataReceived
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||
}
|
||||
|
||||
text: qsTr("Data received: %1").arg(dataReceived)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: dataReceived
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataSent
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Data received: %1").arg(dataReceived)
|
||||
}
|
||||
|
||||
text: qsTr("Data sent: %1").arg(dataSent)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: dataSent
|
||||
Layout.fillWidth: true
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: allowedIps
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Data sent: %1").arg(dataSent)
|
||||
}
|
||||
|
||||
text: qsTr("Allowed IPs: %1").arg(allowedIps)
|
||||
}
|
||||
ParagraphTextType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
visible: allowedIps
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Allowed IPs: %1").arg(allowedIps)
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -952,6 +948,7 @@ PageType {
|
||||
BasicButtonType {
|
||||
id: revokeButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user