Compare commits

..

52 Commits

Author SHA1 Message Date
Yaroslav Yashin
deb9511b62 Merge branch 'dev' into fixbug/ios18 2025-09-29 15:24:02 +03:00
vkamn
c66d8ecca0 chore: bump version (#1892) 2025-09-29 11:07:27 +08:00
vkamn
db535f7e7d chore: increase default values (#1891) 2025-09-29 11:05:30 +08:00
vkamn
89f30d8c31 fix: fixed native wg obfuscation (#1890) 2025-09-29 10:58:44 +08:00
Yaroslav
8bce432824 fix: enable paste from clipboard on ios in addition to android (#1868) 2025-09-29 10:56:41 +08:00
MrMirDan
f3539b2632 fix: proper wl name on connection key page (#1867)
* fix: proper wl name on connection key page

* some changings

* little change

* added missing import

* fix: proper wl default filename
2025-09-29 10:55:53 +08:00
MrMirDan
7a96c212f3 fix: rename user in search (#1847) 2025-09-29 10:51:52 +08:00
MrMirDan
2d5dc54e0f fix: keyboard navigation for text fields (#1879) 2025-09-29 10:50:57 +08:00
MrMirDan
cef4c262e9 fix: keyboard fix for api 'connection key' buttons (#1872) 2025-09-29 10:50:18 +08:00
MrMirDan
34309261a8 fix: scrollbar always visible (#1877)
* fix: scrollbar always visible

* fix: scrollbar always visible on app split tunneling page
2025-09-29 10:49:19 +08:00
MrMirDan
657eeb40c7 fix: mirror error code link (#1863)
* fix: mirror error code link

* remake
2025-09-29 10:48:36 +08:00
MrMirDan
b4938c2cc9 fix: default lang matching between app and OS (#1855)
* fix: default lang matching between app and OS

* remake

* fix: set default lang value
2025-09-29 10:47:54 +08:00
MrMirDan
524fefc5cb feat: warning on app split tunneling for windows (#1880) 2025-09-29 10:45:14 +08:00
Yaroslav
73f13404bb feat: add support for multiple scenes and handle URL contexts in iOS 13+ (#1889) 2025-09-29 10:40:58 +08:00
AnhTVc
4188640c1d update CICD 2025-09-21 21:49:22 +03:00
AnhTVc
4ec20e9f37 Update deploy.yml
update artifact: upload ios
2025-09-21 21:49:22 +03:00
AnhTVc
ac61dd1498 Update build_ios.sh
update script build unsigned ipa
2025-09-21 21:49:22 +03:00
AnhTVc
2923fdaaf0 update convension 2025-09-21 21:49:22 +03:00
AnhTVc
ff63cd24e5 update fix
update fix  missing file
2025-09-21 21:49:22 +03:00
AnhTVc
bfa5fe4eb7 fix bug ios 18
fix bug: can not start vpn in ios18
2025-09-21 21:49:22 +03:00
MrMirDan
5fc68cca83 fix: split tunneling restoration from backup (#1835) 2025-09-15 10:55:18 +08:00
Mitternacht822
fcb7b8fa8d fix: save/restore AmneziaDNS state (#1833) 2025-09-15 10:54:34 +08:00
aiamnezia
a81e32ff95 fix: clean service/client logs in uninstall scripts (#1846)
- Windows (x64/x86):
  - Remove delegation to `AmneziaVPN.exe -c`
  - Delete `%ProgramData%\AmneziaVPN\log\AmneziaVPN-service.log`
  - Delete current user logs at `%AppData%\AmneziaVPN.ORG\AmneziaVPN\log`
  - Remove empty parent dirs (app/org, log)

- Linux:
  - Delete only `/var/log/AmneziaVPN/AmneziaVPN-service.log` (preserve `post-uninstall.log`)
  - Delete current user logs at `$HOME/.local/share/AmneziaVPN.ORG/AmneziaVPN/log`
2025-09-15 10:53:51 +08:00
albexk
c897052107 chore: bump version (#1850) 2025-09-10 19:28:36 +08:00
vkamn
4d0efc7ea5 fix: remove duplicate m_vpnConnection delete from AmneziaApplication destructor (#1848) 2025-09-10 15:01:52 +08:00
Ivan
a77842c9e3 feat: add server diagnostics script (#1837)
Co-authored-by: Ivan Istomin <istomin-ms@yandex.ru>
2025-09-09 19:33:35 +08:00
Mitternacht822
0ded9db780 refactor: use QCommandLineOption members for autostart/cleanup (#1820)
* refactor(app options): use QCommandLineOption members for autostart/cleanup

* fix(app): initialize QCommandLineOption members in ctor/field to avoid no-default-ctor build failures
2025-09-03 12:03:45 +08:00
Mitternacht822
58d480fcb5 fix: moved startMinimized to Q_Property (#1819) 2025-09-03 12:03:10 +08:00
aiamnezia
7154428d26 fix: sharing QR code size (#1830) 2025-09-03 11:58:36 +08:00
MrMirDan
02a52d0169 fix: full config default filename (#1831) 2025-09-03 11:57:30 +08:00
MrMirDan
ec60764072 fix: rename/revoke user while in search on share page (#1787)
* fix: revoke user config

* fix: user renaming

* fix: revoke signal

* some fixes

* remaded fix
2025-09-03 11:56:08 +08:00
MrMirDan
17d2fa5532 fix: premium key duplication (#1818)
* ru translation fix

* crc saving

* little fix

* updated crc saving

* fix: added comparison by key

* remaded fix
2025-09-03 11:54:11 +08:00
MrMirDan
3ca8b534e8 fix: go to home page after first protocol manual installation (#1829) 2025-09-03 11:52:45 +08:00
MrMirDan
e88f7c5e46 fix: index assignment (#1821) 2025-09-02 13:03:05 +08:00
MrMirDan
3ac5d7bd1f chore: ru translation update (#1815) 2025-08-27 18:37:43 +08:00
vkamn
19cad00a00 fix: minor ui fixes (#1817)
* fix: minor ui fixes with services list

* fix: fix page share connection headers and config description
2025-08-27 16:42:28 +08:00
vkamn
1ea716a163 fix: fix page share connection headers and config description 2025-08-27 16:41:20 +08:00
vkamn
4551659c2a fix: minor ui fixes with services list 2025-08-27 15:15:53 +08:00
MrMirDan
c568bf8c24 chore: ru translation update (#1812)
* ru translation update

* fixes
2025-08-26 20:32:00 +08:00
vkamn
a412d91105 feat: subscription expiration processing (#1814) 2025-08-26 20:31:41 +08:00
vkamn
ad01f23bbe feat: add service description customization (#1811) 2025-08-26 12:17:37 +08:00
vkamn
656070b132 feat: add request id (#1809) 2025-08-25 22:05:00 +08:00
MrMirDan
c907f5ca36 fix: removed service logs section for mobile platforms (#1810) 2025-08-25 22:04:48 +08:00
Mykola Baibuz
94a13b2b54 fix: set guid for windows tun2socks tun interface (#1808) 2025-08-25 11:03:42 +08:00
MrMirDan
169f11d9c7 chore: added trimming I's and J's params on save (#1774)
* trimming params on save

* removed unused code
2025-08-21 12:29:22 +08:00
vkamn
816dc3af95 feat: add ping before request to proxy (#1805) 2025-08-21 12:28:03 +08:00
Mykola Baibuz
b802863de5 fix: check for empty secondary DNS (#1799) 2025-08-20 14:19:22 +08:00
vkamn
8dc2a4b76c fix: fixed switcher behavior (#1801) 2025-08-20 13:01:09 +08:00
vkamn
beb1c6dbf2 feat: added cache for proxy bypass (#1797) 2025-08-20 13:00:35 +08:00
vkamn
3eb06916c7 chore: bump version (#1802)
* chore: bump version

* fix: fixed ios build
2025-08-20 13:00:20 +08:00
Cyril Anisimov
30d0f84a4f fix: fixed focus view and reverse focus change in headers (#1791)
* fix: add view movement on changing the focus in backwards direction

* fix: return value in isFirstFocusItemInHeader function
2025-08-20 12:59:57 +08:00
Mykola Baibuz
251f2aa5db fix: remove double disconnect for Win IPSec (#1800) 2025-08-20 12:58:39 +08:00
84 changed files with 2218 additions and 1285 deletions

View File

@@ -238,14 +238,13 @@ jobs:
IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }}
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
# uses: actions/upload-artifact@v4
# with:
# name: app-store ipa & dsyms
# path: |
# ${{ github.workspace }}/AmneziaVPN-iOS.ipa
# ${{ github.workspace }}/*.app.dSYM.zip
# retention-days: 7
- name: 'Upload unsigned .ipa to artifacts'
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-unsigned-ipa
path: |
${{ github.workspace }}/build-ios/AmneziaVPN_unsigned.ipa
retention-days: 7
# ------------------------------------------------------

View File

@@ -4,7 +4,7 @@ on:
workflow_dispatch:
# push:
# tags:
# - **
# - "**"
jobs:

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.9.2)
set(AMNEZIAVPN_VERSION 4.8.11.0)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
DESCRIPTION "AmneziaVPN"
@@ -12,7 +12,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 2092)
set(APP_ANDROID_VERSION_CODE 2095)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View File

@@ -25,7 +25,9 @@
#include <QtQuick/QQuickWindow> // for QQuickWindow
#include <QWindow> // for qobject_cast<QWindow*>
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv),
m_optAutostart({QStringLiteral("a"), QStringLiteral("autostart")}, QStringLiteral("System autostart")),
m_optCleanup ({QStringLiteral("c"), QStringLiteral("cleanup")}, QStringLiteral("Cleanup logs"))
{
setQuitOnLastWindowClosed(false);
@@ -51,20 +53,8 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
AmneziaApplication::~AmneziaApplication()
{
if (m_vpnConnection) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
QThread::msleep(2000);
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectSlots", Qt::QueuedConnection);
}
m_vpnConnectionThread.requestInterruption();
m_vpnConnectionThread.quit();
if (!m_vpnConnectionThread.wait(3000)) {
m_vpnConnectionThread.terminate();
m_vpnConnectionThread.wait(500);
}
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
delete m_engine;
@@ -121,7 +111,7 @@ void AmneziaApplication::init()
Logger::setServiceLogsEnabled(enabled);
#ifdef Q_OS_WIN //TODO
if (m_parser.isSet("a"))
if (m_parser.isSet(m_optAutostart))
m_coreController->pageController()->showOnStartup();
else
emit m_coreController->pageController()->raiseMainWindow();
@@ -189,15 +179,12 @@ bool AmneziaApplication::parseCommands()
m_parser.addHelpOption();
m_parser.addVersionOption();
QCommandLineOption c_autostart { { "a", "autostart" }, "System autostart" };
m_parser.addOption(c_autostart);
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
m_parser.addOption(c_cleanup);
m_parser.addOption(m_optAutostart);
m_parser.addOption(m_optCleanup);
m_parser.process(*this);
if (m_parser.isSet(c_cleanup)) {
if (m_parser.isSet(m_optCleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();

View File

@@ -56,6 +56,9 @@ private:
QCommandLineParser m_parser;
QCommandLineOption m_optAutostart;
QCommandLineOption m_optCleanup;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;

View File

@@ -46,6 +46,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/AmneziaSceneDelegateHooks.mm
)

View File

@@ -23,7 +23,7 @@ namespace
bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate)
{
QDateTime now = QDateTime::currentDateTime();
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs);
return endDate < now;
}

View File

@@ -26,9 +26,8 @@ CoreController::CoreController(const QSharedPointer<VpnConnection> &vpnConnectio
initNotificationHandler();
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
updateTranslator(m_settings->getAppLanguage());
}
void CoreController::initModels()
@@ -233,7 +232,7 @@ void CoreController::initSignalHandlers()
void CoreController::initNotificationHandler()
{
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),

View File

@@ -5,7 +5,7 @@
#include <QQmlContext>
#include <QThread>
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/systemtray_notificationhandler.h"
#endif
@@ -48,7 +48,7 @@
#include "ui/models/services/socks5ProxyConfigModel.h"
#include "ui/models/sites_model.h"
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
#include "ui/notificationhandler.h"
#endif
@@ -97,7 +97,7 @@ private:
QSharedPointer<VpnConnection> m_vpnConnection;
QSharedPointer<QTranslator> m_translator;
#ifndef Q_OS_ANDROID
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
QScopedPointer<NotificationHandler> m_notificationHandler;
#endif

View File

@@ -60,8 +60,9 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
request.setUrl(QString(endpoint).arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
@@ -122,8 +123,9 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
QNetworkRequest request;
request.setTransferTimeout(m_requestTimeoutMsecs);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8());
request.setUrl(endpoint.arg(m_gatewayEndpoint));
request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
// bypass killSwitch exceptions for API-gateway
#ifdef AMNEZIA_DESKTOP
@@ -344,11 +346,14 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
std::mt19937 generator(randomDevice());
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
QEventLoop wait;
QList<QSslError> sslErrors;
QByteArray responseBody;
for (const QString &proxyUrl : proxyUrls) {
auto bypassFunction = [this](const QString &endpoint, const QString &proxyUrl, QNetworkReply *reply,
std::function<QNetworkReply *(const QString &url)> requestFunction,
std::function<bool(QNetworkReply * reply, const QList<QSslError> &sslErrors)> replyProcessingFunction) {
QEventLoop wait;
QList<QSslError> sslErrors;
qDebug() << "go to the next proxy endpoint";
reply->deleteLater(); // delete the previous reply
reply = requestFunction(endpoint.arg(proxyUrl));
@@ -358,6 +363,50 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
wait.exec();
if (replyProcessingFunction(reply, sslErrors)) {
return true;
}
return false;
};
if (m_proxyUrl.isEmpty()) {
QNetworkRequest request;
request.setTransferTimeout(1000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
for (const QString &proxyUrl : proxyUrls) {
request.setUrl(proxyUrl + "lmbd-health");
reply = amnApp->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() == QNetworkReply::NetworkError::NoError) {
reply->deleteLater();
m_proxyUrl = proxyUrl;
if (!m_proxyUrl.isEmpty()) {
break;
}
} else {
reply->deleteLater();
}
}
}
if (!m_proxyUrl.isEmpty()) {
if (bypassFunction(endpoint, m_proxyUrl, reply, requestFunction, replyProcessingFunction)) {
return;
}
}
for (const QString &proxyUrl : proxyUrls) {
if (bypassFunction(endpoint, proxyUrl, reply, requestFunction, replyProcessingFunction)) {
m_proxyUrl = proxyUrl;
break;
}
}

View File

@@ -32,6 +32,8 @@ private:
QString m_gatewayEndpoint;
bool m_isDevEnvironment = false;
bool m_isStrictKillSwitchEnabled = false;
inline static QString m_proxyUrl;
};
#endif // GATEWAYCONTROLLER_H

View File

@@ -120,6 +120,7 @@ namespace amnezia
ApiNotFoundError = 1109,
ApiMigrationError = 1110,
ApiUpdateRequestError = 1111,
ApiSubscriptionExpiredError = 1112,
// QFile errors
OpenError = 1200,

View File

@@ -77,6 +77,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break;
case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break;
case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break;
// QFile errors
case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;

View File

@@ -85,9 +85,8 @@ bool IpcClient::init(IpcClient *instance)
}
qDebug() << "IpcClient::init succeed";
instance->m_isSocketConnected = (Instance()->m_ipcClient->isReplicaValid() && Instance()->m_Tun2SocksClient->isReplicaValid());
return Instance()->isSocketConnected();
return (Instance()->m_ipcClient->isReplicaValid() && Instance()->m_Tun2SocksClient->isReplicaValid());
}
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()

View File

@@ -101,10 +101,10 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
out << "MTU = " << m_deviceMTU << "\n";
}
if (!m_primaryDnsServer.isNull()) {
if (!m_primaryDnsServer.isEmpty()) {
QStringList dnsServers;
dnsServers.append(m_primaryDnsServer);
if (!m_secondaryDnsServer.isNull()) {
if (!m_secondaryDnsServer.isEmpty()) {
dnsServers.append(m_secondaryDnsServer);
}
// If the DNS is not the Gateway, it's a user defined DNS

View File

@@ -32,17 +32,41 @@
<false/>
<key>UILaunchStoryboardName</key>
<string>AmneziaVPNLaunchScreen</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>QIOSWindowSceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array/>
<key>UIRequiresFullScreen</key>
<true/>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array/>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>com.wireguard.ios.app_group_id</key>

View File

@@ -264,13 +264,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
&& !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
// && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined()
// && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined()
&& !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
/* && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined()
@@ -278,27 +278,27 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
&& !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined()
&& !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined()
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) {
&& !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()*/) {
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize));
json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize));
json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize));
json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
// json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize));
// json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize));
json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader));
json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader));
json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader));
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
// json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1));
// json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2));
// json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3));
// json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4));
// json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5));
// json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1));
// json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2));
// json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3));
// json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout));
}
write(json);

View File

@@ -0,0 +1,82 @@
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#include <dispatch/dispatch.h>
#include <QByteArray>
#include <QFile>
#include <QString>
#include "ios_controller.h"
using SceneOpenURLContexts = void (*)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *);
static SceneOpenURLContexts g_originalSceneOpenURLContexts = nullptr;
static void amnezia_handleURL(NSURL *url)
{
if (!url || !url.isFileURL) {
return;
}
QString filePath(url.path.UTF8String);
if (filePath.isEmpty()) {
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (filePath.contains("backup")) {
IosController::Instance()->importBackupFromOutside(filePath);
return;
}
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
return;
}
const QByteArray data = file.readAll();
IosController::Instance()->importConfigFromOutside(QString::fromUtf8(data));
});
}
static void amnezia_scene_openURLContexts(id self, SEL _cmd, UIScene *scene, NSSet<UIOpenURLContext *> *contexts)
{
if (g_originalSceneOpenURLContexts) {
g_originalSceneOpenURLContexts(self, _cmd, scene, contexts);
}
if (!contexts || contexts.count == 0) {
return;
}
if (@available(iOS 13.0, *)) {
for (UIOpenURLContext *context in contexts) {
amnezia_handleURL(context.URL);
}
}
}
@interface AmneziaSceneDelegateHooks : NSObject
@end
@implementation AmneziaSceneDelegateHooks
+ (void)load
{
Class cls = objc_getClass("QIOSWindowSceneDelegate");
if (!cls) {
return;
}
SEL selector = @selector(scene:openURLContexts:);
Method method = class_getInstanceMethod(cls, selector);
if (method) {
g_originalSceneOpenURLContexts = reinterpret_cast<SceneOpenURLContexts>(method_getImplementation(method));
method_setImplementation(method, reinterpret_cast<IMP>(amnezia_scene_openURLContexts));
} else {
const char *types = "v@:@@";
class_addMethod(cls, selector, reinterpret_cast<IMP>(amnezia_scene_openURLContexts), types);
}
}
@end

View File

@@ -73,7 +73,7 @@ extension PacketTunnelProvider {
startHandler = completionHandler
ovpnAdapter?.connect(using: packetFlow)
}
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
@@ -169,8 +169,20 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate {
networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes
networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
}
}
if splitTunnelType == 0 || splitTunnelType == nil {
// Full tunnel: send all traffic via VPN
if let ipv4Settings = networkSettings?.ipv4Settings {
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
NSLog("[Route] Added default IPv4 route (0.0.0.0/0)")
}
if let ipv6Settings = networkSettings?.ipv6Settings {
let ipv6DefaultRoute = NEIPv6Route(destinationAddress: "::", networkPrefixLength: 0)
ipv6Settings.includedRoutes = [ipv6DefaultRoute]
NSLog("[Route] Added default IPv6 route (::/0)")
}
}
}
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}

View File

@@ -27,17 +27,49 @@ const char* MessageKey::isOnDemand = "is-on-demand";
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
#if !MACOS_NE
static UIViewController* getViewController() {
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
UIApplication *application = [UIApplication sharedApplication];
if (@available(iOS 13.0, *)) {
for (UIScene *scene in application.connectedScenes) {
if (scene.activationState != UISceneActivationStateForegroundActive) {
continue;
}
if (![scene isKindOfClass:[UIWindowScene class]]) {
continue;
}
UIWindowScene *windowScene = (UIWindowScene *)scene;
for (UIWindow *window in windowScene.windows) {
if (window.isKeyWindow && window.rootViewController) {
return window.rootViewController;
}
}
for (UIWindow *window in windowScene.windows) {
if (!window.isHidden && window.rootViewController) {
return window.rootViewController;
}
}
}
}
for (UIWindow *window in application.windows) {
if (window.isKeyWindow && window.rootViewController) {
return window.rootViewController;
}
}
for (UIWindow *window in application.windows) {
if (window.rootViewController) {
return window.rootViewController;
}
}
return nil;
}
#endif
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
switch (status) {
@@ -128,6 +160,39 @@ bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configur
m_rawConfig = configuration;
m_serverAddress = configuration.value(config_key::hostName).toString().toNSString();
if (proto == amnezia::Proto::OpenVpn) {
QJsonObject ovpn = configuration["openvpn_config_data"].toObject();
QString ovpnConfig = ovpn["config"].toString();
QStringList unsupportedDirectives = {
"resolv-retry",
"persist-key",
"persist-tun",
"block-ipv6",
"redirect-gateway"
};
QStringList lines = ovpnConfig.split('\n');
QStringList filteredLines;
for (const QString &line : lines) {
QString trimmedLine = line.trimmed();
bool shouldIgnore = false;
for (const QString &bad : unsupportedDirectives) {
if (trimmedLine.startsWith(bad)) {
shouldIgnore = true;
break;
}
}
if (!shouldIgnore) {
filteredLines.append(line);
}
}
ovpnConfig = filteredLines.join("\n");
ovpn["config"] = ovpnConfig;
m_rawConfig["openvpn_config_data"] = ovpn;
}
QString tunnelName;
if (configuration.value(config_key::description).toString().isEmpty()) {
tunnelName = QString("%1 %2")
@@ -251,21 +316,6 @@ void IosController::checkStatus()
sendVpnExtensionMessage(message, [&](NSDictionary* response){
uint64_t txBytes = [response[@"tx_bytes"] intValue];
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
uint64_t last_handshake_time_sec = 0;
#if !MACOS_NE
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
} else {
qDebug() << "Key last_handshake_time_sec is missing or null";
}
if (last_handshake_time_sec < 0) {
disconnectVpn();
qDebug() << "Invalid handshake time, disconnecting VPN.";
}
#endif
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
m_rxBytes = rxBytes;
m_txBytes = txBytes;
@@ -524,8 +574,6 @@ bool IosController::setupWireGuard()
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
@@ -624,23 +672,11 @@ bool IosController::setupAwg()
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]);
wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]);
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]);
wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]);
wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]);
wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]);
wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]);
wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]);
wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]);
wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]);
wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]);
QJsonDocument wgConfigDoc(wgConfig);
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
@@ -820,14 +856,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
[sharingItems addObject:logFileUrl];
}
#if !MACOS_NE
UIViewController *qtController = getViewController();
if (!qtController) return;
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
#endif
__block bool isAccepted = false;
#if !MACOS_NE
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
isAccepted = completed;
emit finished();
@@ -839,17 +875,15 @@ bool IosController::shareText(const QStringList& filesToSend) {
popController.sourceView = qtController.view;
popController.sourceRect = CGRectMake(100, 100, 100, 100);
}
#endif
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return isAccepted;
}
QString IosController::openFile() {
#if !MACOS_NE
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
@@ -859,10 +893,9 @@ QString IosController::openFile() {
if (!qtController) return;
[qtController presentViewController:documentPicker animated:YES completion:nil];
#endif
__block QString filePath;
#if !MACOS_NE
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
if (path) {
filePath = QString::fromUtf8(path.UTF8String);
@@ -871,11 +904,11 @@ QString IosController::openFile() {
}
emit finished();
};
#endif
QEventLoop wait;
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return filePath;
}

View File

@@ -30,7 +30,6 @@ Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent)
Ikev2Protocol::~Ikev2Protocol()
{
qDebug() << "IpsecProtocol::~IpsecProtocol()";
disconnect_vpn();
Ikev2Protocol::stop();
}
@@ -38,7 +37,7 @@ void Ikev2Protocol::stop()
{
setConnectionState(Vpn::ConnectionState::Disconnecting);
{
if (! disconnect_vpn() ){
if (!disconnect_vpn()){
qDebug()<<"We don't disconnect";
setConnectionState(Vpn::ConnectionState::Error);
}
@@ -311,7 +310,9 @@ bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Ikev2Protocol::disconnect_vpn(){
if ( hRasConn != nullptr ){
if ( RasHangUp(hRasConn) != ERROR_SUCCESS)
auto ret = RasHangUp(hRasConn);
qDebug() << "RasHangUp " << ret;
if (ret != ERROR_SUCCESS)
return false;
}
QThread::msleep(3000);

View File

@@ -56,8 +56,7 @@ void OpenVpnProtocol::stop()
}
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
QRemoteObjectPendingReply<bool> disableKillSwitchResp = IpcClient::Interface()->disableKillSwitch();
disableKillSwitchResp.waitForFinished(1000);
IpcClient::Interface()->disableKillSwitch();
#endif
setConnectionState(Vpn::ConnectionState::Disconnected);

View File

@@ -167,10 +167,8 @@ ErrorCode XrayProtocol::startTun2Sock()
void XrayProtocol::stop()
{
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
QRemoteObjectPendingReply<bool> disableKillSwitchResp = IpcClient::Interface()->disableKillSwitch();
disableKillSwitchResp.waitForFinished(1000);
QRemoteObjectPendingReply<bool> StartRoutingIpv6Resp = IpcClient::Interface()->StartRoutingIpv6();
StartRoutingIpv6Resp.waitForFinished(1000);
IpcClient::Interface()->disableKillSwitch();
IpcClient::Interface()->StartRoutingIpv6();
#endif
qDebug() << "XrayProtocol::stop()";
m_xrayProcess.disconnect();
@@ -178,7 +176,6 @@ void XrayProtocol::stop()
m_xrayProcess.waitForFinished(3000);
if (m_t2sProcess) {
m_t2sProcess->stop();
QThread::msleep(200);
}
setConnectionState(Vpn::ConnectionState::Disconnected);

View File

@@ -0,0 +1,519 @@
#!/bin/sh
LOG_DATE=$(date -u +'%Y%m%d-%H%M%S')
SCRIPT_DIR=$(dirname "$0")
LOG_FILE="${SCRIPT_DIR}/server-diagnostics-${LOG_DATE}.log"
# Logging function (sh compatible)
log_and_display() {
if [ "$1" = "-n" ]; then
shift
printf "%s" "$*" | tee -a "$LOG_FILE"
else
echo "$1" | tee -a "$LOG_FILE"
fi
}
# Redirect stderr to stdout for logging
exec 2>&1
header() {
log_and_display ""
log_and_display "=== $1 ==="
}
# Pause for cancellation
log_and_display ""
log_and_display "VPN Server Diagnostics will start in 9s. Press Ctrl+C to cancel."
sleep 9
log_and_display ""
header "STARTING VPN SERVER DIAGNOSTICS"
log_and_display ""
# ------------------------------------------------------------------------------
# 1. Basic system information
# ------------------------------------------------------------------------------
header "System Information"
# Uptime
UPTIME_STR=$(awk '{printf "%d:%02d:%02d", int($1/3600), int(($1%3600)/60), int($1%60)}' /proc/uptime 2>/dev/null || echo "unknown")
log_and_display "Uptime (H:M:S): $UPTIME_STR"
# Date/time UTC
DATE_UTC=$(date -u +'%d %b %Y|%T' 2>/dev/null || echo "unknown")
log_and_display "Date|Time (UTC): $DATE_UTC"
# Init system (PID 1)
INIT_NAME=$(cat /proc/1/status 2>/dev/null | head -1 | awk '{print $2}' 2>/dev/null || echo "unknown")
log_and_display "Init system (PID 1): $INIT_NAME"
# Locale
if echo "$LANG" | grep -E '^(en_US.UTF-8|C.UTF-8|C)$' >/dev/null 2>&1; then
log_and_display "Locale: $LANG"
else
log_and_display "Locale: $LANG (not en_US.UTF-8, C.UTF-8 or C)"
fi
# ------------------------------------------------------------------------------
# 2. Package manager detection
# ------------------------------------------------------------------------------
header "Package Manager Information"
if command -v apt-get >/dev/null 2>&1; then
log_and_display "Package Manager: APT"
PM="apt-get"
PM_VER_OPT="--version"
DOCKER_PKG="docker.io"
elif command -v dnf >/dev/null 2>&1; then
log_and_display "Package Manager: DNF"
PM="dnf"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v yum >/dev/null 2>&1; then
log_and_display "Package Manager: YUM"
PM="yum"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v zypper >/dev/null 2>&1; then
log_and_display "Package Manager: ZYPPER"
PM="zypper"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v pacman >/dev/null 2>&1; then
log_and_display "Package Manager: PACMAN"
PM="pacman"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
elif command -v opkg >/dev/null 2>&1; then
log_and_display "Package Manager: OPKG - Not supported on this platform"
PM="opkg"
PM_VER_OPT="--version"
DOCKER_PKG="docker"
else
log_and_display "Package Manager: Unknown"
# fallback
PM="uname"
PM_VER_OPT="-a"
DOCKER_PKG="docker"
fi
# Check package versions
log_and_display ""
log_and_display "Package versions:"
# Check sudo
if [ "$PM" = "apt-get" ]; then
sudo_version=$(dpkg -s "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
sudo_version=$(rpm -q "sudo" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
sudo_version=$(pacman -Q "sudo" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
sudo_version=$(opkg info "sudo" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
sudo_version="unknown"
fi
log_and_display " sudo: $sudo_version"
# Check Docker package
if [ "$PM" = "apt-get" ]; then
docker_pkg_version=$(dpkg -s "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
docker_pkg_version=$(rpm -q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
docker_pkg_version=$(pacman -Q "$DOCKER_PKG" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
docker_pkg_version=$(opkg info "$DOCKER_PKG" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
docker_pkg_version="unknown"
fi
log_and_display " $DOCKER_PKG: $docker_pkg_version"
# Check lsof
if [ "$PM" = "apt-get" ]; then
lsof_version=$(dpkg -s "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
elif [ "$PM" = "dnf" ] || [ "$PM" = "yum" ] || [ "$PM" = "zypper" ]; then
lsof_version=$(rpm -q "lsof" 2>/dev/null || echo "not installed")
elif [ "$PM" = "pacman" ]; then
lsof_version=$(pacman -Q "lsof" 2>/dev/null || echo "not installed")
elif [ "$PM" = "opkg" ]; then
lsof_version=$(opkg info "lsof" 2>/dev/null | grep '^Version:' | awk '{print $2}' || echo "not installed")
else
lsof_version="unknown"
fi
log_and_display " lsof: $lsof_version"
# ------------------------------------------------------------------------------
# 3. Additional system information (hostnamectl / /proc/version)
# ------------------------------------------------------------------------------
header "OS / Kernel Information"
if command -v hostnamectl >/dev/null 2>&1; then
hostnamectl 2>/dev/null | grep -E 'Operating System:|Virtualization:|Kernel:|Architecture:' | sed 's/^[ \t]*//;s/:/: /' | while read line; do
log_and_display " $line"
done
else
log_and_display "Operating System: $(cat /proc/version 2>/dev/null || echo 'unknown')"
fi
# CPU threads
CPU_THREADS=$(nproc 2>/dev/null || grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo "unknown")
log_and_display " CPU threads: $CPU_THREADS"
# ------------------------------------------------------------------------------
# 4. Memory (RAM) check
# ------------------------------------------------------------------------------
header "Memory Information"
if command -v free >/dev/null 2>&1; then
# Remove extra spaces in header
free -h 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
elif command -v vmstat >/dev/null 2>&1; then
vmstat -S M -s 2>/dev/null | grep -iE 'total memory|total swap' | sed 's/ *//' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
else
grep -iE 'MemTotal|SwapTotal' /proc/meminfo 2>/dev/null | sed 's/ \+/ /' | tee -a "$LOG_FILE" || log_and_display " Error getting memory info"
fi
if command -v free >/dev/null 2>&1; then
log_and_display ""
log_and_display "Detailed Memory Info:"
free -h 2>/dev/null | awk 'NR==2{printf " Used: %s / %s (%.1f%%)\n", $3, $2, $3/$2*100}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating memory usage"
free -h 2>/dev/null | awk 'NR==3{printf " Swap: %s / %s (%.1f%%)\n", $3, $2, $2>0 ? $3/$2*100 : 0}' 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error calculating swap usage"
fi
# Disk usage
header "Disk Usage"
df -h 2>/dev/null | awk '
BEGIN {print " Filesystem Size Used Avail Use% Mounted"}
NR>1 {printf " %-10s %5s %5s %5s %4s %s\n", $1, $2, $3, $4, $5, $6}' | tee -a "$LOG_FILE" || log_and_display " Error getting disk usage"
# ------------------------------------------------------------------------------
# 5. Current user and sudo check
# ------------------------------------------------------------------------------
header "User Check"
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///')
USER_GROUP=$(groups "$CUR_USER" 2>/dev/null || echo "")
USER_GOOD=0
log_and_display -n "Current user: $CUR_USER => "
if [ "$CUR_USER" = "root" ]; then
log_and_display "passed.. (is root)"
USER_GOOD="r" # root
else
if echo "$USER_GROUP" | grep -qE '(^|[[:space:]])sudo($|[[:space:]])'; then
log_and_display "passed.. (in sudo group)"
USER_GOOD=1
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])wheel($|[[:space:]])'; then
log_and_display "passed.. (in wheel group)"
USER_GOOD=1
elif echo "$USER_GROUP" | grep -qE '(^|[[:space:]])docker($|[[:space:]])'; then
log_and_display "failed.. (only in docker group)"
USER_GOOD="d"
else
log_and_display "failed.. (not a member of the sudo or wheel groups)"
USER_GOOD=0
fi
fi
# Check if password is required for sudo
if [ "$USER_GOOD" = "0" ] || [ "$USER_GOOD" = "d" ]; then
log_and_display -n "Passwd request: "
log_and_display "check skipped (not sudoer)"
else
if command -v sudo >/dev/null 2>&1; then
# Try sudo without password - more thorough check
PASSWD_REQUEST=$(sudo -K 2>&1 && sudo -nu $CUR_USER $PM $PM_VER_OPT 2>&1 >/dev/null && sudo -n $PM $PM_VER_OPT 2>&1 >/dev/null)
if [ -n "$PASSWD_REQUEST" ]; then
USER_GOOD=0
log_and_display -n "Passwd request: "
log_and_display "failed.. ($PASSWD_REQUEST)" \
| sed "s/$CUR_USER/User/g;s/$(hostname 2>/dev/null || echo 'Server')/Server/g;s/ user / /g"
else
log_and_display -n "Passwd request: "
log_and_display "passed.. (not required)"
fi
else
if [ "$USER_GOOD" = "r" ]; then
log_and_display -n "Passwd request: "
log_and_display "check skipped (sudo not installed, but root user)"
else
log_and_display "Warning! The sudo package must be pre-installed!"
USER_GOOD=0
fi
fi
fi
# Home directory check
log_and_display -n "Home dir: "
if cd ~ 2>/dev/null; then
log_and_display "passed.. (accessible)"
else
log_and_display "failed.. (not accessible)"
fi
log_and_display "Default shell: $SHELL"
# ------------------------------------------------------------------------------
# 6. Important components check (sudo, lsof, fuser, apparmor)
# ------------------------------------------------------------------------------
header "Component Checks"
log_and_display -n " sudo: "
if command -v sudo >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "not installed"
fi
log_and_display -n " lsof: "
if command -v lsof >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "not installed"
fi
log_and_display -n " fuser: "
if command -v fuser >/dev/null 2>&1; then
log_and_display "passed.. (installed)"
else
log_and_display "psmisc not installed"
fi
log_and_display -n "apparmor: "
AA_ENABLED=$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null || echo "N")
if [ "$AA_ENABLED" = "Y" ]; then
if command -v apparmor_parser >/dev/null 2>&1; then
log_and_display "passed.. (used)"
else
log_and_display "failed.. (installation required)"
fi
else
if command -v apparmor_parser >/dev/null 2>&1; then
log_and_display "passed.. (not used)"
else
log_and_display "passed.. (not required)"
fi
fi
# ------------------------------------------------------------------------------
# 7. SELinux check
# ------------------------------------------------------------------------------
header "SELinux Check"
if command -v getenforce >/dev/null 2>&1; then
SELINUX_STATUS=$(getenforce 2>/dev/null || echo "unknown")
if [ "$SELINUX_STATUS" = "Enforcing" ]; then
log_and_display "SELinux status: $SELINUX_STATUS (strict mode)"
elif [ "$SELINUX_STATUS" = "Permissive" ]; then
log_and_display "SELinux status: $SELINUX_STATUS (permissive mode)"
else
log_and_display "SELinux status: $SELINUX_STATUS (disabled)"
fi
else
log_and_display "SELinux: not found (or not applicable)"
fi
# ------------------------------------------------------------------------------
# 8. Docker + Docker/Podman service check
# ------------------------------------------------------------------------------
header "Docker / Podman Status"
CHECK_CONTAINERS=0
if ! command -v docker >/dev/null 2>&1; then
log_and_display "Docker: $DOCKER_PKG not installed"
else
# If user is in sudoers, use sudo without password
if [ "$USER_GOOD" = "1" ]; then
SUD="sudo -n"
elif [ "$USER_GOOD" = "r" ]; then
SUD="" # root
else
SUD=""
fi
DOCKER_VERSION=$($SUD docker -v 2>/dev/null || echo 'docker -v error')
log_and_display "Installed: $DOCKER_VERSION"
# Check for podman
if echo "$DOCKER_VERSION" | grep -qi "podman"; then
log_and_display " WARNING: Podman detected - not supported at the moment!"
log_and_display " Podman (podman-docker) is not supported and is installed by mistake"
docker_service="podman.socket"
else
docker_service="docker.service"
fi
log_and_display " service: $docker_service"
# Check status
if command -v systemctl >/dev/null 2>&1; then
docker_status=$(systemctl is-active "$docker_service" 2>/dev/null || echo "unknown")
docker_loading=$(systemctl is-enabled "$docker_service" 2>/dev/null || echo "unknown")
else
docker_status="unknown (systemctl not found)"
docker_loading="unknown"
fi
if [ "$docker_status" = "active" ]; then
log_and_display " status: passed.. ($docker_status)"
CHECK_CONTAINERS=1
else
log_and_display " status: incorrect.. ($docker_status)"
CHECK_CONTAINERS=0
fi
if [ "$docker_loading" = "enabled" ]; then
log_and_display " loading: good (startup $docker_loading)"
else
log_and_display " loading: bad (startup $docker_loading)"
fi
fi
# ------------------------------------------------------------------------------
# 9. Docker pull test + container check with improved Docker Hub verification
# ------------------------------------------------------------------------------
header "Docker Hub: pull hello-world test"
if [ "$CHECK_CONTAINERS" = "1" ] && [ "$USER_GOOD" != "0" ]; then
# First check Docker Hub availability
log_and_display "Checking Docker Hub connectivity..."
# Try to execute docker pull with timeout
if timeout 30 $SUD docker pull docker.io/library/hello-world >/dev/null 2>&1; then
log_and_display "Docker Hub: available"
# Start container for testing
if $SUD docker run --rm docker.io/library/hello-world >/dev/null 2>&1; then
log_and_display "Hello-world container: successfully started and completed"
else
log_and_display "Hello-world container: startup error"
fi
else
log_and_display "Docker Hub: unavailable or blocked (possibly exceeded download limit)"
log_and_display "Docker Hub has download limits, try again later"
fi
log_and_display ""
total_cont=$($SUD docker ps -aq 2>/dev/null | wc -l || echo "0")
active_cont=$($SUD docker ps -q 2>/dev/null | wc -l || echo "0")
amnezia_cont=$($SUD docker ps -a 2>/dev/null | grep -c amnezia || echo "0")
log_and_display "Containers check: Total $total_cont / Active $active_cont / Amnezia $amnezia_cont"
$SUD docker ps -a --format "{{.Names}} ({{.Image}}) ({{.Status}}) ({{.Ports}})" 2>/dev/null | grep amnezia || true
# Peers check
if $SUD docker ps 2>/dev/null | grep -qE '\<(amnezia-awg|amnezia-wireguard)\>'; then
log_and_display ""
log_and_display "Peers check (beta):"
if $SUD docker ps 2>/dev/null | grep -q amnezia-awg; then
AMNEZIA_WG_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-awg | awk '{print $1}' | head -1)
if [ -n "$AMNEZIA_WG_CONTAINER" ]; then
WG_PEERS=$($SUD docker exec -it "$AMNEZIA_WG_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
log_and_display "AmneziaWG peers: $WG_PEERS"
fi
fi
if $SUD docker ps 2>/dev/null | grep -q amnezia-wireguard; then
WIREGUARD_CONTAINER=$($SUD docker ps 2>/dev/null | grep amnezia-wireguard | awk '{print $1}' | head -1)
if [ -n "$WIREGUARD_CONTAINER" ]; then
WG_PEERS=$($SUD docker exec -it "$WIREGUARD_CONTAINER" wg show 2>/dev/null | grep -c 'peer' || echo "0")
log_and_display "WireGuard peers: $WG_PEERS"
fi
fi
fi
else
log_and_display "skipped.."
fi
# ------------------------------------------------------------------------------
# 10. Additional improvements
# ------------------------------------------------------------------------------
#
# 10.1. CPU and memory load check (Load average, top processes)
#
header "CPU & Memory usage (top)"
# Load average (last 1,5,15 minutes)
LOAD_AVG=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' || echo "unknown")
log_and_display "Load average: $LOAD_AVG"
log_and_display ""
log_and_display "Top 5 processes by CPU:"
ps aux 2>/dev/null | sort -k3 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting CPU processes"
log_and_display ""
log_and_display "Top 5 processes by MEM:"
ps aux 2>/dev/null | sort -k4 -nr | head -n 6 | awk '{printf "%s %s %s %s %s\n", $1,$2,$3"%",$4"%",$11}' | column -t 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting MEM processes"
# 10.2. System logs check (latest critical messages)
header "Last 10 critical/error messages (journalctl)"
if command -v journalctl >/dev/null 2>&1; then
journalctl -p 3 -n 10 --no-pager 2>/dev/null | tee -a "$LOG_FILE" || log_and_display " Error getting system logs"
else
log_and_display "journalctl not found (non-systemd system?)"
fi
# 10.3. System package versions check (examples)
# Open ports check
header "Network Ports Check"
if command -v netstat >/dev/null 2>&1; then
log_and_display "Listening ports:"
netstat -tlnp 2>/dev/null | grep LISTEN | head -10 | while read line; do
log_and_display " $line"
done
elif command -v ss >/dev/null 2>&1; then
log_and_display "Listening ports:"
ss -tlnp 2>/dev/null | head -10 | while read line; do
log_and_display " $line"
done
else
log_and_display "netstat/ss not found"
fi
# SSH check
header "SSH Service Check"
if command -v systemctl >/dev/null 2>&1; then
ssh_status=$(systemctl is-active ssh 2>/dev/null || systemctl is-active sshd 2>/dev/null || echo "not found")
if [ "$ssh_status" = "active" ]; then
log_and_display "SSH service: $ssh_status"
else
log_and_display "SSH service: $ssh_status"
fi
else
log_and_display "systemctl not found"
fi
# Time check
header "Time Synchronization"
if command -v timedatectl >/dev/null 2>&1; then
timedatectl status 2>/dev/null | grep -E "System clock|NTP service" | while read line; do
log_and_display " $line"
done
else
log_and_display " System time: $(date 2>/dev/null || echo 'unknown')"
fi
# Kernel check
header "Kernel Information"
log_and_display "Kernel version: $(uname -r 2>/dev/null || echo 'unknown')"
log_and_display "Kernel architecture: $(uname -m 2>/dev/null || echo 'unknown')"
if [ -f /proc/cmdline ]; then
log_and_display "Kernel parameters:"
cat /proc/cmdline 2>/dev/null | tr ' ' '\n' | head -5 | while read param; do
log_and_display " $param"
done
fi
# ------------------------------------------------------------------------------
# Completion
# ------------------------------------------------------------------------------
log_and_display ""
header "FINISH"
log_and_display ""
log_and_display "Diagnostics completed. Log saved to: $LOG_FILE"
log_and_display ""
# Variable cleanup
pm="" && opt="" && docker_pkg="" && CUR_USER="" && USER_GOOD="" && USER_GROUP="" && PASSWD_REQUEST="" && CHECK_CONTAINERS="" && SUD="" && docker_service="" && docker_status="" && docker_loading=""

View File

@@ -174,7 +174,7 @@ public:
QLocale getAppLanguage()
{
QString localeStr = m_settings.value("Conf/appLanguage").toString();
QString localeStr = m_settings.value("Conf/appLanguage", QLocale::system().name()).toString();
return QLocale(localeStr);
};
void setAppLanguage(QLocale locale)

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,7 @@ namespace
constexpr char uuid[] = "installation_uuid";
constexpr char osVersion[] = "os_version";
constexpr char appVersion[] = "app_version";
constexpr char appLanguage[] = "app_language";
constexpr char userCountryCode[] = "user_country_code";
constexpr char serverCountryCode[] = "server_country_code";
@@ -43,6 +44,9 @@ namespace
constexpr char authData[] = "auth_data";
constexpr char config[] = "config";
constexpr char subscription[] = "subscription";
constexpr char endDate[] = "end_date";
}
struct ProtocolData
@@ -163,7 +167,7 @@ namespace
auto clientProtocolConfig =
QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
//TODO looks like this block can be removed after v1 configs EOL
// TODO looks like this block can be removed after v1 configs EOL
serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount);
serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize);
@@ -223,6 +227,19 @@ namespace
return ErrorCode::NoError;
}
bool isSubscriptionExpired(const QJsonObject &apiConfig)
{
auto subscription = apiConfig.value(configKey::subscription).toObject();
if (subscription.isEmpty()) {
return false;
}
auto subscriptionEndDate = subscription.value(configKey::endDate).toString();
if (apiUtils::isSubscriptionExpired(subscriptionEndDate)) {
return true;
}
return false;
}
}
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
@@ -242,6 +259,11 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -277,6 +299,11 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -322,6 +349,7 @@ bool ApiConfigsController::fillAvailableServices()
{
QJsonObject apiPayload;
apiPayload[configKey::osVersion] = QSysInfo::productType();
apiPayload[configKey::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody);
@@ -396,6 +424,11 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
if (isSubscriptionExpired(apiConfig)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -429,6 +462,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, gatewayRequestData.authData);
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) {
newServerConfig.insert(config_key::name, serverConfig.value(config_key::name));
@@ -502,6 +536,11 @@ bool ApiConfigsController::deactivateDevice()
return true;
}
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
m_settings->getInstallationUuid(true),
@@ -536,6 +575,11 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q
return true;
}
if (isSubscriptionExpired(apiConfigObject)) {
emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError);
return false;
}
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
uuid,

View File

@@ -297,10 +297,11 @@ void ExportController::revokeConfig(const int row, const DockerContainer contain
{
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
ErrorCode errorCode =
m_clientManagementModel->revokeClient(row, container, credentials, m_serversModel->getProcessedServerIndex(), serverController);
m_clientManagementModel->revokeClient(row, container, credentials, m_serversModel->getProcessedServerIndex(), serverController);
if (errorCode != ErrorCode::NoError) {
emit exportErrorOccurred(errorCode);
}
emit revokeConfigCompleted();
}
void ExportController::renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials)

View File

@@ -42,6 +42,7 @@ public slots:
signals:
void generateConfig(int type);
void revokeConfigCompleted();
void exportErrorOccurred(const QString &errorMessage);
void exportErrorOccurred(ErrorCode errorCode);

View File

@@ -274,7 +274,7 @@ void ImportController::processNativeWireGuardConfig()
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject();
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50);
clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount;

View File

@@ -73,7 +73,7 @@ void InstallController::install(DockerContainer container, int port, TransportPr
containerConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, protocol));
if (container == DockerContainer::Awg) {
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(4, 7));
QString junkPacketMinSize = QString::number(10);
QString junkPacketMaxSize = QString::number(50);

View File

@@ -112,6 +112,7 @@ void ListViewFocusController::previousDelegate()
case Section::Default: {
if (hasFooter()) {
m_currentSection = Section::Footer;
viewAtCurrentIndex();
break;
}
[[fallthrough]];
@@ -127,9 +128,11 @@ void ListViewFocusController::previousDelegate()
case Section::Delegate: {
if (m_delegateIndex > 0) {
setDelegateIndex(m_delegateIndex - 1);
viewAtCurrentIndex();
break;
} else if (hasHeader()) {
m_currentSection = Section::Header;
viewAtCurrentIndex();
break;
}
[[fallthrough]];
@@ -137,6 +140,7 @@ void ListViewFocusController::previousDelegate()
case Section::Header: {
m_isReturnNeeded = true;
m_currentSection = Section::Default;
viewAtCurrentIndex();
break;
}
default: {
@@ -275,7 +279,7 @@ bool ListViewFocusController::isFirstFocusItemInListView() const
return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader();
}
case Section::Header: {
isFirstFocusItemInDelegate();
return isFirstFocusItemInDelegate();
}
case Section::Default: {
return true;

View File

@@ -169,7 +169,7 @@ void PageController::onShowErrorMessage(ErrorCode errorCode)
{
const auto fullErrorMessage = errorString(errorCode);
const auto errorMessage = fullErrorMessage.mid(fullErrorMessage.indexOf(". ") + 1); // remove ErrorCode %1.
const auto errorUrl = QStringLiteral("https://docs.amnezia.org/troubleshooting/error-codes/#error-%1-%2").arg(static_cast<int>(errorCode)).arg(utils::enumToString(errorCode).toLower());
const auto errorUrl = QStringLiteral("troubleshooting/error-codes/#error-%1-%2").arg(static_cast<int>(errorCode)).arg(utils::enumToString(errorCode).toLower());
const auto fullMessage = QStringLiteral("<a href=\"%1\" style=\"color: #FBB26A;\">ErrorCode: %2</a>. %3").arg(errorUrl).arg(static_cast<int>(errorCode)).arg(errorMessage);
emit showErrorMessage(fullMessage);

View File

@@ -40,7 +40,7 @@ namespace PageLoader
PageSettingsApiDevices,
PageSettingsApiSubscriptionKey,
PageSettingsKillSwitchExceptions,
PageServiceSftpSettings,
PageServiceTorWebsiteSettings,
PageServiceDnsSettings,
@@ -125,6 +125,8 @@ signals:
void goToPageViewConfig();
void goToPageSettingsServerServices();
void goToPageSettingsBackup();
void goToShareConnectionPage(QString headerText, QString configContentHeaderText, QString configCaption, QString configExtension,
QString configFileName);
void closePage();

View File

@@ -151,6 +151,7 @@ void SettingsController::backupAppConfig(const QString &fileName)
config["Conf/autoStart"] = Autostart::isAutostart();
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
}
@@ -186,7 +187,8 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true";
bool appSplittunnelingEnabled =
newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
#if defined(Q_OS_WINDOWS)
@@ -198,12 +200,13 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
m_appSplitTunnelingModel->clearAppsList();
}
}
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
#endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true";
bool siteSplittunnelingEnabled =
newConfigData.value("Conf/sitesSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
@@ -214,6 +217,11 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
m_settings->setStrictKillSwitchEnabled(false);
#endif
bool amneziaDnsEnabled = newConfigData.contains("Conf/useAmneziaDns")
? newConfigData.value("Conf/useAmneziaDns").toBool()
: m_settings->useAmneziaDns();
emit amneziaDnsToggled(amneziaDnsEnabled);
emit restoreBackupFinished();
} else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@@ -264,6 +272,9 @@ bool SettingsController::isAutoStartEnabled()
void SettingsController::toggleAutoStart(bool enable)
{
Autostart::setAutostart(enable);
if (!enable) {
toggleStartMinimized(false);
}
}
bool SettingsController::isStartMinimizedEnabled()
@@ -274,6 +285,7 @@ bool SettingsController::isStartMinimizedEnabled()
void SettingsController::toggleStartMinimized(bool enable)
{
m_settings->setStartMinimized(enable);
emit startMinimizedChanged();
}
bool SettingsController::isScreenshotsEnabled()

View File

@@ -32,6 +32,7 @@ public:
Q_PROPERTY(bool isDevGatewayEnv READ isDevGatewayEnv WRITE toggleDevGatewayEnv NOTIFY devGatewayEnvChanged)
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
public slots:
void toggleAmneziaDns(bool enable);
@@ -125,6 +126,7 @@ signals:
void devGatewayEnvChanged(bool enabled);
void isHomeAdLabelVisibleChanged(bool visible);
void startMinimizedChanged();
private:
QSharedPointer<ServersModel> m_serversModel;

View File

@@ -31,7 +31,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
return tr("Active");
}
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("Inactive") : tr("Active");
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>") : tr("Active");
}
case EndDateRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {

View File

@@ -15,6 +15,7 @@ namespace
constexpr char serviceInfo[] = "service_info";
constexpr char serviceType[] = "service_type";
constexpr char serviceProtocol[] = "service_protocol";
constexpr char serviceDescription[] = "service_description";
constexpr char name[] = "name";
constexpr char price[] = "price";
@@ -22,6 +23,10 @@ namespace
constexpr char timelimit[] = "timelimit";
constexpr char region[] = "region";
constexpr char description[] = "description";
constexpr char cardDescription[] = "card_description";
constexpr char features[] = "features";
constexpr char availableCountries[] = "available_countries";
constexpr char storeEndpoint[] = "store_endpoint";
@@ -65,11 +70,9 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
case CardDescriptionRole: {
auto speed = apiServiceData.serviceInfo.speed;
if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. "
"Access all websites and online resources. Speeds up to %1 Mbps.")
.arg(speed);
return apiServiceData.serviceInfo.cardDescription.arg(speed);
} else if (serviceType == serviceType::amneziaFree) {
QString description = tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
QString description = apiServiceData.serviceInfo.cardDescription;
if (!isServiceAvailable) {
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, "
"return to the previous screen, and try again.</a>");
@@ -78,12 +81,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
}
}
case ServiceDescriptionRole: {
if (serviceType == serviceType::amneziaPremium) {
return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
"Access all websites and online resources.");
} else {
return tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan.");
}
return apiServiceData.serviceInfo.description;
}
case IsServiceAvailableRole: {
if (serviceType == serviceType::amneziaFree) {
@@ -107,13 +105,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
return apiServiceData.serviceInfo.region;
}
case FeaturesRole: {
if (serviceType == serviceType::amneziaPremium) {
return tr("");
} else {
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. "
"Other sites will be opened from your real IP address, "
"<a href=\"%1\" style=\"color: #FBB26A;\">more details on the website.</a>");
}
return apiServiceData.serviceInfo.features;
}
case PriceRole: {
auto price = apiServiceData.serviceInfo.price;
@@ -125,6 +117,13 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
case EndDateRole: {
return QDateTime::fromString(apiServiceData.subscription.endDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
}
case OrderRole: {
if (serviceType == serviceType::amneziaPremium) {
return 0;
} else if (serviceType == serviceType::amneziaFree) {
return 1;
}
}
}
return QVariant();
@@ -224,6 +223,7 @@ QHash<int, QByteArray> ApiServicesModel::roleNames() const
roles[FeaturesRole] = "features";
roles[PriceRole] = "price";
roles[EndDateRole] = "endDate";
roles[OrderRole] = "order";
return roles;
}
@@ -234,6 +234,7 @@ ApiServicesModel::ApiServicesData ApiServicesModel::getApiServicesData(const QJs
auto serviceType = data.value(configKey::serviceType).toString();
auto serviceProtocol = data.value(configKey::serviceProtocol).toString();
auto availableCountries = data.value(configKey::availableCountries).toArray();
auto serviceDescription = data.value(configKey::serviceDescription).toObject();
auto subscriptionObject = data.value(configKey::subscription).toObject();
@@ -244,6 +245,10 @@ ApiServicesModel::ApiServicesData ApiServicesModel::getApiServicesData(const QJs
serviceData.serviceInfo.speed = serviceInfo.value(configKey::speed).toString();
serviceData.serviceInfo.timeLimit = serviceInfo.value(configKey::timelimit).toString();
serviceData.serviceInfo.cardDescription = serviceDescription.value(configKey::cardDescription).toString();
serviceData.serviceInfo.description = serviceDescription.value(configKey::description).toString();
serviceData.serviceInfo.features = serviceDescription.value(configKey::features).toString();
serviceData.type = serviceType;
serviceData.protocol = serviceProtocol;

View File

@@ -20,7 +20,8 @@ public:
RegionRole,
FeaturesRole,
PriceRole,
EndDateRole
EndDateRole,
OrderRole
};
explicit ApiServicesModel(QObject *parent = nullptr);
@@ -58,6 +59,10 @@ private:
QString region;
QString price;
QString description;
QString features;
QString cardDescription;
QJsonObject object;
};

View File

@@ -497,7 +497,8 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt
return error;
}
ErrorCode ClientManagementModel::renameClient(const int row, const QString &clientName, const DockerContainer container,
ErrorCode ClientManagementModel::renameClient(const int row, const QString &clientName,
const DockerContainer container,
const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, bool addTimeStamp)
{
@@ -529,7 +530,8 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie
return error;
}
ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContainer container, const ServerCredentials &credentials,
ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContainer container,
const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController)
{
ErrorCode errorCode = ErrorCode::NoError;

View File

@@ -44,10 +44,10 @@ public slots:
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController);
ErrorCode appendClient(const QString &clientId, const QString &clientName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController);
ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container, const ServerCredentials &credentials,
const QSharedPointer<ServerController> &serverController, bool addTimeStamp = false);
ErrorCode revokeClient(const int index, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex,
const QSharedPointer<ServerController> &serverController);
ErrorCode renameClient(const int row, const QString &userName, const DockerContainer container,
const ServerCredentials &credentials, const QSharedPointer<ServerController> &serverController, bool addTimeStamp = false);
ErrorCode revokeClient(const int index, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController);
ErrorCode revokeClient(const QJsonObject &containerConfig, const DockerContainer container, const ServerCredentials &credentials,
const int serverIndex, const QSharedPointer<ServerController> &serverController);
@@ -60,6 +60,8 @@ signals:
private:
bool isClientExists(const QString &clientId);
int clientIndexById(const QString &clientId);
void migration(const QByteArray &clientsTableString);
ErrorCode revokeOpenVpn(const int row, const DockerContainer container, const ServerCredentials &credentials, const int serverIndex,

View File

@@ -191,14 +191,14 @@ QJsonObject AwgConfigModel::getConfig()
jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount];
jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize];
jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize];
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1];
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2];
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3];
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4];
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5];
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1];
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2];
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3];
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1].toString().trimmed();
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2].toString().trimmed();
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3].toString().trimmed();
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4].toString().trimmed();
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5].toString().trimmed();
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1].toString().trimmed();
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2].toString().trimmed();
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3].toString().trimmed();
jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout];
m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());

View File

@@ -173,6 +173,7 @@ void ServersModel::resetModel()
m_servers = m_settings->serversArray();
m_defaultServerIndex = m_settings->defaultServerIndex();
m_processedServerIndex = m_defaultServerIndex;
m_isAmneziaDnsEnabled = m_settings->useAmneziaDns();
endResetModel();
emit defaultServerIndexChanged(m_defaultServerIndex);
}

View File

@@ -73,7 +73,7 @@ DrawerType2 {
var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ")
str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:")
str += "<ul style='margin-left: -16px;'>"
str += qsTr("<li>13 locations (with more coming soon)</li>")
str += qsTr("<li>20 locations (with more coming soon)</li>")
str += qsTr("<li>Easier switching between countries in the app</li>")
str += qsTr("<li>Personal dashboard to manage your subscription</li>")
str += "</ul>"

View File

@@ -20,8 +20,8 @@ Menu {
MenuItem {
text: qsTr("&Paste")
shortcut: StandardKey.Paste
// Fix calling paste from clipboard when launching app on android
enabled: Qt.platform.os === "android" ? true : textObj.canPaste
// Fix calling paste from clipboard when launching app on android/ios
enabled: (Qt.platform.os === "android" || Qt.platform.os === "ios") ? true : textObj.canPaste
onTriggered: textObj.paste()
}

View File

@@ -7,17 +7,20 @@ import Style 1.0
import "TextTypes"
RowLayout {
id: root
property string imageSource
property string leftText
property var rightText
property bool isRightTextUndefined: rightText === undefined
property int rightTextFormat: Text.PlainText
visible: !isRightTextUndefined
Image {
Layout.preferredHeight: 18
Layout.preferredWidth: 18
source: imageSource
source: root.imageSource
}
ListItemTitleType {
@@ -25,14 +28,15 @@ RowLayout {
Layout.rightMargin: 10
Layout.alignment: Qt.AlignRight
text: leftText
text: root.leftText
}
ParagraphTextType {
visible: rightText !== ""
visible: root.rightText !== ""
Layout.alignment: Qt.AlignLeft
text: isRightTextUndefined ? "" : rightText
text: root.isRightTextUndefined ? "" : root.rightText
textFormat: root.rightTextFormat
}
}

View File

@@ -72,7 +72,7 @@ Popup {
Layout.fillWidth: true
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link))
}
text: root.text

View File

@@ -155,7 +155,7 @@ Switch {
function handleSwitch(event) {
if (!event.isAutoRepeat) {
root.checked = !root.checked
root.checkedChanged()
root.toggled()
}
event.accepted = true
}

View File

@@ -37,6 +37,22 @@ Item {
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
ColumnLayout {
id: content
anchors.fill: parent

View File

@@ -153,7 +153,7 @@ PageType {
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
onCheckedChanged: {
onToggled: function() {
if (checked !== autoNegotiateEncryprion) {
autoNegotiateEncryprion = checked
}
@@ -320,7 +320,7 @@ PageType {
text: qsTr("Additional client configuration commands")
onCheckedChanged: {
onToggled: function() {
if (!checked) {
additionalClientCommands = ""
}
@@ -357,7 +357,7 @@ PageType {
text: qsTr("Additional server configuration commands")
onCheckedChanged: {
onToggled: function() {
if (!checked) {
additionalServerCommands = ""
}

View File

@@ -29,6 +29,7 @@ PageType {
readonly property string title: qsTr("Subscription Status")
readonly property string contentKey: "subscriptionStatus"
readonly property string objectImageSource: "qrc:/images/controls/info.svg"
readonly property bool isRichText: true
}
QtObject {
@@ -37,6 +38,7 @@ PageType {
readonly property string title: qsTr("Valid Until")
readonly property string contentKey: "endDate"
readonly property string objectImageSource: "qrc:/images/controls/history.svg"
readonly property bool isRichText: false
}
QtObject {
@@ -45,6 +47,7 @@ PageType {
readonly property string title: qsTr("Active Connections")
readonly property string contentKey: "connectedDevices"
readonly property string objectImageSource: "qrc:/images/controls/monitor.svg"
readonly property bool isRichText: false
}
property var processedServer
@@ -134,6 +137,7 @@ PageType {
imageSource: objectImageSource
leftText: title
rightText: ApiAccountInfoModel.data(contentKey)
rightTextFormat: isRichText ? Text.RichText : Text.PlainText
visible: rightText !== ""
}
@@ -214,9 +218,6 @@ PageType {
ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false)
// Navigate to PageShareConnection page
//PageController.goToPage(PageEnum.PageShareConnection)
}
}

View File

@@ -6,6 +6,8 @@ import Qt.labs.platform 1.1
import QtCore
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
@@ -17,6 +19,33 @@ import "../Components"
PageType {
id: root
property var processedServer
Connections {
target: ServersModel
function onProcessedServerChanged() {
root.processedServer = proxyServersModel.get(0)
}
}
SortFilterProxyModel {
id: proxyServersModel
objectName: "proxyServersModel"
sourceModel: ServersModel
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
}
]
Component.onCompleted: {
root.processedServer = proxyServersModel.get(0)
}
}
Component.onCompleted: {
PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport()
@@ -40,7 +69,7 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
text: qsTr("Amnezia Premium\nsubscription key")
text: qsTr(root.processedServer.name + "\nsubscription key")
font.pixelSize: 32
font.bold: true
color: AmneziaStyle.color.paleGray
@@ -53,18 +82,10 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.paleGray
hoveredColor: AmneziaStyle.color.sheerWhite
pressedColor: AmneziaStyle.color.translucentWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.black
leftImageColor: "black"
borderWidth: 1
text: qsTr("Copy key")
leftImageSource: "qrc:/images/controls/copy.svg"
onClicked: {
clickedFunc: function() {
ApiConfigsController.copyVpnKeyToClipboard()
PageController.showNotificationMessage(qsTr("Copied"))
}
@@ -85,13 +106,13 @@ PageType {
text: qsTr("Save key as a file")
leftImageSource: "qrc:/images/controls/share-2.svg"
onClicked: {
clickedFunc: function() {
var fileName = GC.isMobile()
? "amnezia_vpn_key.vpn"
? root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key.vpn"
: SystemController.getFileName(
qsTr("Save AmneziaVPN config"),
qsTr("Config files (*.vpn)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_vpn_key",
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + root.processedServer.name.toLowerCase().replace(/\s+/g, "_") + "_key",
true,
".vpn"
)
@@ -118,7 +139,7 @@ PageType {
text: qsTr("Show key text")
leftImageSource: "qrc:/images/controls/eye.svg"
onClicked: {
clickedFunc: function() {
PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false)
@@ -127,8 +148,9 @@ PageType {
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.preferredWidth: Math.min(Math.min(root.width - (Layout.leftMargin + Layout.rightMargin), root.height * 0.5), 360)
Layout.preferredHeight: Layout.preferredWidth
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -140,6 +162,9 @@ PageType {
Image {
anchors.fill: parent
smooth: false
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.width
sourceSize.height: parent.height
source: ApiConfigsController.qrCodesCount > 0 && ApiConfigsController.qrCodes[0] ? ApiConfigsController.qrCodes[0] : ""
}
}
@@ -181,7 +206,7 @@ PageType {
Header2Type {
Layout.fillWidth: true
headerText: qsTr("Amnezia Premium Subscription key")
headerText: qsTr(root.processedServer.name + " Subscription key")
}
TextArea {
@@ -194,7 +219,7 @@ PageType {
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
text: ApiConfigsController.vpnKey //|| ""
text: ApiConfigsController.vpnKey
wrapMode: Text.Wrap
background: Rectangle { color: AmneziaStyle.color.transparent }
}

View File

@@ -145,11 +145,25 @@ PageType {
}
}
}
WarningType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
textString: qsTr("Only \"Apps from the list should not have access via VPN\" mode is available on Windows")
iconPath: "qrc:/images/controls/alert-circle.svg"
enabled: (Qt.platform.os === "windows") && root.pageEnabled
}
}
ListViewType {
id: listView
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
anchors.top: header.bottom
anchors.bottom: addAppButton.top
anchors.left: parent.left

View File

@@ -66,7 +66,7 @@ PageType {
text: qsTr("Allow application screenshots")
checked: SettingsController.isScreenshotsEnabled()
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isScreenshotsEnabled()) {
SettingsController.toggleScreenshotsEnabled(checked)
}
@@ -109,7 +109,7 @@ PageType {
descriptionText: qsTr("Launch the application every time the device is starts")
checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isAutoStartEnabled()) {
SettingsController.toggleAutoStart(checked)
}
@@ -132,7 +132,7 @@ PageType {
descriptionText: qsTr("Connect to VPN on app start")
checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isAutoConnectEnabled()) {
SettingsController.toggleAutoConnect(checked)
}
@@ -157,9 +157,9 @@ PageType {
enabled: switcherAutoStart.checked
opacity: enabled ? 1.0 : 0.5
checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isStartMinimizedEnabled()) {
checked: SettingsController.startMinimized
onToggled: function() {
if (checked !== SettingsController.startMinimized) {
SettingsController.toggleStartMinimized(checked)
}
}

View File

@@ -66,7 +66,7 @@ PageType {
descriptionText: qsTr("If AmneziaDNS is installed on the server")
checked: SettingsController.isAmneziaDnsEnabled()
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isAmneziaDnsEnabled()) {
SettingsController.toggleAmneziaDns(checked)
}

View File

@@ -64,7 +64,7 @@ PageType {
checked: SettingsController.isLoggingEnabled
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}
@@ -167,7 +167,8 @@ PageType {
// Show service logs only if this is NOT a macOS build with
// Network-Extension (IsMacOsNeBuild is injected from C++ at run-time)
property list<QtObject> logTypes: IsMacOsNeBuild ? [
// or if this is NOT a mobile build
property list<QtObject> logTypes: (IsMacOsNeBuild || GC.isMobile()) ? [
clientLogs
] : [
clientLogs,
@@ -214,15 +215,11 @@ PageType {
}
readonly property var exportLogsHandler: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
}
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportServiceLogsFile(fileName)

View File

@@ -164,6 +164,8 @@ PageType {
ListViewType {
id: listView
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
anchors.top: header.bottom
anchors.topMargin: 16
anchors.bottom: addSiteButton.top

View File

@@ -66,6 +66,8 @@ PageType {
imageSource: imagePath
leftText: lText
rightText: rText
visible: isVisible
}
}

View File

@@ -3,6 +3,8 @@ import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
@@ -54,7 +56,15 @@ PageType {
spacing: 0
model: ApiServicesModel
model: SortFilterProxyModel {
id: proxyApiServicesModel
sourceModel: ApiServicesModel
sorters: RoleSorter {
roleName: "order"
sortOrder: Qt.AscendingOrder
}
}
delegate: ColumnLayout {
@@ -78,7 +88,7 @@ PageType {
onClicked: {
if (isServiceAvailable) {
ApiServicesModel.setServiceIndex(index)
ApiServicesModel.setServiceIndex(proxyApiServicesModel.mapToSource(index))
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
}

View File

@@ -86,7 +86,7 @@ PageType {
visible: PageController.isStartPageVisible()
checked: SettingsController.isLoggingEnabled
onCheckedChanged: {
onToggled: function() {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}

View File

@@ -30,7 +30,7 @@ PageType {
if (!ConnectionController.isConnected && !ContainersModel.isServiceContainer(containerIndex)) {
ServersModel.setDefaultContainer(ServersModel.processedIndex, containerIndex)
}
PageController.closePage() // close installing page
PageController.closePage() // close protocol settings page
@@ -38,6 +38,10 @@ PageType {
PageController.restorePageHomeState(true)
}
if (stackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageSetupWizardProtocols)) {
PageController.goToPage(PageEnum.PageHome)
}
PageController.showNotificationMessage(finishedMessage)
}

View File

@@ -29,56 +29,78 @@ PageType {
Xray
}
signal revokeConfig(int index)
onRevokeConfig: function(index) {
PageController.showBusyIndicator(true)
ExportController.revokeConfig(index,
ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Config revoked"))
}
Connections {
target: ExportController
function onRevokeConfigCompleted() {
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Config revoked"))
}
function onGenerateConfig(type) {
PageController.showBusyIndicator(true)
var configCaption
var configExtension
var configFileName
switch (type) {
case PageShare.ConfigType.AmneziaConnection: {
ExportController.generateConnectionConfig(clientNameTextField.textField.text);
configCaption = qsTr("Save AmneziaVPN config")
configExtension = ".vpn"
configFileName = "amnezia_config"
break;
}
case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig(clientNameTextField.textField.text)
configCaption = qsTr("Save OpenVPN config")
configExtension = ".ovpn"
configFileName = "amnezia_for_openvpn"
break
}
case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig(clientNameTextField.textField.text)
configCaption = qsTr("Save WireGuard config")
configExtension = ".conf"
configFileName = "amnezia_for_wireguard"
break
}
case PageShare.ConfigType.Awg: {
ExportController.generateAwgConfig(clientNameTextField.textField.text)
configCaption = qsTr("Save AmneziaWG config")
configExtension = ".conf"
configFileName = "amnezia_for_awg"
break
}
case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig()
configCaption = qsTr("Save Shadowsocks config")
configExtension = ".json"
configFileName = "amnezia_for_shadowsocks"
break
}
case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig()
configCaption = qsTr("Save Cloak config")
configExtension = ".json"
configFileName = "amnezia_for_cloak"
break
}
case PageShare.ConfigType.Xray: {
ExportController.generateXrayConfig(clientNameTextField.textField.text)
configCaption = qsTr("Save XRay config")
configExtension = ".json"
configFileName = "amnezia_for_xray"
break
}
}
PageController.showBusyIndicator(false)
PageController.goToPage(PageEnum.PageShareConnection)
var headerText = qsTr("Connection to ") + serverSelector.text
var configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
PageController.goToShareConnectionPage(headerText, configContentHeaderText, configCaption, configExtension, configFileName)
}
function onExportErrorOccurred(error) {
@@ -581,12 +603,14 @@ PageType {
visible: accessTypeSelector.currentIndex === 1
property bool isFocusable: true
property bool freezeFilter: false
model: SortFilterProxyModel {
id: proxyClientManagementModel
sourceModel: ClientManagementModel
filters: RegExpFilter {
roleName: "clientName"
enabled: !clientsListView.freezeFilter
pattern: ".*" + searchTextField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
@@ -769,12 +793,14 @@ PageType {
}
if (clientNameEditor.textField.text !== clientName) {
clientsListView.freezeFilter = true
PageController.showBusyIndicator(true)
ExportController.renameClient(index,
ExportController.renameClient(proxyClientManagementModel.mapToSource(index),
clientNameEditor.textField.text,
ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
Qt.callLater(function(){ clientsListView.freezeFilter = false })
clientNameEditDrawer.closeTriggered()
}
}
@@ -805,7 +831,10 @@ PageType {
var yesButtonFunction = function() {
clientInfoDrawer.closeTriggered()
root.revokeConfig(index)
PageController.showBusyIndicator(true)
ExportController.revokeConfig(proxyClientManagementModel.mapToSource(index),
ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials())
}
var noButtonFunction = function() {
}

View File

@@ -21,12 +21,6 @@ PageType {
id: pageShareConnection
property string headerText
Component.onCompleted: {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
property string configContentHeaderText
property string shareButtonText: qsTr("Share")
property string copyButtonText: qsTr("Copy")
@@ -36,17 +30,17 @@ PageType {
property string configCaption: qsTr("Save AmneziaVPN config")
property string configFileName: "amnezia_config"
onVisibleChanged: {
configExtension = ".vpn"
configCaption = qsTr("Save AmneziaVPN config")
configFileName = "amnezia_config"
// onVisibleChanged: {
// configExtension = ".vpn"
// configCaption = qsTr("Save AmneziaVPN config")
// configFileName = "amnezia_config"
if (visible) {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
}
// if (visible) {
// var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
// headerText = qsTr("Connection to ") + serverName
// configContentHeaderText = qsTr("File with connection settings to ") + serverName
// }
// }
BackButtonType {
id: backButton
@@ -275,8 +269,9 @@ PageType {
Rectangle {
id: qrCodeContainer
Layout.fillWidth: true
Layout.preferredHeight: width
Layout.preferredWidth: Math.min(Math.min(listView.width - (Layout.leftMargin + Layout.rightMargin), pageShareConnection.height * 0.5), 360)
Layout.preferredHeight: Layout.preferredWidth
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -286,6 +281,9 @@ PageType {
Image {
anchors.fill: parent
smooth: false
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.width
sourceSize.height: parent.height
source: pageShareConnection.isSelfHostedConfig ? (isQrCodeVisible ? ExportController.qrCodes[0] : "") : (isQrCodeVisible ? ApiConfigsController.qrCodes[0] : "")
property bool isFocusable: true
Keys.onTabPressed: FocusController.nextKeyTabItem()

View File

@@ -37,6 +37,9 @@ PageType {
ListViewType {
id: listView
property string headerText: ""
property string configContentHeaderText: ""
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
@@ -104,12 +107,13 @@ PageType {
clickedFunction: function() {
handler()
if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) {
serverSelector.currentIndex = serverSelectorListView.currentIndex
if (serverSelector.currentIndex !== serverSelectorListView.selectedIndex) {
serverSelector.currentIndex = serverSelectorListView.selectedIndex
serverSelector.severSelectorIndexChanged()
}
shareConnectionPage.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionPage.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
listView.headerText = qsTr("Accessing ") + serverSelector.text
listView.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.closeTriggered()
}
@@ -121,7 +125,7 @@ PageType {
function handler() {
serverSelector.text = selectedText
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
ServersModel.processedIndex = proxyServersModel.mapToSource(selectedIndex)
}
}
}
@@ -156,7 +160,7 @@ PageType {
PageController.showBusyIndicator(false)
PageController.goToPage(PageEnum.PageShareConnection)
PageController.goToShareConnectionPage(listView.headerText, listView.configContentHeaderText, "", ".vpn", "amnezia_config")
}
}
}

View File

@@ -44,6 +44,19 @@ PageType {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
}
function onGoToShareConnectionPage(headerText, configContentHeaderText, configCaption, configExtension, configFileName) {
var pagePath = PageController.getPagePath(PageEnum.PageShareConnection)
tabBarStackView.push(pagePath,
{ "objectName" : pagePath,
"headerText" : headerText,
"configContentHeaderText" : configContentHeaderText,
"configCaption" : configCaption,
"configExtension" : configExtension,
"configFileName" : configFileName
},
StackView.PushTransition)
}
function onDisableControls(disabled) {
isControlsDisabled = disabled
}

View File

@@ -32,8 +32,8 @@
VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent)
: QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this))
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
m_checkTimer.setInterval(1000);
#if defined(Q_OS_IOS) || defined(MACOS_NE)
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged);
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
@@ -42,6 +42,9 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent
VpnConnection::~VpnConnection()
{
#if defined AMNEZIA_DESKTOP
disconnectFromVpn();
#endif
}
void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
@@ -52,7 +55,19 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
void VpnConnection::onKillSwitchModeChanged(bool enabled)
{
#ifdef AMNEZIA_DESKTOP
if (InterfaceReady()) {
if (!m_IpcClient) {
m_IpcClient = new IpcClient(this);
}
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occurred when init IPC client";
emit serviceIsNotReady();
return;
}
}
if (IpcClient::Interface()) {
qDebug() << "Set KillSwitch Strict mode enabled " << enabled;
IpcClient::Interface()->refreshKillSwitch(enabled);
}
@@ -65,7 +80,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
#ifdef AMNEZIA_DESKTOP
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
if (InterfaceReady()) {
if (IpcClient::Interface()) {
if (state == Vpn::ConnectionState::Connected) {
IpcClient::Interface()->resetIpStack();
IpcClient::Interface()->flushDns();
@@ -197,41 +212,14 @@ void VpnConnection::deleteRoutes(const QStringList &ips)
#endif
}
bool VpnConnection::InterfaceReady()
{
#ifdef AMNEZIA_DESKTOP
if (!m_IpcClient) {
m_IpcClient = new IpcClient(this);
}
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occurred when init IPC client";
emit serviceIsNotReady();
return false;
}
}
return IpcClient::Interface() != nullptr;
#endif
return true;
}
void VpnConnection::flushDns()
{
#ifdef AMNEZIA_DESKTOP
if (InterfaceReady())
if (IpcClient::Interface())
IpcClient::Interface()->flushDns();
#endif
}
void VpnConnection::disconnectSlots()
{
if (m_vpnProtocol) {
m_vpnProtocol->disconnect();
}
}
ErrorCode VpnConnection::lastError() const
{
#ifdef Q_OS_ANDROID
@@ -252,12 +240,21 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
.arg(serverIndex)
.arg(ContainerProps::containerToString(container))
<< m_settings->routeMode();
if (!InterfaceReady()) {
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
if (!m_IpcClient) {
m_IpcClient = new IpcClient(this);
}
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occurred when init IPC client";
emit serviceIsNotReady();
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
}
#endif
m_remoteAddress = NetworkUtilities::getIPAddress(credentials.hostName);
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
@@ -443,18 +440,13 @@ QString VpnConnection::bytesPerSecToText(quint64 bytes)
void VpnConnection::disconnectFromVpn()
{
#ifdef AMNEZIA_DESKTOP
if (InterfaceReady()) {
QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex());
if (IpcClient::Interface()) {
IpcClient::Interface()->flushDns();
m_vpnProtocol.data()->stop();
qDebug() << "Interface is ready!";
QRemoteObjectPendingReply<bool> flushDnsResp = IpcClient::Interface()->flushDns();
flushDnsResp.waitForFinished(1000);
qDebug() << "Flushed DNS";
// delete cached routes
QRemoteObjectPendingReply<bool> clearSavedRoutesResp = IpcClient::Interface()->clearSavedRoutes();
clearSavedRoutesResp.waitForFinished(1000);
QRemoteObjectPendingReply<bool> response = IpcClient::Interface()->clearSavedRoutes();
response.waitForFinished(1000);
}
#endif
@@ -483,13 +475,12 @@ void VpnConnection::disconnectFromVpn()
return;
}
#if !defined(Q_OS_ANDROID) && !defined(AMNEZIA_DESKTOP)
#ifndef Q_OS_ANDROID
if (m_vpnProtocol) {
m_vpnProtocol->deleteLater();
}
#endif
m_vpnProtocol = nullptr;
#endif
}
Vpn::ConnectionState VpnConnection::connectionState()

View File

@@ -56,7 +56,6 @@ public slots:
void deleteRoutes(const QStringList &ips);
void flushDns();
void onKillSwitchModeChanged(bool enabled);
void disconnectSlots();
signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
@@ -96,7 +95,6 @@ private:
void appendSplitTunnelingConfig();
void appendKillSwitchConfig();
bool InterfaceReady();
};
#endif // VPNCONNECTION_H

View File

@@ -34,66 +34,23 @@ clang -v
# Generate XCodeProj
$QT_BIN_DIR/qt-cmake . -B $BUILD_DIR -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -DDEPLOY=ON
KEYCHAIN=amnezia.build.ios.keychain
KEYCHAIN_FILE=$HOME/Library/Keychains/${KEYCHAIN}-db
# Setup keychain
if [ "${IOS_SIGNING_CERT_BASE64+x}" ]; then
echo "Import certificate"
cd $BUILD_DIR
xcodebuild archive \
-project AmneziaVPN.xcodeproj \
-scheme AmneziaVPN \
-configuration Release \
-archivePath ./build/AmneziaVPN.xcarchive \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
TRUST_CERT_CER=$BUILD_DIR/trust-cert.cer
SIGNING_CERT_P12=$BUILD_DIR/signing-cert.p12
mkdir -p Payload
echo $IOS_TRUST_CERT_BASE64 | base64 --decode > $TRUST_CERT_CER
echo $IOS_SIGNING_CERT_BASE64 | base64 --decode > $SIGNING_CERT_P12
cp -R ./build/AmneziaVPN.xcarchive/Products/Applications/AmneziaVPN.app Payload/
shasum -a 256 $TRUST_CERT_CER
shasum -a 256 $SIGNING_CERT_P12
zip -r AmneziaVPN_unsigned.ipa Payload
KEYCHAIN_PASS=$IOS_SIGNING_CERT_PASSWORD
rm -rf Payload
security create-keychain -p $KEYCHAIN_PASS $KEYCHAIN || true
security default-keychain -s $KEYCHAIN
security unlock-keychain -p $KEYCHAIN_PASS $KEYCHAIN
security default-keychain
security list-keychains
security import $TRUST_CERT_CER -k $KEYCHAIN -P "" -T /usr/bin/codesign
security import $SIGNING_CERT_P12 -k $KEYCHAIN -P $IOS_SIGNING_CERT_PASSWORD -T /usr/bin/codesign
security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k $KEYCHAIN_PASS $KEYCHAIN
security find-identity -p codesigning
security set-keychain-settings $KEYCHAIN_FILE
security set-keychain-settings -t 3600 $KEYCHAIN_FILE
security unlock-keychain -p $KEYCHAIN_PASS $KEYCHAIN_FILE
# Copy provisioning prifiles
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles/"
echo $IOS_APP_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision
echo $IOS_NE_PROVISIONING_PROFILE | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision
shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision
shasum -a 256 ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision
profile_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision | grep -io "[-A-F0-9]\{36\}"`
profile_ne_uuid=`grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision | grep -io "[-A-F0-9]\{36\}"`
mv ~/Library/MobileDevice/Provisioning\ Profiles/app.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_uuid.mobileprovision
mv ~/Library/MobileDevice/Provisioning\ Profiles/ne.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$profile_ne_uuid.mobileprovision
else
echo "Failed to import certificate, aborting..."
exit 1
fi
# Build project
xcodebuild \
"OTHER_CODE_SIGN_FLAGS=--keychain '$KEYCHAIN_FILE'" \
-configuration Release \
-scheme AmneziaVPN \
-destination "generic/platform=iOS,name=Any iOS'" \
-project $BUILD_DIR/AmneziaVPN.xcodeproj
# restore keychain
security default-keychain -s login.keychain
echo " Build setup completed successfully."

View File

@@ -1,6 +1,7 @@
#!/bin/bash
APP_NAME=AmneziaVPN
ORG_NAME=AmneziaVPN.ORG
LOG_FOLDER=/var/log/$APP_NAME
LOG_FILE="$LOG_FOLDER/post-uninstall.log"
APP_PATH=/opt/$APP_NAME
@@ -64,6 +65,24 @@ if test -f /usr/share/pixmaps/$APP_NAME.png; then
fi
### Remove the service log file (keep post-uninstall.log)
if test -f "$LOG_FOLDER/AmneziaVPN-service.log"; then
sudo rm -f "$LOG_FOLDER/AmneziaVPN-service.log" >> $LOG_FILE 2>&1
fi
### Remove user logs for current user only
TARGET_HOME="$HOME"
if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
TARGET_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
fi
if test -d "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log"; then
rm -rf "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME/log" >> $LOG_FILE 2>&1
fi
# Try to remove empty app and organization directories under user share
if rmdir "$TARGET_HOME/.local/share/$ORG_NAME/$APP_NAME" 2>/dev/null; then :; fi
if rmdir "$TARGET_HOME/.local/share/$ORG_NAME" 2>/dev/null; then :; fi
if command -v steamos-readonly &> /dev/null; then
sudo steamos-readonly enable >> $LOG_FILE
echo "steamos-readonly enabled" >> $LOG_FILE

View File

@@ -1,7 +1,14 @@
set AmneziaPath=%~dp0
echo %AmneziaPath%
"%AmneziaPath%\AmneziaVPN.exe" -c
rem Define directories for logs
set "ORG_DIR=%AppData%\AmneziaVPN.ORG"
set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN"
set "USER_LOG_DIR=%USER_APP_DIR%\log"
set "SYS_APP_DIR=%ProgramData%\AmneziaVPN"
set "SYS_LOG_DIR=%SYS_APP_DIR%\log"
set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log"
timeout /t 1
sc stop AmneziaVPN-service
sc delete AmneziaVPN-service
@@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN
sc delete AmneziaWGTunnel$AmneziaVPN
taskkill /IM "AmneziaVPN-service.exe" /F
taskkill /IM "AmneziaVPN.exe" /F
rem Delete the service log file under ProgramData
if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%"
if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%"
rem Try to remove application dir if empty
rd "%SYS_APP_DIR%" 2>nul
rem Delete client logs under current user's AppData\Roaming (Organization\Application)
if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%"
rem Try to remove app and org directories if empty
rd "%USER_APP_DIR%" 2>nul
rd "%ORG_DIR%" 2>nul
exit /b 0

View File

@@ -1,7 +1,14 @@
set AmneziaPath=%~dp0
echo %AmneziaPath%
"%AmneziaPath%\AmneziaVPN.exe" -c
rem Define directories for logs
set "ORG_DIR=%AppData%\AmneziaVPN.ORG"
set "USER_APP_DIR=%ORG_DIR%\AmneziaVPN"
set "USER_LOG_DIR=%USER_APP_DIR%\log"
set "SYS_APP_DIR=%ProgramData%\AmneziaVPN"
set "SYS_LOG_DIR=%SYS_APP_DIR%\log"
set "SYS_LOG_FILE=%SYS_LOG_DIR%\AmneziaVPN-service.log"
timeout /t 1
sc stop AmneziaVPN-service
sc delete AmneziaVPN-service
@@ -9,4 +16,17 @@ sc stop AmneziaWGTunnel$AmneziaVPN
sc delete AmneziaWGTunnel$AmneziaVPN
taskkill /IM "AmneziaVPN-service.exe" /F
taskkill /IM "AmneziaVPN.exe" /F
rem Delete the service log file under ProgramData
if exist "%SYS_LOG_FILE%" del /F /Q "%SYS_LOG_FILE%"
if exist "%SYS_LOG_DIR%" rmdir /S /Q "%SYS_LOG_DIR%"
rem Try to remove application dir if empty
rd "%SYS_APP_DIR%" 2>nul
rem Delete client logs under current user's AppData\Roaming (Organization\Application)
if exist "%USER_LOG_DIR%" rmdir /S /Q "%USER_LOG_DIR%"
rem Try to remove app and org directories if empty
rd "%USER_APP_DIR%" 2>nul
rd "%ORG_DIR%" 2>nul
exit /b 0

View File

@@ -12,7 +12,7 @@ class IpcInterface
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
SLOT( bool clearSavedRoutes() );
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
SLOT( bool flushDns() );
SLOT( void flushDns() );
SLOT( void resetIpStack() );
SLOT( bool checkAndInstallDriver() );
@@ -25,8 +25,8 @@ class IpcInterface
SLOT( bool createTun(const QString &dev, const QString &subnet) );
SLOT( bool deleteTun(const QString &dev) );
SLOT( bool StartRoutingIpv6() );
SLOT( bool StopRoutingIpv6() );
SLOT( void StartRoutingIpv6() );
SLOT( void StopRoutingIpv6() );
SLOT( bool disableKillSwitch() );
SLOT( bool disableAllTraffic() );

View File

@@ -83,7 +83,7 @@ bool IpcServer::routeDeleteList(const QString &gw, const QStringList &ips)
return Router::routeDeleteList(gw, ips);
}
bool IpcServer::flushDns()
void IpcServer::flushDns()
{
#ifdef MZ_DEBUG
qDebug() << "IpcServer::flushDns";
@@ -157,13 +157,13 @@ bool IpcServer::updateResolvers(const QString &ifname, const QList<QHostAddress>
return Router::updateResolvers(ifname, resolvers);
}
bool IpcServer::StartRoutingIpv6()
void IpcServer::StartRoutingIpv6()
{
return Router::StartRoutingIpv6();
Router::StartRoutingIpv6();
}
bool IpcServer::StopRoutingIpv6()
void IpcServer::StopRoutingIpv6()
{
return Router::StopRoutingIpv6();
Router::StopRoutingIpv6();
}
void IpcServer::setLogsEnabled(bool enabled)

View File

@@ -23,7 +23,7 @@ public:
virtual int routeAddList(const QString &gw, const QStringList &ips) override;
virtual bool clearSavedRoutes() override;
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
virtual bool flushDns() override;
virtual void flushDns() override;
virtual void resetIpStack() override;
virtual bool checkAndInstallDriver() override;
virtual QStringList getTapList() override;
@@ -32,8 +32,8 @@ public:
virtual void setLogsEnabled(bool enabled) override;
virtual bool createTun(const QString &dev, const QString &subnet) override;
virtual bool deleteTun(const QString &dev) override;
virtual bool StartRoutingIpv6() override;
virtual bool StopRoutingIpv6() override;
virtual void StartRoutingIpv6() override;
virtual void StopRoutingIpv6() override;
virtual bool disableAllTraffic() override;
virtual bool addKillSwitchAllowedRange(QStringList ranges) override;
virtual bool resetKillSwitchAllowedRange(QStringList ranges) override;

View File

@@ -29,7 +29,7 @@ void IpcProcessTun2Socks::start()
QString XrayConStr = "socks5://127.0.0.1:10808";
#ifdef Q_OS_WIN
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr, "-tun-post-up",
QStringList arguments({"-device", "tun://tun2?guid={081A8A84-8D12-4DF5-B8C4-396D5B0053E4}", "-proxy", XrayConStr, "-tun-post-up",
QString("cmd /c netsh interface ip set address name=\"tun2\" static %1 255.255.255.255")
.arg(amnezia::protocols::xray::defaultLocalAddr)});
#endif

View File

@@ -98,17 +98,6 @@ bool KillSwitch::disableKillSwitch() {
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
} else {
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
LinuxFirewall::uninstall();
}
#endif

View File

@@ -42,14 +42,14 @@ int Router::routeDeleteList(const QString &gw, const QStringList &ips)
#endif
}
bool Router::flushDns()
void Router::flushDns()
{
#ifdef Q_OS_WIN
return RouterWin::Instance().flushDns();
RouterWin::Instance().flushDns();
#elif defined (Q_OS_MAC)
return RouterMac::Instance().flushDns();
RouterMac::Instance().flushDns();
#elif defined Q_OS_LINUX
return RouterLinux::Instance().flushDns();
RouterLinux::Instance().flushDns();
#endif
}
@@ -100,25 +100,25 @@ bool Router::updateResolvers(const QString& ifname, const QList<QHostAddress>& r
}
bool Router::StopRoutingIpv6()
void Router::StopRoutingIpv6()
{
#ifdef Q_OS_WIN
return RouterWin::Instance().StopRoutingIpv6();
RouterWin::Instance().StopRoutingIpv6();
#elif defined (Q_OS_MAC)
return true;// todo fixme
// todo fixme
#elif defined Q_OS_LINUX
return RouterLinux::Instance().StopRoutingIpv6();
RouterLinux::Instance().StopRoutingIpv6();
#endif
}
bool Router::StartRoutingIpv6()
void Router::StartRoutingIpv6()
{
#ifdef Q_OS_WIN
return RouterWin::Instance().StartRoutingIpv6();
RouterWin::Instance().StartRoutingIpv6();
#elif defined (Q_OS_MAC)
return true;// todo fixme
// todo fixme
#elif defined Q_OS_LINUX
return RouterLinux::Instance().StartRoutingIpv6();
RouterLinux::Instance().StartRoutingIpv6();
#endif
}

View File

@@ -19,12 +19,12 @@ public:
static int routeAddList(const QString &gw, const QStringList &ips);
static bool clearSavedRoutes();
static int routeDeleteList(const QString &gw, const QStringList &ips);
static bool flushDns();
static void flushDns();
static void resetIpStack();
static bool createTun(const QString &dev, const QString &subnet);
static bool deleteTun(const QString &dev);
static bool StartRoutingIpv6();
static bool StopRoutingIpv6();
static void StartRoutingIpv6();
static void StopRoutingIpv6();
static bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
};

View File

@@ -160,7 +160,7 @@ bool RouterLinux::isServiceActive(const QString &serviceName) {
return process.exitCode() == 0;
}
bool RouterLinux::flushDns()
void RouterLinux::flushDns()
{
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
@@ -174,7 +174,7 @@ bool RouterLinux::flushDns()
p.start("systemctl", { "restart", "systemd-resolved" });
} else {
qDebug() << "No suitable DNS manager found.";
return false;
return;
}
p.waitForFinished();
@@ -183,8 +183,6 @@ bool RouterLinux::flushDns()
qDebug().noquote() << "Flush dns completed";
else
qDebug().noquote() << "OUTPUT systemctl restart nscd/systemd-resolved: " + output;
return true;
}
bool RouterLinux::createTun(const QString &dev, const QString &subnet) {
@@ -281,7 +279,7 @@ bool RouterLinux::updateResolvers(const QString& ifname, const QList<QHostAddres
return m_dnsUtil->updateResolvers(ifname, resolvers);
}
bool RouterLinux::StartRoutingIpv6()
void RouterLinux::StartRoutingIpv6()
{
QProcess process;
QStringList commands;
@@ -291,12 +289,12 @@ bool RouterLinux::StartRoutingIpv6()
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start activate ipv6\n";
return false;
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not activate ipv6\n";
return false;
return;
}
commands.clear();
@@ -305,20 +303,19 @@ bool RouterLinux::StartRoutingIpv6()
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start activate ipv6\n";
return false;
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not activate ipv6\n";
return false;
return;
}
commands.clear();
qDebug().noquote() << "StartRoutingIpv6 OK";
return true;
}
bool RouterLinux::StopRoutingIpv6()
void RouterLinux::StopRoutingIpv6()
{
QProcess process;
QStringList commands;
@@ -328,12 +325,12 @@ bool RouterLinux::StopRoutingIpv6()
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start disable ipv6\n";
return false;
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not disable ipv6\n";
return false;
return;
}
commands.clear();
@@ -342,15 +339,14 @@ bool RouterLinux::StopRoutingIpv6()
if (!process.waitForStarted(1000))
{
qDebug().noquote() << "Could not start disable ipv6\n";
return false;
return;
}
else if (!process.waitForFinished(2000))
{
qDebug().noquote() << "Could not disable ipv6\n";
return false;
return;
}
commands.clear();
qDebug().noquote() << "StopRoutingIpv6 OK";
return true;
}

View File

@@ -30,11 +30,11 @@ public:
bool routeDelete(const QString &ip, const QString &gw, const int &sock);
bool routeDeleteList(const QString &gw, const QStringList &ips);
QString getgatewayandiface();
bool flushDns();
void flushDns();
bool createTun(const QString &dev, const QString &subnet);
bool deleteTun(const QString &dev);
bool StartRoutingIpv6();
bool StopRoutingIpv6();
void StartRoutingIpv6();
void StopRoutingIpv6();
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
public slots:

View File

@@ -166,7 +166,7 @@ bool RouterMac::deleteTun(const QString &dev)
return true;
}
bool RouterMac::flushDns()
void RouterMac::flushDns()
{
// sudo killall -HUP mDNSResponder
QProcess p;
@@ -174,7 +174,5 @@ bool RouterMac::flushDns()
p.start("killall", QStringList() << "-HUP" << "mDNSResponder");
p.waitForFinished();
qDebug().noquote() << "OUTPUT killall -HUP mDNSResponder: " + p.readAll();
return true;
}

View File

@@ -29,7 +29,7 @@ public:
bool clearSavedRoutes();
bool routeDelete(const QString &ip, const QString &gw);
bool routeDeleteList(const QString &gw, const QStringList &ips);
bool flushDns();
void flushDns();
bool createTun(const QString &dev, const QString &subnet);
bool deleteTun(const QString &dev);
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);

View File

@@ -273,7 +273,7 @@ int RouterWin::routeDeleteList(const QString &gw, const QStringList &ips)
return success_count;
}
bool RouterWin::flushDns()
void RouterWin::flushDns()
{
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
@@ -281,7 +281,6 @@ bool RouterWin::flushDns()
p.start(command);
p.waitForFinished();
return true;
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
}
@@ -445,7 +444,7 @@ bool RouterWin::updateResolvers(const QString& ifname, const QList<QHostAddress>
}
bool RouterWin::StopRoutingIpv6()
void RouterWin::StopRoutingIpv6()
{
{
QProcess p;
@@ -465,10 +464,9 @@ bool RouterWin::StopRoutingIpv6()
p.start(command);
p.waitForFinished();
}
return true;
}
bool RouterWin::StartRoutingIpv6()
void RouterWin::StartRoutingIpv6()
{
{
QProcess p;
@@ -488,6 +486,5 @@ bool RouterWin::StartRoutingIpv6()
p.start(command);
p.waitForFinished();
}
return true;
}

View File

@@ -39,11 +39,11 @@ public:
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
int routeDeleteList(const QString &gw, const QStringList &ips);
bool flushDns();
void flushDns();
void resetIpStack();
bool StartRoutingIpv6();
bool StopRoutingIpv6();
void StartRoutingIpv6();
void StopRoutingIpv6();
void suspendWcmSvc(bool suspend);
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);