Compare commits

...

63 Commits

Author SHA1 Message Date
pokamest b9ee600fa8 Version bump 4.2.1.0 2024-01-20 18:29:38 +00:00
pokamest 407b2e8bf7 Merge pull request #517 from amnezia-vpn/dev
Release 4.2.1.0
2024-01-20 09:40:31 -08:00
pokamest f9123e7b71 Merge pull request #516 from amnezia-vpn/bugfix/easy-setup-default-container
fixed easy setup default container selection
2024-01-20 09:18:57 -08:00
pokamest 0f9ed4e69c Merge pull request #515 from amnezia-vpn/fix/android-wg
set wg PresharedKey parameter as optional
2024-01-20 09:16:16 -08:00
vladimir.kuznetsov 9dfc95bac0 fixed easy setup default container selection 2024-01-20 23:28:20 +07:00
albexk 9b7914538f set wg PresharedKey parameter as optional 2024-01-20 18:43:26 +03:00
pokamest 85c9cd260d Merge pull request #514 from amnezia-vpn/fix/android-camera
Hide "QR-code" button from config import menu for Android devices wit…
2024-01-20 07:42:55 -08:00
pokamest 73751cc049 Merge pull request #513 from amnezia-vpn/bugfix/issue499
Fix import of some native WG configs
2024-01-20 07:27:54 -08:00
albexk 2b61c48303 Hide "QR-code" button from config import menu for Android devices without camera 2024-01-20 18:20:06 +03:00
Mykola Baibuz 47b03f2bf4 PSK paramater is not mandatory 2024-01-20 08:44:48 -05:00
albexk 3e02dfef63 Refactoring Android logging (#511)
Refactoring Android logging
2024-01-20 13:40:12 +00:00
Mykola Baibuz 4176d0130a Fix import of some native WG configs 2024-01-20 08:35:24 -05:00
pokamest f6175c2c69 Merge pull request #512 from amnezia-vpn/fix/line-ending
Fix amnezia_application line-ending
2024-01-20 05:04:22 -08:00
albexk b8b423ca19 Fix amnezia_application line-ending 2024-01-20 16:02:35 +03:00
pokamest 3253de9384 Merge pull request #509 from amnezia-vpn/featute/amneziawg-apple
Change to amneziawg-apple
2024-01-19 02:17:02 -08:00
Igor Sorokin 51070635a5 Change to amneziawg-apple 2024-01-19 03:27:44 +03:00
pokamest 9b533f1ba6 Merge pull request #507 from amnezia-vpn/linux-installer
Improve Linux installer
2024-01-18 09:39:00 -08:00
tiaga 9371dd405e Improve Linux installer
Pack installer to a .tar archive in order to save executable bit for `AmneziaVPN_Linux_Installer.bin`.
2024-01-19 00:16:19 +07:00
pokamest 27d8108e55 Merge pull request #501 from amnezia-vpn/feature/api-spit-tunneling
for servers received via api, ignore the split tunneling settings
2024-01-18 08:51:12 -08:00
pokamest 820a3f0b77 Merge pull request #506 from amnezia-vpn/bugfix/client-management-ss-cloak
fixed append/rename/revoke client for cloak and ss on client management panel
2024-01-18 07:09:14 -08:00
pokamest 3d61b20271 Merge pull request #505 from amnezia-vpn/fix/readme
Update README.md
2024-01-18 07:03:08 -08:00
vladimir.kuznetsov eec81f8124 fixed append/rename/revoke client for cloak and ss on client management panel 2024-01-18 21:29:56 +07:00
Igor Sorokin d7fbddb97f Update README.md 2024-01-18 17:13:04 +03:00
pokamest 9170cb0318 Merge pull request #498 from amnezia-vpn/sort-installer-containers-first
show installed protocols first in protocols tab
2024-01-18 05:57:16 -08:00
pokamest 76dec6fa9c Merge pull request #504 from amnezia-vpn/feature/android-deploy
Add Android App Bundle build and upload
2024-01-18 05:56:50 -08:00
pokamest 4e3edcfc0a Merge pull request #503 from amnezia-vpn/fix/ipv6
Fix IPv6 problem
2024-01-18 05:56:07 -08:00
albexk ad21d7ab64 Add Android App Bundle build and upload
Up upload-artifact action to version 4
2024-01-18 16:06:37 +03:00
Igor Sorokin f576eb509d Fix IPv6 problem 2024-01-18 15:25:36 +03:00
vladimir.kuznetsov 33e229d0b2 added blocking of the page with dns settings for servers received by API 2024-01-17 23:38:20 +07:00
pokamest 78420d617b Merge pull request #502 from amnezia-vpn/fix/android-clipboard
Fix/android clipboard
2024-01-17 07:24:24 -08:00
albexk 2552e33d64 Add Android TextArea clipboard workaround 2024-01-17 17:46:54 +03:00
albexk 301141c755 Remove Android TextField workaround
It has been fixed in Qt 6.5.3, 6.6.0
2024-01-17 17:43:54 +03:00
vladimir.kuznetsov fac57ac89a for servers received via api, ignore the split tunneling settings 2024-01-17 11:28:57 +07:00
pokamest da5ad0a845 Merge pull request #500 from amnezia-vpn/fix/android-keyboard
Restore previous Android keyboard behavior
2024-01-16 05:50:28 -08:00
albexk 60aeefe1c2 Restore previous Android keyboard behaviour
There is a Qt bug related to this behaviour: https://bugreports.qt.io/browse/QTBUG-41170
2024-01-16 15:06:34 +03:00
Morteza Sherafati d527f6a5ce show installed protocols first in protocols tab 2024-01-15 20:18:10 +00:00
pokamest 2802b42747 Merge pull request #491 from amnezia-vpn/bugfix/current-processed-server-selection
fixed current processed server selection after import/install new server
2024-01-15 05:43:34 -08:00
pokamest 9c30b0f034 Merge pull request #496 from amnezia-vpn/dev
Release 4.2.0.1
2024-01-15 05:35:29 -05:00
pokamest 66c5d2f0a8 Updated submodule qtkeychain 2024-01-15 10:06:20 +00:00
pokamest 6dbf4ac62c Release 4.2.0.1 2024-01-15 09:28:48 +00:00
pokamest ae2872830b Updated submodule qtkeychain 2024-01-15 09:20:13 +00:00
pokamest 6f4a3587e4 Merge pull request #495 from amnezia-vpn/feature/update-awg-core
Update AWG core (v0.1.8)
2024-01-14 17:33:10 -05:00
Mykola Baibuz 145f51906e Update AWG core (v0.1.8) 2024-01-14 21:58:53 +02:00
pokamest 12e72bc74b Merge pull request #481 from amnezia-vpn/refactoring/android
Refactor Android open file method
2024-01-13 06:46:25 -05:00
vladimir.kuznetsov 88cd5825d3 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD 2024-01-11 20:26:06 +07:00
pokamest ecdad2a315 Merge branch 'dev' into refactoring/android 2024-01-11 13:01:03 +00:00
pokamest 0398ddd6a2 Merge pull request #488 from amnezia-vpn/bugfix/split-tunnel-reset-on-app-reset
bugfix/split-tunnel-reset-on-app-reset
2024-01-11 07:58:04 -05:00
pokamest 50ea4d3b0f Merge pull request #453 from amnezia-vpn/bugfix/windows-crush-on-utf8-symbolos
added conversion using the system locale
2024-01-11 07:51:22 -05:00
pokamest 77be8169f2 Merge pull request #493 from amnezia-vpn/bugfix/secureqsettings-deadlock-2
now value and setValue of secureQSettings are always called in the main thread
2024-01-11 07:48:42 -05:00
lunardunno 7a435f76b6 ArchLinux_support (#463)
Arch linux support (#464)
2024-01-10 20:15:14 +00:00
pokamest 42949e0dea Merge pull request #492 from amnezia-vpn/bugfix/append-client
fixed insert rows count in appendClient function
2024-01-10 12:42:07 -05:00
pokamest 26db423232 Merge pull request #489 from amnezia-vpn/bugfix/remote-links-on-site
added the ability to change the site link via a translation file
2024-01-10 12:40:19 -05:00
vladimir.kuznetsov 645cf52803 now value and setValue of secureQSettings are always called in the main thread 2024-01-10 23:05:22 +07:00
vladimir.kuznetsov 673b8ad5b2 fixed insert rows count in appendClient function 2024-01-10 11:55:32 +07:00
vladimir.kuznetsov 2a03834bb2 fixed current processed server selection after import/install new server 2024-01-09 00:25:18 +07:00
vladimir.kuznetsov 0690d86e52 added the ability to change the site link via a translation file 2024-01-06 20:42:43 +07:00
vladimir.kuznetsov bb8a11d110 added reset routeMode when resetting application settings 2024-01-06 20:38:41 +07:00
vladimir.kuznetsov 69dd415ab5 Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into bugfix/windows-crush-on-utf8-symbolos 2023-12-28 14:47:19 +07:00
albexk e605f549bd Merge branch 'dev' into refactoring/android 2023-12-26 18:23:53 +03:00
albexk a961932b2e Refactor AndroidUtils 2023-12-26 17:41:49 +03:00
albexk e8cc80f046 Refactor Android open file method
Fix some bugs with mimetype filters when the Qt mimetype database does not match the Android database
2023-12-26 16:30:33 +03:00
vladimir.kuznetsov 67f29ac483 added conversion using the system locale 2023-12-26 13:36:21 +07:00
albexk 7437d47d92 Remove unnecessary permission RECEIVE_BOOT_COMPLETED 2023-12-25 15:16:22 +03:00
75 changed files with 1851 additions and 1327 deletions
+34 -14
View File
@@ -48,14 +48,18 @@ jobs:
export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin
bash deploy/build_linux.sh
- name: 'Pack installer'
run: cd deploy && tar -cf AmneziaVPN_Linux_Installer.tar AmneziaVPN_Linux_Installer.bin
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Linux_installer
path: deploy/AmneziaVPN_Linux_Installer
name: AmneziaVPN_Linux_installer.tar
path: deploy/AmneziaVPN_Linux_Installer.tar
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Linux_unpacked
path: deploy/AppDir
@@ -110,13 +114,14 @@ jobs:
call deploy\\build_windows.bat
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Windows_installer
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_Windows_unpacked
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
@@ -200,7 +205,7 @@ jobs:
IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }}
# - name: 'Upload appstore .ipa and dSYMs to artifacts'
# uses: actions/upload-artifact@v3
# uses: actions/upload-artifact@v4
# with:
# name: app-store ipa & dsyms
# path: |
@@ -255,13 +260,14 @@ jobs:
bash deploy/build_macos.sh
- name: 'Upload installer artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_installer
path: AmneziaVPN.dmg
retention-days: 7
- name: 'Upload unpacked artifact'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN_MacOS_unpacked
path: deploy/build/client/AmneziaVPN.app
@@ -277,6 +283,7 @@ jobs:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.1
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
BUILD_AAB: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}
steps:
- name: 'Install desktop Qt'
@@ -375,32 +382,45 @@ jobs:
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash
run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
run: ./deploy/build_android.sh ${{ env.BUILD_AAB == 'true' && '--aab' || '' }} --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload x86_64 apk'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-x86_64
path: deploy/build/AmneziaVPN-x86_64-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
compression-level: 0
retention-days: 7
- name: 'Upload aab'
if: ${{ env.BUILD_AAB == 'true' }}
uses: actions/upload-artifact@v4
with:
name: AmneziaVPN-android
path: deploy/build/AmneziaVPN-release.aab
compression-level: 0
retention-days: 7
+3 -3
View File
@@ -22,6 +22,6 @@
[submodule "client/3rd-prebuilt"]
path = client/3rd-prebuilt
url = https://github.com/amnezia-vpn/3rd-prebuilt
[submodule "client/3rd/awg-apple"]
path = client/3rd/awg-apple
url = https://github.com/amnezia-vpn/awg-apple
[submodule "client/3rd/amneziawg-apple"]
path = client/3rd/amneziawg-apple
url = https://github.com/amnezia-vpn/amneziawg-apple
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.1.0.1
project(${PROJECT} VERSION 4.2.1.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 39)
set(APP_ANDROID_VERSION_CODE 42)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
+12 -3
View File
@@ -36,7 +36,7 @@ AmneziaVPN uses a number of open source projects to work:
Make sure to pull all submodules after checking out the repo.
```bash
git submodule update --init
git submodule update --init --recursive
```
## Development
@@ -50,7 +50,15 @@ Look deploy folder for build scripts.
1. First, make sure you have [XCode](https://developer.apple.com/xcode/) installed, at least version 14 or higher.
2. We use QT to generate the XCode project. we need QT version 6.4. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html)
2. We use QT to generate the XCode project. we need QT version 6.6.1. Install QT for macos in [here](https://doc.qt.io/qt-6/macos.html) or [QT Online Installer](https://www.qt.io/download-open-source). Required modules:
- macOS
- iOS
- Qt 5 Compatibility Module
- Qt Shader Tools
- Additional Libraries:
- Qt Image Formats
- Qt Multimedia
- Qt Remote Objects
3. Install cmake is require. We recommend cmake version 3.25. You can install cmake in [here](https://cmake.org/download/)
@@ -66,10 +74,11 @@ gomobile init
5. Build project
```bash
export QT_BIN_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/ios/bin"
export QT_MACOS_ROOT_DIR="<PATH-TO-QT-FOLDER>/Qt/<QT-VERSION>/macos"
export QT_IOS_BIN=$QT_BIN_DIR
export PATH=$PATH:~/go/bin
mkdir build-ios
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_BIN_DIR
$QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
```
Replace PATH-TO-QT-FOLDER and QT-VERSION to your environment
Vendored Submodule
+1
Submodule client/3rd/awg-apple deleted from 233eda6760
+389 -380
View File
@@ -1,380 +1,389 @@
#include "amnezia_application.h"
#include <QClipboard>
#include <QFontDatabase>
#include <QMimeData>
#include <QQuickStyle>
#include <QResource>
#include <QStandardPaths>
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QQuickItem>
#include "logger.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
#if defined(Q_OS_ANDROID)
#include "platforms/android/android_controller.h"
#endif
#include "protocols/qml_register_protocols.h"
#if defined(Q_OS_IOS)
#include "platforms/ios/ios_controller.h"
#endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
#else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
int timeout, const QString &userData)
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif
{
setQuitOnLastWindowClosed(false);
// Fix config file permissions
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
{
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
s.setValue("permFixed", true);
}
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif
m_settings = std::shared_ptr<Settings>(new Settings);
}
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
delete m_engine;
}
}
void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
QObject::connect(
m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
initModels();
loadTranslator();
initControllers();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
[this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qCritical() << QString("Init failed");
if (m_vpnConnection)
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
#endif
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
m_pageController->replaceStartPage();
m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath);
});
#endif
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState);
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(),
&PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
&ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
&NotificationHandler::onTranslationsUpdated);
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
if (m_settings->isSaveLogs()) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#ifdef Q_OS_WIN
if (m_parser.isSet("a"))
m_pageController->showOnStartup();
else
emit m_pageController->raiseMainWindow();
#else
m_pageController->showOnStartup();
#endif
// TODO - fix
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (isPrimary()) {
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
qDebug() << "Secondary instance started, showing this window instead";
emit m_pageController->raiseMainWindow();
});
}
#endif
// Android TextField clipboard workaround
// https://bugreports.qt.io/browse/QTBUG-113461
#ifdef Q_OS_ANDROID
QObject::connect(qApp, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) {
if (state == Qt::ApplicationActive) {
if (qApp->clipboard()->mimeData()->formats().contains("text/html")) {
QTextDocument doc;
doc.setHtml(qApp->clipboard()->mimeData()->html());
qApp->clipboard()->setText(doc.toPlainText());
}
}
});
#endif
}
void AmneziaApplication::registerTypes()
{
qRegisterMetaType<ServerCredentials>("ServerCredentials");
qRegisterMetaType<DockerContainer>("DockerContainer");
qRegisterMetaType<TransportProto>("TransportProto");
qRegisterMetaType<Proto>("Proto");
qRegisterMetaType<ServiceType>("ServiceType");
declareQmlProtocolEnum();
declareQmlContainerEnum();
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
m_containerProps.reset(new ContainerProps());
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
m_protocolProps.reset(new ProtocolProps());
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
"ContainersModelFilters");
//
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
}
void AmneziaApplication::loadFonts()
{
QQuickStyle::setStyle("Basic");
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
void AmneziaApplication::loadTranslator()
{
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
}
void AmneziaApplication::updateTranslator(const QLocale &locale)
{
if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator.get());
}
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
}
} else {
m_settings->setAppLanguage(QLocale::English);
}
m_engine->retranslate();
emit translationsUpdated();
}
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
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.process(*this);
if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
return false;
}
return true;
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
}
void AmneziaApplication::initModels()
{
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
&ContainersModel::setDefaultContainer);
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked,
m_serversModel.get(), &ServersModel::clearCachedProfile);
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials) {
m_serversModel->reloadContainerConfig();
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
emit m_configurator->clientModelUpdated();
});
}
void AmneziaApplication::initControllers()
{
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
&ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel,
m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(),
&ServersModel::toggleAmneziaDns);
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
}
#include "amnezia_application.h"
#include <QClipboard>
#include <QFontDatabase>
#include <QMimeData>
#include <QQuickStyle>
#include <QResource>
#include <QStandardPaths>
#include <QTextDocument>
#include <QTimer>
#include <QTranslator>
#include <QQuickItem>
#include "logger.h"
#include "version.h"
#include "platforms/ios/QRCodeReaderBase.h"
#if defined(Q_OS_ANDROID)
#include "platforms/android/android_controller.h"
#endif
#include "protocols/qml_register_protocols.h"
#if defined(Q_OS_IOS)
#include "platforms/ios/ios_controller.h"
#endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
#else
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options,
int timeout, const QString &userData)
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
#endif
{
setQuitOnLastWindowClosed(false);
// Fix config file permissions
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
{
QSettings s(ORGANIZATION_NAME, APPLICATION_NAME);
s.setValue("permFixed", true);
}
QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/"
+ ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf";
QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
#endif
m_settings = std::shared_ptr<Settings>(new Settings);
}
AmneziaApplication::~AmneziaApplication()
{
m_vpnConnectionThread.quit();
m_vpnConnectionThread.wait(3000);
if (m_engine) {
QObject::disconnect(m_engine, 0, 0, 0);
delete m_engine;
}
}
void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
QObject::connect(
m_engine, &QQmlApplicationEngine::objectCreated, this,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
m_vpnConnection.reset(new VpnConnection(m_settings, m_configurator));
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
initModels();
loadTranslator();
initControllers();
#ifdef Q_OS_ANDROID
if(!AndroidController::initLogging()) {
qFatal("Android logging initialization failed");
}
AndroidController::instance()->setSaveLogs(m_settings->isSaveLogs());
connect(m_settings.get(), &Settings::saveLogsChanged,
AndroidController::instance(), &AndroidController::setSaveLogs);
connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
[this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qFatal("Android controller initialization failed");
}
connect(AndroidController::instance(), &AndroidController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
#endif
#ifdef Q_OS_IOS
IosController::Instance()->initialize();
connect(IosController::Instance(), &IosController::importConfigFromOutside, [this](QString data) {
m_pageController->replaceStartPage();
m_importController->extractConfigFromData(data);
m_pageController->goToPageViewConfig();
});
connect(IosController::Instance(), &IosController::importBackupFromOutside, [this](QString filePath) {
m_pageController->replaceStartPage();
m_pageController->goToPageSettingsBackup();
m_settingsController->importBackupFromOutside(filePath);
});
#endif
m_notificationHandler.reset(NotificationHandler::create(nullptr));
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
&NotificationHandler::setConnectionState);
connect(m_notificationHandler.get(), &NotificationHandler::raiseRequested, m_pageController.get(),
&PageController::raiseMainWindow);
connect(m_notificationHandler.get(), &NotificationHandler::connectRequested, m_connectionController.get(),
&ConnectionController::openConnection);
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection);
connect(this, &AmneziaApplication::translationsUpdated, m_notificationHandler.get(),
&NotificationHandler::onTranslationsUpdated);
m_engine->load(url);
m_systemController->setQmlRoot(m_engine->rootObjects().value(0));
#ifndef Q_OS_ANDROID
if (m_settings->isSaveLogs()) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
#endif
#ifdef Q_OS_WIN
if (m_parser.isSet("a"))
m_pageController->showOnStartup();
else
emit m_pageController->raiseMainWindow();
#else
m_pageController->showOnStartup();
#endif
// TODO - fix
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (isPrimary()) {
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
qDebug() << "Secondary instance started, showing this window instead";
emit m_pageController->raiseMainWindow();
});
}
#endif
// Android TextArea clipboard workaround
// Text from TextArea always has "text/html" mime-type:
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
// Next, html is created for this mime-type:
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1885
// And this html goes to the Androids clipboard, i.e. text from TextArea is always copied as richText:
// /qt/6.6.1/Src/qtbase/src/plugins/platforms/android/androidjniclipboard.cpp:46
// So we catch all the copies to the clipboard and clear them from "text/html"
#ifdef Q_OS_ANDROID
connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, []() {
auto clipboard = QGuiApplication::clipboard();
if (clipboard->mimeData()->hasHtml()) {
clipboard->setText(clipboard->text());
}
});
#endif
}
void AmneziaApplication::registerTypes()
{
qRegisterMetaType<ServerCredentials>("ServerCredentials");
qRegisterMetaType<DockerContainer>("DockerContainer");
qRegisterMetaType<TransportProto>("TransportProto");
qRegisterMetaType<Proto>("Proto");
qRegisterMetaType<ServiceType>("ServiceType");
declareQmlProtocolEnum();
declareQmlContainerEnum();
qmlRegisterType<QRCodeReader>("QRCodeReader", 1, 0, "QRCodeReader");
m_containerProps.reset(new ContainerProps());
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", m_containerProps.get());
m_protocolProps.reset(new ProtocolProps());
qmlRegisterSingletonInstance("ProtocolProps", 1, 0, "ProtocolProps", m_protocolProps.get());
qmlRegisterSingletonType(QUrl("qrc:/ui/qml/Filters/ContainersModelFilters.qml"), "ContainersModelFilters", 1, 0,
"ContainersModelFilters");
//
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
}
void AmneziaApplication::loadFonts()
{
QQuickStyle::setStyle("Basic");
QFontDatabase::addApplicationFont(":/fonts/pt-root-ui_vf.ttf");
}
void AmneziaApplication::loadTranslator()
{
auto locale = m_settings->getAppLanguage();
m_translator.reset(new QTranslator());
updateTranslator(locale);
}
void AmneziaApplication::updateTranslator(const QLocale &locale)
{
if (!m_translator->isEmpty()) {
QCoreApplication::removeTranslator(m_translator.get());
}
QString strFileName = QString(":/translations/amneziavpn") + QLatin1String("_") + locale.name() + ".qm";
if (m_translator->load(strFileName)) {
if (QCoreApplication::installTranslator(m_translator.get())) {
m_settings->setAppLanguage(locale);
}
} else {
m_settings->setAppLanguage(QLocale::English);
}
m_engine->retranslate();
emit translationsUpdated();
}
bool AmneziaApplication::parseCommands()
{
m_parser.setApplicationDescription(APPLICATION_NAME);
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.process(*this);
if (m_parser.isSet(c_cleanup)) {
Logger::cleanUp();
QTimer::singleShot(100, this, [this] { quit(); });
exec();
return false;
}
return true;
}
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
{
return m_engine;
}
void AmneziaApplication::initModels()
{
m_containersModel.reset(new ContainersModel(this));
m_engine->rootContext()->setContextProperty("ContainersModel", m_containersModel.get());
m_serversModel.reset(new ServersModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ServersModel", m_serversModel.get());
connect(m_serversModel.get(), &ServersModel::containersUpdated, m_containersModel.get(),
&ContainersModel::updateModel);
connect(m_serversModel.get(), &ServersModel::defaultContainerChanged, m_containersModel.get(),
&ContainersModel::setDefaultContainer);
m_containersModel->setDefaultContainer(m_serversModel->getDefaultContainer()); // make better?
m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
connect(m_languageModel.get(), &LanguageModel::updateTranslations, this, &AmneziaApplication::updateTranslator);
connect(this, &AmneziaApplication::translationsUpdated, m_languageModel.get(), &LanguageModel::translationsUpdated);
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_protocolsModel.reset(new ProtocolsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get());
m_openVpnConfigModel.reset(new OpenVpnConfigModel(this));
m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get());
m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this));
m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get());
m_cloakConfigModel.reset(new CloakConfigModel(this));
m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get());
m_wireGuardConfigModel.reset(new WireGuardConfigModel(this));
m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get());
m_awgConfigModel.reset(new AwgConfigModel(this));
m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get());
#ifdef Q_OS_WINDOWS
m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this));
m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get());
#endif
m_sftpConfigModel.reset(new SftpConfigModel(this));
m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get());
m_clientManagementModel.reset(new ClientManagementModel(m_settings, this));
m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get());
connect(m_clientManagementModel.get(), &ClientManagementModel::adminConfigRevoked,
m_serversModel.get(), &ServersModel::clearCachedProfile);
connect(m_configurator.get(), &VpnConfigurator::newVpnConfigCreated, this,
[this](const QString &clientId, const QString &clientName, const DockerContainer container,
ServerCredentials credentials) {
m_serversModel->reloadContainerConfig();
m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
emit m_configurator->clientModelUpdated();
});
}
void AmneziaApplication::initControllers()
{
m_connectionController.reset(new ConnectionController(m_serversModel, m_containersModel, m_vpnConnection));
m_engine->rootContext()->setContextProperty("ConnectionController", m_connectionController.get());
connect(this, &AmneziaApplication::translationsUpdated, m_connectionController.get(),
&ConnectionController::onTranslationsUpdated);
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());
m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
connect(m_installController.get(), &InstallController::passphraseRequestStarted, m_pageController.get(),
&PageController::showPassphraseRequestDrawer);
connect(m_pageController.get(), &PageController::passphraseRequestDrawerClosed, m_installController.get(),
&InstallController::setEncryptedPassphrase);
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated);
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
m_exportController.reset(new ExportController(m_serversModel, m_containersModel, m_clientManagementModel,
m_settings, m_configurator));
m_engine->rootContext()->setContextProperty("ExportController", m_exportController.get());
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
}
connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled , m_serversModel.get(),
&ServersModel::toggleAmneziaDns);
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_cloudController.reset(new ApiController(m_serversModel, m_containersModel));
m_engine->rootContext()->setContextProperty("ApiController", m_cloudController.get());
}
+127 -127
View File
@@ -1,127 +1,127 @@
#ifndef AMNEZIA_APPLICATION_H
#define AMNEZIA_APPLICATION_H
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "settings.h"
#include "vpnconnection.h"
#include "configurators/vpn_configurator.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/apiController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
#include "ui/notificationhandler.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS SingleApplication
#define QAPPLICATION_CLASS QApplication
#include "singleapplication.h"
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
{
Q_OBJECT
public:
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]);
#else
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
const QString &userData = {});
#endif
virtual ~AmneziaApplication();
void init();
void registerTypes();
void loadFonts();
void loadTranslator();
void updateTranslator(const QLocale &locale);
bool parseCommands();
QQmlApplicationEngine *qmlEngine() const;
signals:
void translationsUpdated();
private:
void initModels();
void initControllers();
QQmlApplicationEngine *m_engine {};
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
QSharedPointer<ContainerProps> m_containerProps;
QSharedPointer<ProtocolProps> m_protocolProps;
QSharedPointer<QTranslator> m_translator;
QCommandLineParser m_parser;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QScopedPointer<NotificationHandler> m_notificationHandler;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<ApiController> m_cloudController;
};
#endif // AMNEZIA_APPLICATION_H
#ifndef AMNEZIA_APPLICATION_H
#define AMNEZIA_APPLICATION_H
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QThread>
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#include <QGuiApplication>
#else
#include <QApplication>
#endif
#include "settings.h"
#include "vpnconnection.h"
#include "configurators/vpn_configurator.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/apiController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
#include "ui/notificationhandler.h"
#ifdef Q_OS_WINDOWS
#include "ui/models/protocols/ikev2ConfigModel.h"
#endif
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/openvpnConfigModel.h"
#include "ui/models/protocols/shadowsocksConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/services/sftpConfigModel.h"
#include "ui/models/sites_model.h"
#include "ui/models/clientManagementModel.h"
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
#define AMNEZIA_BASE_CLASS QGuiApplication
#else
#define AMNEZIA_BASE_CLASS SingleApplication
#define QAPPLICATION_CLASS QApplication
#include "singleapplication.h"
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
{
Q_OBJECT
public:
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
AmneziaApplication(int &argc, char *argv[]);
#else
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
const QString &userData = {});
#endif
virtual ~AmneziaApplication();
void init();
void registerTypes();
void loadFonts();
void loadTranslator();
void updateTranslator(const QLocale &locale);
bool parseCommands();
QQmlApplicationEngine *qmlEngine() const;
signals:
void translationsUpdated();
private:
void initModels();
void initControllers();
QQmlApplicationEngine *m_engine {};
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
QSharedPointer<ContainerProps> m_containerProps;
QSharedPointer<ProtocolProps> m_protocolProps;
QSharedPointer<QTranslator> m_translator;
QCommandLineParser m_parser;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QScopedPointer<OpenVpnConfigModel> m_openVpnConfigModel;
QScopedPointer<ShadowSocksConfigModel> m_shadowSocksConfigModel;
QScopedPointer<CloakConfigModel> m_cloakConfigModel;
QScopedPointer<WireGuardConfigModel> m_wireGuardConfigModel;
QScopedPointer<AwgConfigModel> m_awgConfigModel;
#ifdef Q_OS_WINDOWS
QScopedPointer<Ikev2ConfigModel> m_ikev2ConfigModel;
#endif
QScopedPointer<SftpConfigModel> m_sftpConfigModel;
QSharedPointer<VpnConnection> m_vpnConnection;
QThread m_vpnConnectionThread;
QScopedPointer<NotificationHandler> m_notificationHandler;
QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
QScopedPointer<ExportController> m_exportController;
QScopedPointer<SettingsController> m_settingsController;
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<ApiController> m_cloudController;
};
#endif // AMNEZIA_APPLICATION_H
+9 -4
View File
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<!-- Leave package attribute for androiddeployqt -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn"
android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
@@ -22,7 +24,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
@@ -32,13 +33,17 @@
android:label="-- %%INSERT_APP_NAME%% --"
android:icon="@mipmap/icon"
android:roundIcon="@mipmap/icon_round"
android:theme="@style/NoActionBar">
android:theme="@style/NoActionBar"
android:fullBackupContent="@xml/backup_content"
android:dataExtractionRules="@xml/data_extraction_rules"
tools:targetApi="s">
<activity
android:name=".AmneziaActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
@@ -147,7 +152,7 @@
android:authorities="org.amnezia.vpn.qtprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
</provider>
</application>
</manifest>
-1
View File
@@ -108,7 +108,6 @@ dependencies {
implementation(project(":cloak"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
@@ -52,7 +52,7 @@ class OpenVpnClient(
// Callback to construct a new tun builder
// Should be called first.
override fun tun_builder_new(): Boolean {
Log.v(TAG, "tun_builder_new")
Log.d(TAG, "tun_builder_new")
configBuilder.clearAddresses()
return true
}
@@ -60,7 +60,7 @@ class OpenVpnClient(
// Callback to set MTU of the VPN interface
// Never called more than once per tun_builder session.
override fun tun_builder_set_mtu(mtu: Int): Boolean {
Log.v(TAG, "tun_builder_set_mtu: $mtu")
Log.d(TAG, "tun_builder_set_mtu: $mtu")
configBuilder.setMtu(mtu)
return true
}
@@ -71,7 +71,7 @@ class OpenVpnClient(
address: String, prefix_length: Int,
gateway: String, ipv6: Boolean, net30: Boolean
): Boolean {
Log.v(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
Log.d(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
configBuilder.addAddress(InetNetwork(address, prefix_length))
return true
}
@@ -80,7 +80,7 @@ class OpenVpnClient(
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
Log.d(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
if (address == "remote_host") return false
configBuilder.addRoute(InetNetwork(address, prefix_length))
return true
@@ -90,7 +90,7 @@ class OpenVpnClient(
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
}
@@ -104,7 +104,7 @@ class OpenVpnClient(
// domain should be routed.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_dns_server: $address, $ipv6")
Log.d(TAG, "tun_builder_add_dns_server: $address, $ipv6")
configBuilder.addDnsServer(parseInetAddress(address))
return true
}
@@ -119,28 +119,28 @@ class OpenVpnClient(
// ignored for that family
// See also Android's VPNService.Builder.allowFamily method
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_allow_family: $af, $allow")
Log.d(TAG, "tun_builder_set_allow_family: $af, $allow")
return true
} */
// Callback to set address of remote server
// Never called more than once per tun_builder session.
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_remote_address: $address, $ipv6")
Log.d(TAG, "tun_builder_set_remote_address: $address, $ipv6")
return true
}
// Optional callback that indicates OSI layer, should be 2 or 3.
// Defaults to 3.
override fun tun_builder_set_layer(layer: Int): Boolean {
Log.v(TAG, "tun_builder_set_layer: $layer")
Log.d(TAG, "tun_builder_set_layer: $layer")
return layer == 3
}
// Callback to set the session name
// Never called more than once per tun_builder session.
override fun tun_builder_set_session_name(name: String): Boolean {
Log.v(TAG, "tun_builder_set_session_name: $name")
Log.d(TAG, "tun_builder_set_session_name: $name")
return true
}
@@ -149,7 +149,7 @@ class OpenVpnClient(
// if the tunnel could not be established.
// Always called last after tun_builder session has been configured.
override fun tun_builder_establish(): Int {
Log.v(TAG, "tun_builder_establish")
Log.d(TAG, "tun_builder_establish")
return establish(configBuilder)
}
@@ -159,7 +159,7 @@ class OpenVpnClient(
// flags are defined in RGWFlags (rgwflags.hpp).
// Never called more than once per tun_builder session.
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
Log.v(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
Log.d(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
if (ipv4) {
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
@@ -176,7 +176,7 @@ class OpenVpnClient(
// reroute_dns parameter.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_search_domain(domain: String): Boolean {
Log.v(TAG, "tun_builder_add_search_domain: $domain")
Log.d(TAG, "tun_builder_add_search_domain: $domain")
configBuilder.setSearchDomain(domain)
return true
}
@@ -184,7 +184,7 @@ class OpenVpnClient(
// Callback to set the HTTP proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_http: $host, $port")
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
@@ -199,7 +199,7 @@ class OpenVpnClient(
// Callback to set the HTTPS proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_https: $host, $port")
Log.d(TAG, "tun_builder_set_proxy_https: $host, $port")
return false
}
@@ -208,7 +208,7 @@ class OpenVpnClient(
// to exclude them from the VPN network are generated
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
Log.v(TAG, "tun_builder_get_local_networks: $ipv6")
Log.d(TAG, "tun_builder_get_local_networks: $ipv6")
val networks = ClientAPI_StringVec()
for (address in getLocalNetworks(ipv6)) {
networks.add(address.toString())
@@ -222,21 +222,21 @@ class OpenVpnClient(
// tun_builder_reroute_gw. Route metric is ignored
// if < 0.
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
Log.v(TAG, "tun_builder_set_route_metric_default: $metric")
Log.d(TAG, "tun_builder_set_route_metric_default: $metric")
return super.tun_builder_set_route_metric_default(metric)
} */
// Callback to add a host which should bypass the proxy
// May be called more than once per tun_builder session
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
Log.v(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
Log.d(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
return super.tun_builder_add_proxy_bypass(bypass_host)
} */
// Callback to set the proxy "Auto Config URL"
// Never called more than once per tun_builder session.
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
Log.v(TAG, "tun_builder_set_proxy_auto_config_url: $url")
Log.d(TAG, "tun_builder_set_proxy_auto_config_url: $url")
return super.tun_builder_set_proxy_auto_config_url(url)
} */
@@ -245,7 +245,7 @@ class OpenVpnClient(
// May be called more than once per tun_builder session.
// Guaranteed to be called after tun_builder_reroute_gw.
/* override fun tun_builder_add_wins_server(address: String): Boolean {
Log.v(TAG, "tun_builder_add_wins_server: $address")
Log.d(TAG, "tun_builder_add_wins_server: $address")
return super.tun_builder_add_wins_server(address)
} */
@@ -254,7 +254,7 @@ class OpenVpnClient(
// set the "Connection-specific DNS Suffix" property on
// the TAP driver.
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
Log.v(TAG, "tun_builder_set_adapter_domain_suffix: $name")
Log.d(TAG, "tun_builder_set_adapter_domain_suffix: $name")
return super.tun_builder_set_adapter_domain_suffix(name)
} */
@@ -266,13 +266,13 @@ class OpenVpnClient(
// tun_builder_establish_lite() will be called. Otherwise,
// tun_builder_establish() will be called.
/* override fun tun_builder_persist(): Boolean {
Log.v(TAG, "tun_builder_persist")
Log.d(TAG, "tun_builder_persist")
return super.tun_builder_persist()
} */
// Indicates a reconnection with persisted tun state.
/* override fun tun_builder_establish_lite() {
Log.v(TAG, "tun_builder_establish_lite")
Log.d(TAG, "tun_builder_establish_lite")
super.tun_builder_establish_lite()
} */
@@ -280,7 +280,7 @@ class OpenVpnClient(
// If disconnect == true, then the teardown is occurring
// prior to final disconnect.
/* override fun tun_builder_teardown(disconnect: Boolean) {
Log.v(TAG, "tun_builder_teardown: $disconnect")
Log.d(TAG, "tun_builder_teardown: $disconnect")
super.tun_builder_teardown(disconnect)
} */
@@ -290,7 +290,7 @@ class OpenVpnClient(
// Parse OpenVPN configuration file.
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
Log.v(TAG, "eval_config")
Log.d(TAG, "eval_config")
return super.eval_config(arg0)
}
@@ -299,7 +299,7 @@ class OpenVpnClient(
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
override fun connect(): ClientAPI_Status {
Log.v(TAG, "connect")
Log.d(TAG, "connect")
return super.connect()
}
@@ -307,7 +307,7 @@ class OpenVpnClient(
// Will be called from the thread executing connect().
// The remote and ipv6 are the remote host this socket will connect to
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
Log.v(TAG, "socket_protect: $socket, $remote, $ipv6")
Log.d(TAG, "socket_protect: $socket, $remote, $ipv6")
return protect(socket)
}
@@ -315,7 +315,7 @@ class OpenVpnClient(
// May be called asynchronously from a different thread
// when connect() is running.
override fun stop() {
Log.v(TAG, "stop")
Log.d(TAG, "stop")
super.stop()
}
@@ -323,21 +323,21 @@ class OpenVpnClient(
// when network is down. May be called from a different thread
// when connect() is running.
override fun pause(reason: String) {
Log.v(TAG, "pause: $reason")
Log.d(TAG, "pause: $reason")
super.pause(reason)
}
// Resume the client after it has been paused. May be called from a
// different thread when connect() is running.
override fun resume() {
Log.v(TAG, "resume")
Log.d(TAG, "resume")
super.resume()
}
// Do a disconnect/reconnect cycle n seconds from now. May be called
// from a different thread when connect() is running.
override fun reconnect(seconds: Int) {
Log.v(TAG, "reconnect")
Log.d(TAG, "reconnect: $seconds")
super.reconnect(seconds)
}
@@ -346,14 +346,14 @@ class OpenVpnClient(
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
// state.
override fun pause_on_connection_timeout(): Boolean {
Log.v(TAG, "pause_on_connection_timeout")
Log.d(TAG, "pause_on_connection_timeout")
return false
}
// Return information about the most recent connection. Should be called
// after an event of type "CONNECTED".
/* override fun connection_info(): ClientAPI_ConnectionInfo {
Log.v(TAG, "connection_info")
Log.d(TAG, "connection_info")
return super.connection_info()
} */
@@ -366,7 +366,7 @@ class OpenVpnClient(
override fun event(event: ClientAPI_Event) {
val name = event.name
val info = event.info
Log.v(TAG, "OpenVpn event: $name: $info")
Log.d(TAG, "OpenVpn event: $name: $info")
when (name) {
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
"CONNECTED" -> state.value = CONNECTED
@@ -398,31 +398,31 @@ class OpenVpnClient(
// return transport stats only
override fun transport_stats(): ClientAPI_TransportStats {
Log.v(TAG, "transport_stats")
Log.d(TAG, "transport_stats")
return super.transport_stats()
}
// return a stats value, index should be >= 0 and < stats_n()
/* override fun stats_value(index: Int): Long {
Log.v(TAG, "stats_value: $index")
Log.d(TAG, "stats_value: $index")
return super.stats_value(index)
} */
// return all stats in a bundle
/* override fun stats_bundle(): ClientAPI_LLVector {
Log.v(TAG, "stats_bundle")
Log.d(TAG, "stats_bundle")
return super.stats_bundle()
} */
// return tun stats only
/* override fun tun_stats(): ClientAPI_InterfaceStats {
Log.v(TAG, "tun_stats")
Log.d(TAG, "tun_stats")
return super.tun_stats()
} */
// post control channel message
/* override fun post_cc_msg(msg: String) {
Log.v(TAG, "post_cc_msg: $msg")
Log.d(TAG, "post_cc_msg: $msg")
super.post_cc_msg(msg)
} */
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="." />
</full-backup-content>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="sharedpref" path="." />
</cloud-backup>
<device-transfer>
<exclude domain="sharedpref" path="." />
</device-transfer>
</data-extraction-rules>
@@ -2,8 +2,10 @@ package org.amnezia.vpn
import android.content.ComponentName
import android.content.Intent
import android.content.Intent.EXTRA_MIME_TYPES
import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.Uri
import android.net.VpnService
import android.os.Bundle
@@ -12,11 +14,13 @@ import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.annotation.MainThread
import androidx.core.content.ContextCompat
import java.io.IOException
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.text.RegexOption.IGNORE_CASE
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -35,6 +39,7 @@ private const val TAG = "AmneziaActivity"
private const val CHECK_VPN_PERMISSION_ACTION_CODE = 1
private const val CREATE_FILE_ACTION_CODE = 2
private const val OPEN_FILE_ACTION_CODE = 3
private const val BIND_SERVICE_TIMEOUT = 1000L
class AmneziaActivity : QtActivity() {
@@ -139,7 +144,7 @@ class AmneziaActivity : QtActivity() {
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.v(TAG, "Create Amnezia activity: $intent")
Log.d(TAG, "Create Amnezia activity: $intent")
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
vpnServiceMessenger = IpcMessenger(
onDeadObjectException = ::doUnbindService,
@@ -150,7 +155,7 @@ class AmneziaActivity : QtActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.v(TAG, "onNewIntent: $intent")
Log.d(TAG, "onNewIntent: $intent")
intent?.let(::processIntent)
}
@@ -170,7 +175,7 @@ class AmneziaActivity : QtActivity() {
override fun onStart() {
super.onStart()
Log.v(TAG, "Start Amnezia activity")
Log.d(TAG, "Start Amnezia activity")
mainScope.launch {
qtInitialized.await()
doBindService()
@@ -178,13 +183,13 @@ class AmneziaActivity : QtActivity() {
}
override fun onStop() {
Log.v(TAG, "Stop Amnezia activity")
Log.d(TAG, "Stop Amnezia activity")
doUnbindService()
super.onStop()
}
override fun onDestroy() {
Log.v(TAG, "Destroy Amnezia activity")
Log.d(TAG, "Destroy Amnezia activity")
mainScope.cancel()
super.onDestroy()
}
@@ -201,10 +206,19 @@ class AmneziaActivity : QtActivity() {
}
}
OPEN_FILE_ACTION_CODE -> {
when (resultCode) {
RESULT_OK -> data?.data?.toString() ?: ""
else -> ""
}.let { uri ->
QtAndroidController.onFileOpened(uri)
}
}
CHECK_VPN_PERMISSION_ACTION_CODE -> {
when (resultCode) {
RESULT_OK -> {
Log.v(TAG, "Vpn permission granted")
Log.d(TAG, "Vpn permission granted")
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
checkVpnPermissionCallbacks?.run { onSuccess() }
}
@@ -227,7 +241,7 @@ class AmneziaActivity : QtActivity() {
*/
@MainThread
private fun doBindService() {
Log.v(TAG, "Bind service")
Log.d(TAG, "Bind service")
Intent(this, AmneziaVpnService::class.java).also {
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
}
@@ -238,7 +252,7 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun doUnbindService() {
if (isInBoundState) {
Log.v(TAG, "Unbind service")
Log.d(TAG, "Unbind service")
isWaitingStatus = true
QtAndroidController.onServiceDisconnected()
vpnServiceMessenger.reset()
@@ -273,7 +287,7 @@ class AmneziaActivity : QtActivity() {
@MainThread
private fun checkVpnPermission(onSuccess: () -> Unit, onFail: () -> Unit) {
Log.v(TAG, "Check VPN permission")
Log.d(TAG, "Check VPN permission")
VpnService.prepare(applicationContext)?.let {
checkVpnPermissionCallbacks = CheckVpnPermissionCallbacks(onSuccess, onFail)
startActivityForResult(it, CHECK_VPN_PERMISSION_ACTION_CODE)
@@ -294,7 +308,7 @@ class AmneziaActivity : QtActivity() {
}
private fun connectToVpn(vpnConfig: String) {
Log.v(TAG, "Connect to VPN")
Log.d(TAG, "Connect to VPN")
vpnServiceMessenger.send {
Action.CONNECT.packToMessage {
putString(VPN_CONFIG, vpnConfig)
@@ -303,7 +317,7 @@ class AmneziaActivity : QtActivity() {
}
private fun startVpnService(vpnConfig: String) {
Log.v(TAG, "Start VPN service")
Log.d(TAG, "Start VPN service")
Intent(this, AmneziaVpnService::class.java).apply {
putExtra(VPN_CONFIG, vpnConfig)
}.also {
@@ -312,7 +326,7 @@ class AmneziaActivity : QtActivity() {
}
private fun disconnectFromVpn() {
Log.v(TAG, "Disconnect from VPN")
Log.d(TAG, "Disconnect from VPN")
vpnServiceMessenger.send(Action.DISCONNECT)
}
@@ -356,7 +370,7 @@ class AmneziaActivity : QtActivity() {
@Suppress("unused")
fun saveFile(fileName: String, data: String) {
Log.v(TAG, "Save file $fileName")
Log.d(TAG, "Save file $fileName")
mainScope.launch {
tmpFileContentToSave = data
@@ -371,17 +385,43 @@ class AmneziaActivity : QtActivity() {
}
@Suppress("unused")
fun setNotificationText(title: String, message: String, timerSec: Int) {
Log.v(TAG, "Set notification text")
Log.w(TAG, "Not yet implemented")
fun openFile(filter: String?) {
Log.v(TAG, "Open file with filter: $filter")
val mimeTypes = if (!filter.isNullOrEmpty()) {
val extensionRegex = "\\*\\.[a-z .]+".toRegex(IGNORE_CASE)
val mime = MimeTypeMap.getSingleton()
extensionRegex.findAll(filter).map {
mime.getMimeTypeFromExtension(it.value.drop(2))
}.filterNotNull().toSet()
} else emptySet()
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
Log.v(TAG, "File mimyType filter: $mimeTypes")
when (mimeTypes.size) {
1 -> type = mimeTypes.first()
in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}
else -> type = "*/*"
}
}.also {
startActivityForResult(it, OPEN_FILE_ACTION_CODE)
}
}
@Suppress("unused")
fun cleanupLogs() {
Log.v(TAG, "Cleanup logs")
Log.w(TAG, "Not yet implemented")
fun setNotificationText(title: String, message: String, timerSec: Int) {
Log.v(TAG, "Set notification text")
}
@Suppress("unused")
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
@Suppress("unused")
fun startQrCodeReader() {
Log.v(TAG, "Start camera")
@@ -389,4 +429,29 @@ class AmneziaActivity : QtActivity() {
startActivity(it)
}
}
@Suppress("unused")
fun setSaveLogs(enabled: Boolean) {
Log.d(TAG, "Set save logs: $enabled")
mainScope.launch {
Log.saveLogs = enabled
vpnServiceMessenger.send {
Action.SET_SAVE_LOGS.packToMessage {
putBoolean(SAVE_LOGS, enabled)
}
}
}
}
@Suppress("unused")
fun exportLogsFile(fileName: String) {
Log.v(TAG, "Export logs file")
saveFile(fileName, Log.getLogs())
}
@Suppress("unused")
fun clearLogs() {
Log.v(TAG, "Clear logs")
Log.clearLogs()
}
}
@@ -5,14 +5,20 @@ import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraXConfig
import androidx.core.app.NotificationChannelCompat.Builder
import androidx.core.app.NotificationManagerCompat
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.qtproject.qt.android.bindings.QtApplication
private const val TAG = "AmneziaApplication"
const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
override fun onCreate() {
super.onCreate()
Prefs.init(this)
Log.init(this)
Log.d(TAG, "Create Amnezia application")
createNotificationChannel()
}
@@ -50,6 +50,7 @@ import org.amnezia.vpn.protocol.putStatistics
import org.amnezia.vpn.protocol.putStatus
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.Prefs
import org.amnezia.vpn.util.net.NetworkState
import org.json.JSONException
import org.json.JSONObject
@@ -58,6 +59,8 @@ private const val TAG = "AmneziaVpnService"
const val VPN_CONFIG = "VPN_CONFIG"
const val ERROR_MSG = "ERROR_MSG"
const val SAVE_LOGS = "SAVE_LOGS"
const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK"
private const val PREFS_CONFIG_KEY = "LAST_CONF"
private const val NOTIFICATION_ID = 1337
@@ -118,7 +121,7 @@ class AmneziaVpnService : VpnService() {
Action.CONNECT -> {
val vpnConfig = msg.data.getString(VPN_CONFIG)
saveConfigToPrefs(vpnConfig)
Prefs.save(PREFS_CONFIG_KEY, vpnConfig)
connect(vpnConfig)
}
@@ -135,6 +138,10 @@ class AmneziaVpnService : VpnService() {
}
}
}
Action.SET_SAVE_LOGS -> {
Log.saveLogs = msg.data.getBoolean(SAVE_LOGS)
}
}
}
}
@@ -179,7 +186,7 @@ class AmneziaVpnService : VpnService() {
*/
override fun onCreate() {
super.onCreate()
Log.v(TAG, "Create Amnezia VPN service")
Log.d(TAG, "Create Amnezia VPN service")
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
connectionScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + connectionExceptionHandler)
clientMessenger = IpcMessenger(messengerName = "Client")
@@ -193,15 +200,15 @@ class AmneziaVpnService : VpnService() {
else intent?.component?.packageName != packageName
if (isAlwaysOnCompat) {
Log.v(TAG, "Start service via Always-on")
connect(loadConfigFromPrefs())
Log.d(TAG, "Start service via Always-on")
connect(Prefs.load(PREFS_CONFIG_KEY))
} else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) {
Log.v(TAG, "Start service after permission check")
connect(loadConfigFromPrefs())
Log.d(TAG, "Start service after permission check")
connect(Prefs.load(PREFS_CONFIG_KEY))
} else {
Log.v(TAG, "Start service")
Log.d(TAG, "Start service")
val vpnConfig = intent?.getStringExtra(VPN_CONFIG)
saveConfigToPrefs(vpnConfig)
Prefs.save(PREFS_CONFIG_KEY, vpnConfig)
connect(vpnConfig)
}
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, foregroundServiceTypeCompat)
@@ -237,7 +244,7 @@ class AmneziaVpnService : VpnService() {
}
override fun onRevoke() {
Log.v(TAG, "onRevoke")
Log.d(TAG, "onRevoke")
// Calls to onRevoke() method may not happen on the main thread of the process
mainScope.launch {
disconnect()
@@ -245,7 +252,7 @@ class AmneziaVpnService : VpnService() {
}
override fun onDestroy() {
Log.v(TAG, "Destroy service")
Log.d(TAG, "Destroy service")
runBlocking {
disconnect()
disconnectionJob?.join()
@@ -256,7 +263,7 @@ class AmneziaVpnService : VpnService() {
}
private fun stopService() {
Log.v(TAG, "Stop service")
Log.d(TAG, "Stop service")
// the coroutine below will be canceled during the onDestroy call
mainScope.launch {
delay(STOP_SERVICE_TIMEOUT)
@@ -272,7 +279,7 @@ class AmneziaVpnService : VpnService() {
private fun launchProtocolStateHandler() {
mainScope.launch {
protocolState.collect { protocolState ->
Log.d(TAG, "Protocol state: $protocolState")
Log.d(TAG, "Protocol state changed: $protocolState")
when (protocolState) {
CONNECTED -> {
clientMessenger.send(ServiceEvent.CONNECTED)
@@ -305,7 +312,7 @@ class AmneziaVpnService : VpnService() {
@MainThread
private fun launchSendingStatistics() {
if (isServiceBound && isConnected) {
/* if (isServiceBound && isConnected) {
statisticsSendingJob = mainScope.launch {
while (true) {
clientMessenger.send {
@@ -316,7 +323,7 @@ class AmneziaVpnService : VpnService() {
delay(STATISTICS_SENDING_TIMEOUT)
}
}
}
} */
}
@MainThread
@@ -328,7 +335,7 @@ class AmneziaVpnService : VpnService() {
private fun connect(vpnConfig: String?) {
if (isConnected || protocolState.value == CONNECTING) return
Log.v(TAG, "Start VPN connection")
Log.d(TAG, "Start VPN connection")
protocolState.value = CONNECTING
@@ -357,7 +364,7 @@ class AmneziaVpnService : VpnService() {
private fun disconnect() {
if (isUnknown || isDisconnected || protocolState.value == DISCONNECTING) return
Log.v(TAG, "Stop VPN connection")
Log.d(TAG, "Stop VPN connection")
protocolState.value = DISCONNECTING
@@ -383,7 +390,7 @@ class AmneziaVpnService : VpnService() {
private fun reconnect() {
if (!isConnected) return
Log.v(TAG, "Reconnect VPN")
Log.d(TAG, "Reconnect VPN")
protocolState.value = RECONNECTING
@@ -439,10 +446,4 @@ class AmneziaVpnService : VpnService() {
} else {
true
}
private fun loadConfigFromPrefs(): String? =
Prefs.get(this).getString(PREFS_CONFIG_KEY, null)
private fun saveConfigToPrefs(config: String?) =
Prefs.get(this).edit().putString(PREFS_CONFIG_KEY, config).apply()
}
@@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.v(TAG, "Create Import Config Activity: $intent")
Log.d(TAG, "Create Import Config Activity: $intent")
intent?.let(::readConfig)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.v(TAG, "onNewIntent: $intent")
Log.d(TAG, "onNewIntent: $intent")
intent?.let(::readConfig)
}
private fun readConfig(intent: Intent) {
when (intent.action) {
ACTION_SEND -> {
Log.v(TAG, "Process SEND action, type: ${intent.type}")
Log.d(TAG, "Process SEND action, type: ${intent.type}")
when (intent.type) {
"application/octet-stream" -> {
intent.getUriCompat()?.let { uri ->
@@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
}
ACTION_VIEW -> {
Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
when (intent.scheme) {
"file", "content" -> {
intent.data?.let { uri ->
@@ -128,7 +128,7 @@ class ImportConfigActivity : ComponentActivity() {
private fun startMainActivity(config: String) {
if (config.isNotBlank()) {
Log.v(TAG, "startMainActivity")
Log.d(TAG, "startMainActivity")
Intent(applicationContext, AmneziaActivity::class.java).apply {
action = ACTION_IMPORT_CONFIG
addCategory(CATEGORY_DEFAULT)
@@ -32,7 +32,8 @@ enum class Action : IpcMessage {
REGISTER_CLIENT,
CONNECT,
DISCONNECT,
REQUEST_STATUS
REQUEST_STATUS,
SET_SAVE_LOGS
}
fun <T> T.packToMessage(): Message
@@ -1,25 +0,0 @@
package org.amnezia.vpn
import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import org.amnezia.vpn.util.Log
private const val TAG = "Prefs"
private const val PREFS_FILE = "org.amnezia.vpn.prefs"
private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure"
object Prefs {
fun get(context: Context, appContext: Context = context.applicationContext): SharedPreferences =
try {
EncryptedSharedPreferences(
appContext,
SECURE_PREFS_FILE,
MasterKey(appContext)
)
} catch (e: Exception) {
Log.e(TAG, "Getting Encryption Storage failed: ${e.message}, plaintext fallback")
appContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
}
}
@@ -25,7 +25,7 @@ class VpnRequestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.v(TAG, "Start request activity")
Log.d(TAG, "Start request activity")
val requestIntent = VpnService.prepare(applicationContext)
if (requestIntent != null) {
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
@@ -15,6 +15,8 @@ object QtAndroidController {
external fun onVpnReconnecting()
external fun onStatisticsUpdate(rxBytes: Long, txBytes: Long)
external fun onFileOpened(uri: String)
external fun onConfigImported(data: String)
external fun decodeQrCode(data: String): Boolean
+4
View File
@@ -15,3 +15,7 @@ android {
buildConfig = true
}
}
dependencies {
implementation(libs.androidx.security.crypto)
}
+234 -15
View File
@@ -1,33 +1,252 @@
package org.amnezia.vpn.util
import android.content.Context
import android.icu.text.DateFormat
import android.icu.text.SimpleDateFormat
import android.os.Build
import android.os.Process
import java.io.File
import java.io.IOException
import java.io.RandomAccessFile
import java.nio.channels.FileChannel
import java.nio.channels.FileLock
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale
import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E
import org.amnezia.vpn.util.Log.Priority.F
import org.amnezia.vpn.util.Log.Priority.I
import org.amnezia.vpn.util.Log.Priority.V
import org.amnezia.vpn.util.Log.Priority.W
import android.util.Log as NativeLog
class Log {
companion object {
fun v(tag: String, msg: String) = debugLog(tag, msg, NativeLog::v)
private const val TAG = "Log"
private const val LOG_FILE_NAME = "amneziaVPN.log"
private const val ROTATE_LOG_FILE_NAME = "amneziaVPN.rotate.log"
private const val LOCK_FILE_NAME = ".lock"
private const val DATE_TIME_PATTERN = "MM-dd HH:mm:ss.SSS"
private const val PREFS_SAVE_LOGS_KEY = "SAVE_LOGS"
private const val LOG_MAX_FILE_SIZE = 1024 * 1024
fun d(tag: String, msg: String) = debugLog(tag, msg, NativeLog::d)
/**
* | Priority | Save to file | Logcat logging |
* |-------------------|--------------|----------------------------------------------|
* | Verbose | Don't save | Only in Debug build |
* | Debug | Save | In Debug build or if log saving is enabled |
* | Info, Warn, Error | Save | Enabled |
* | Fatal (Assert) | Save | Enabled. Depending on system configuration, |
* | | | create a report and/or terminate the process |
*/
object Log {
private val dateTimeFormat: Any =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
else object : ThreadLocal<DateFormat>() {
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
}
fun i(tag: String, msg: String) = log(tag, msg, NativeLog::i)
private lateinit var logDir: File
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
private val rotateLogFile: File by lazy { File(logDir, ROTATE_LOG_FILE_NAME) }
fun w(tag: String, msg: String) = log(tag, msg, NativeLog::w)
private val fileLock: FileChannel by lazy { RandomAccessFile(File(logDir, LOCK_FILE_NAME).path, "rw").channel }
private val threadLock: ReentrantLock by lazy { ReentrantLock() }
fun e(tag: String, msg: String) = log(tag, msg, NativeLog::e)
@Volatile
private var _saveLogs: Boolean = false
var saveLogs: Boolean
get() = _saveLogs
set(value) {
if (_saveLogs != value) {
if (value && !logDir.exists() && !logDir.mkdir()) {
NativeLog.e(TAG, "Failed to create dir: $logDir")
return
}
_saveLogs = value
Prefs.save(PREFS_SAVE_LOGS_KEY, value)
}
}
fun v(tag: String, msg: Any?) = v(tag, msg.toString())
@JvmStatic
fun v(tag: String, msg: String) = log(tag, msg, V)
fun d(tag: String, msg: Any?) = d(tag, msg.toString())
@JvmStatic
fun d(tag: String, msg: String) = log(tag, msg, D)
fun i(tag: String, msg: Any?) = i(tag, msg.toString())
@JvmStatic
fun i(tag: String, msg: String) = log(tag, msg, I)
fun w(tag: String, msg: Any?) = w(tag, msg.toString())
@JvmStatic
fun w(tag: String, msg: String) = log(tag, msg, W)
fun e(tag: String, msg: Any?) = e(tag, msg.toString())
@JvmStatic
fun e(tag: String, msg: String) = log(tag, msg, E)
private inline fun log(tag: String, msg: String, delegate: (String, String) -> Unit) = delegate(tag, msg)
@JvmStatic
fun f(tag: String, msg: String) = log(tag, msg, F)
private inline fun debugLog(tag: String, msg: String, delegate: (String, String) -> Unit) {
if (BuildConfig.DEBUG) delegate(tag, msg)
fun v(tag: String, msg: Any?) = v(tag, msg.toString())
fun d(tag: String, msg: Any?) = d(tag, msg.toString())
fun i(tag: String, msg: Any?) = i(tag, msg.toString())
fun w(tag: String, msg: Any?) = w(tag, msg.toString())
fun e(tag: String, msg: Any?) = e(tag, msg.toString())
fun f(tag: String, msg: Any?) = f(tag, msg.toString())
fun init(context: Context) {
v(TAG, "Init Log")
logDir = File(context.cacheDir, "logs")
saveLogs = Prefs.load(PREFS_SAVE_LOGS_KEY)
}
fun getLogs(): String =
"${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}"
fun clearLogs() {
withLock {
logFile.delete()
rotateLogFile.delete()
}
}
private fun log(tag: String, msg: String, priority: Priority) {
if (saveLogs && priority != V) saveLogMsg(formatLogMsg(tag, msg, priority))
if (priority == F) {
NativeLog.wtf(tag, msg)
} else if (
(priority != V && priority != D) ||
(priority == V && BuildConfig.DEBUG) ||
(priority == D && (BuildConfig.DEBUG || saveLogs))
) {
NativeLog.println(priority.level, tag, msg)
}
}
private fun saveLogMsg(msg: String) {
withTryLock(condition = { logFile.length() > LOG_MAX_FILE_SIZE }) {
logFile.renameTo(rotateLogFile)
}
try {
logFile.appendText(msg)
} catch (e: IOException) {
NativeLog.e(TAG, "Failed to write log: $e")
}
}
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter)
} else {
@Suppress("UNCHECKED_CAST")
(dateTimeFormat as ThreadLocal<DateFormat>).get()?.format(Date())
}
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n"
}
private fun deviceInfo(): String {
val sb = StringBuilder()
sb.append("Model: ").appendLine(Build.MODEL)
sb.append("Brand: ").appendLine(Build.BRAND)
sb.append("Product: ").appendLine(Build.PRODUCT)
sb.append("Device: ").appendLine(Build.DEVICE)
sb.append("Codename: ").appendLine(Build.VERSION.CODENAME)
sb.append("Release: ").appendLine(Build.VERSION.RELEASE)
sb.append("SDK: ").appendLine(Build.VERSION.SDK_INT)
sb.append("ABI: ").appendLine(Build.SUPPORTED_ABIS.joinToString())
return sb.toString()
}
private fun readLogs(): String {
var logText = ""
withLock {
try {
if (rotateLogFile.exists()) logText = rotateLogFile.readText()
if (logFile.exists()) logText += logFile.readText()
} catch (e: IOException) {
val errorMsg = "Failed to read log: $e"
NativeLog.e(TAG, errorMsg)
logText += errorMsg
}
}
return logText
}
private fun getLogcat(): String {
try {
val process = ProcessBuilder("logcat", "-d").redirectErrorStream(true).start()
return process.inputStream.reader().readText()
} catch (e: IOException) {
val errorMsg = "Failed to get logcat log: $e"
NativeLog.e(TAG, errorMsg)
return errorMsg
}
}
private fun withLock(block: () -> Unit) {
threadLock.lock()
try {
var l: FileLock? = null
try {
l = fileLock.lock()
block()
} catch (e: IOException) {
NativeLog.e(TAG, "Failed to get file lock: $e")
} finally {
try {
l?.release()
} catch (e: IOException) {
NativeLog.e(TAG, "Failed to release file lock: $e")
}
}
} finally {
threadLock.unlock()
}
}
private fun withTryLock(condition: () -> Boolean, block: () -> Unit) {
if (condition()) {
if (threadLock.tryLock()) {
try {
if (condition()) {
var l: FileLock? = null
try {
l = fileLock.tryLock()
if (l != null) {
if (condition()) {
block()
}
}
} catch (e: IOException) {
NativeLog.e(TAG, "Failed to get file tryLock: $e")
} finally {
try {
l?.release()
} catch (e: IOException) {
NativeLog.e(TAG, "Failed to release file tryLock: $e")
}
}
}
} finally {
threadLock.unlock()
}
}
}
}
private enum class Priority(val level: Int) {
V(2),
D(3),
I(4),
W(5),
E(6),
F(7)
}
}
@@ -0,0 +1,58 @@
package org.amnezia.vpn.util
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import kotlin.reflect.typeOf
private const val TAG = "Prefs"
private const val PREFS_FILE = "org.amnezia.vpn.prefs"
private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure"
object Prefs {
private lateinit var app: Application
val prefs: SharedPreferences
get() = try {
EncryptedSharedPreferences(
app,
SECURE_PREFS_FILE,
MasterKey(app)
)
} catch (e: Exception) {
Log.e(TAG, "Getting Encryption Storage failed: $e, plaintext fallback")
app.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
}
fun init(app: Application) {
Log.v(TAG, "Init Prefs")
this.app = app
}
fun save(key: String, value: Boolean) =
prefs.edit().putBoolean(key, value).apply()
fun save(key: String, value: String?) =
prefs.edit().putString(key, value).apply()
fun save(key: String, value: Int) =
prefs.edit().putInt(key, value).apply()
fun save(key: String, value: Long) =
prefs.edit().putLong(key, value).apply()
fun save(key: String, value: Float) =
prefs.edit().putFloat(key, value).apply()
inline fun <reified T> load(key: String): T {
return when (typeOf<T>()) {
typeOf<Boolean>() -> prefs.getBoolean(key, false)
typeOf<String>() -> prefs.getString(key, "")
typeOf<Int>() -> prefs.getInt(key, 0)
typeOf<Long>() -> prefs.getLong(key, 0L)
typeOf<Float>() -> prefs.getFloat(key, 0f)
else -> throw IllegalArgumentException("SharedPreferences does not support type: ${typeOf<T>()}")
} as T
}
}
@@ -82,7 +82,7 @@ class NetworkState(
fun bindNetworkListener() {
if (isListenerBound) return
Log.v(TAG, "Bind network listener")
Log.d(TAG, "Bind network listener")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -95,7 +95,7 @@ class NetworkState(
fun unbindNetworkListener() {
if (!isListenerBound) return
Log.v(TAG, "Unbind network listener")
Log.d(TAG, "Unbind network listener")
connectivityManager.unregisterNetworkCallback(networkCallback)
isListenerBound = false
currentNetwork = null
@@ -11,7 +11,7 @@ open class WireguardConfig protected constructor(
val endpoint: InetEndpoint,
val persistentKeepalive: Int,
val publicKeyHex: String,
val preSharedKeyHex: String,
val preSharedKeyHex: String?,
val privateKeyHex: String
) : ProtocolConfig(protocolConfigBuilder) {
@@ -43,7 +43,8 @@ open class WireguardConfig protected constructor(
appendLine("endpoint=$endpoint")
if (persistentKeepalive != 0)
appendLine("persistent_keepalive_interval=$persistentKeepalive")
appendLine("preshared_key=$preSharedKeyHex")
if (preSharedKeyHex != null)
appendLine("preshared_key=$preSharedKeyHex")
}
open class Builder : ProtocolConfig.Builder(true) {
@@ -56,7 +57,7 @@ open class WireguardConfig protected constructor(
internal lateinit var publicKeyHex: String
private set
internal lateinit var preSharedKeyHex: String
internal var preSharedKeyHex: String? = null
private set
internal lateinit var privateKeyHex: String
+1 -1
View File
@@ -90,7 +90,7 @@ include_directories(
${LIBSSH_ROOT_DIR}/include
${CLIENT_ROOT_DIR}/3rd/libssh/include
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/include
${CLIENT_ROOT_DIR}/3rd/qtkeychain
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
)
+2 -2
View File
@@ -27,7 +27,7 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
)
@@ -35,7 +35,7 @@ set(HEADERS ${HEADERS}
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_notificationhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidutils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp
)
+1 -1
View File
@@ -97,7 +97,7 @@ target_compile_options(${PROJECT} PRIVATE
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
)
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/awg-apple/Sources)
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
target_sources(${PROJECT} PRIVATE
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
+21 -19
View File
@@ -118,31 +118,33 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
return QJsonDocument(jConfig).toJson();
}
QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig, const int serverIndex)
{
QJsonObject json = QJsonDocument::fromJson(jsonConfig.toUtf8()).object();
QString config = json[config_key::config].toString();
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (!m_settings->server(serverIndex).value(config_key::configVersion).toInt()) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (m_settings->routeMode() == Settings::VpnAllSites) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
}
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
if (m_settings->routeMode() == Settings::VpnAllSites) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
}
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway
}
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
// no redirect-gateway
}
if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#ifndef Q_OS_ANDROID
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
#endif
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n");
}
}
#ifndef MZ_WINDOWS
+1 -1
View File
@@ -26,7 +26,7 @@ public:
QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode = nullptr);
QString processConfigWithLocalSettings(QString jsonConfig);
QString processConfigWithLocalSettings(QString jsonConfig, const int serverIndex);
QString processConfigWithExportSettings(QString jsonConfig);
ErrorCode signCert(DockerContainer container,
+1 -1
View File
@@ -92,7 +92,7 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker
processConfigWithDnsSettings(serverIndex, container, proto, config);
if (proto == Proto::OpenVpn) {
config = openVpnConfigurator->processConfigWithLocalSettings(config);
config = openVpnConfigurator->processConfigWithLocalSettings(config, serverIndex);
}
return config;
}
@@ -211,8 +211,14 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
localFile.write(data);
localFile.close();
#ifdef Q_OS_WINDOWS
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toLocal8Bit().toStdString(), remotePath.toStdString(),
"non_desc");
#else
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(),
"non_desc");
#endif
if (error != ErrorCode::NoError) {
return error;
}
+1 -1
View File
@@ -58,7 +58,7 @@ target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/awg-apple/Sources)
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
target_sources(networkextension PRIVATE
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
@@ -1,6 +1,6 @@
#include "wireguard-go-version.h"
#include "3rd/awg-apple/Sources/WireGuardKitGo/wireguard.h"
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include <stdbool.h>
#include <stdint.h>
+142 -29
View File
@@ -1,8 +1,10 @@
#include <QCoreApplication>
#include <QJniEnvironment>
#include <QJsonDocument>
#include <QQmlFile>
#include <QEventLoop>
#include "android_controller.h"
#include "android_utils.h"
#include "ui/controllers/importController.h"
namespace
@@ -10,13 +12,15 @@ namespace
AndroidController *s_instance = nullptr;
constexpr auto QT_ANDROID_CONTROLLER_CLASS = "org/amnezia/vpn/qt/QtAndroidController";
constexpr auto ANDROID_LOG_CLASS = "org/amnezia/vpn/util/Log";
constexpr auto TAG = "AmneziaQt";
} // namespace
AndroidController::AndroidController() : QObject()
{
connect(this, &AndroidController::status, this,
[this](AndroidController::ConnectionState state) {
qDebug() << "Android event: status; state:" << textConnectionState(state);
qDebug() << "Android event: status =" << textConnectionState(state);
if (isWaitingStatus) {
qDebug() << "Initialization by service status";
isWaitingStatus = false;
@@ -106,6 +110,7 @@ bool AndroidController::initialize()
{"onVpnDisconnected", "()V", reinterpret_cast<void *>(onVpnDisconnected)},
{"onVpnReconnecting", "()V", reinterpret_cast<void *>(onVpnReconnecting)},
{"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)},
{"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onFileOpened)},
{"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast<void *>(onConfigImported)},
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
};
@@ -123,24 +128,19 @@ bool AndroidController::initialize()
// static
template <typename Ret, typename ...Args>
auto AndroidController::callActivityMethod(const char *methodName, const char *signature,
const std::function<Ret()> &defValue, Args &&...args)
auto AndroidController::callActivityMethod(const char *methodName, const char *signature, Args &&...args)
{
qDebug() << "Call activity method:" << methodName;
QJniObject activity = QNativeInterface::QAndroidApplication::context();
if (activity.isValid()) {
return activity.callMethod<Ret>(methodName, signature, std::forward<Args>(args)...);
} else {
qCritical() << "Activity is not valid";
return defValue();
}
QJniObject activity = AndroidUtils::getActivity();
Q_ASSERT(activity.isValid());
return activity.callMethod<Ret>(methodName, signature, std::forward<Args>(args)...);
}
// static
template <typename ...Args>
void AndroidController::callActivityMethod(const char *methodName, const char *signature, Args &&...args)
{
callActivityMethod<void>(methodName, signature, [] {}, std::forward<Args>(args)...);
callActivityMethod<void>(methodName, signature, std::forward<Args>(args)...);
}
ErrorCode AndroidController::start(const QJsonObject &vpnConfig)
@@ -165,6 +165,24 @@ void AndroidController::saveFile(const QString &fileName, const QString &data)
QJniObject::fromString(data).object<jstring>());
}
QString AndroidController::openFile(const QString &filter)
{
QEventLoop wait;
QString fileName;
connect(this, &AndroidController::fileOpened, this,
[&fileName, &wait](const QString &uri) {
qDebug() << "Android event: file opened; uri:" << uri;
fileName = QQmlFile::urlToLocalFileOrQrc(uri);
qDebug() << "Android opened filename:" << fileName;
wait.quit();
},
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
callActivityMethod("openFile", "(Ljava/lang/String;)V",
QJniObject::fromString(filter).object<jstring>());
wait.exec();
return fileName;
}
void AndroidController::setNotificationText(const QString &title, const QString &message, int timerSec)
{
callActivityMethod("setNotificationText", "(Ljava/lang/String;Ljava/lang/String;I)V",
@@ -173,11 +191,114 @@ void AndroidController::setNotificationText(const QString &title, const QString
(jint) timerSec);
}
bool AndroidController::isCameraPresent()
{
return callActivityMethod<jboolean>("isCameraPresent", "()Z");
}
void AndroidController::startQrReaderActivity()
{
callActivityMethod("startQrCodeReader", "()V");
}
void AndroidController::setSaveLogs(bool enabled)
{
callActivityMethod("setSaveLogs", "(Z)V", enabled);
}
void AndroidController::exportLogsFile(const QString &fileName)
{
callActivityMethod("exportLogsFile", "(Ljava/lang/String;)V",
QJniObject::fromString(fileName).object<jstring>());
}
void AndroidController::clearLogs()
{
callActivityMethod("clearLogs", "()V");
}
// Moving log processing to the Android side
jclass AndroidController::log;
jmethodID AndroidController::logDebug;
jmethodID AndroidController::logInfo;
jmethodID AndroidController::logWarning;
jmethodID AndroidController::logError;
jmethodID AndroidController::logFatal;
// static
bool AndroidController::initLogging()
{
QJniEnvironment env;
log = env.findClass(ANDROID_LOG_CLASS);
if (log == nullptr) {
qCritical() << "Android log class" << ANDROID_LOG_CLASS << "not found";
return false;
}
auto logMethodSignature = "(Ljava/lang/String;Ljava/lang/String;)V";
logDebug = env.findStaticMethod(log, "d", logMethodSignature);
if (logDebug == nullptr) {
qCritical() << "Android debug log method not found";
return false;
}
logInfo = env.findStaticMethod(log, "i", logMethodSignature);
if (logInfo == nullptr) {
qCritical() << "Android info log method not found";
return false;
}
logWarning = env.findStaticMethod(log, "w", logMethodSignature);
if (logWarning == nullptr) {
qCritical() << "Android warning log method not found";
return false;
}
logError = env.findStaticMethod(log, "e", logMethodSignature);
if (logError == nullptr) {
qCritical() << "Android error log method not found";
return false;
}
logFatal = env.findStaticMethod(log, "f", logMethodSignature);
if (logFatal == nullptr) {
qCritical() << "Android fatal log method not found";
return false;
}
qInstallMessageHandler(messageHandler);
return true;
}
// static
void AndroidController::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
jmethodID logMethod = logDebug;
switch (type) {
case QtDebugMsg:
logMethod = logDebug;
break;
case QtInfoMsg:
logMethod = logInfo;
break;
case QtWarningMsg:
logMethod = logWarning;
break;
case QtCriticalMsg:
logMethod = logError;
break;
case QtFatalMsg:
logMethod = logFatal;
break;
}
QString formattedMessage = qFormatLogMessage(type, context, message);
QJniObject::callStaticMethod<void>(log, logMethod,
QJniObject::fromString(TAG).object<jstring>(),
QJniObject::fromString(formattedMessage).object<jstring>());
}
void AndroidController::qtAndroidControllerInitialized()
{
callActivityMethod("qtAndroidControllerInitialized", "()V");
@@ -285,20 +406,19 @@ void AndroidController::onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBy
}
// static
void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data)
void AndroidController::onFileOpened(JNIEnv *env, jobject thiz, jstring uri)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return;
}
emit AndroidController::instance()->fileOpened(AndroidUtils::convertJString(env, uri));
}
QString config(buffer);
env->ReleaseStringUTFChars(data, buffer);
// static
void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data)
{
Q_UNUSED(thiz);
emit AndroidController::instance()->configImported(config);
emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data));
}
// static
@@ -306,12 +426,5 @@ bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
{
Q_UNUSED(thiz);
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return false;
}
QString code(buffer);
env->ReleaseStringUTFChars(data, buffer);
return ImportController::decodeQrCode(code);
return ImportController::decodeQrCode(AndroidUtils::convertJString(env, data));
}
+21 -4
View File
@@ -18,7 +18,8 @@ public:
bool initialize();
// keep synchronized with org.amnezia.vpn.protocol.ProtocolState
enum class ConnectionState {
enum class ConnectionState
{
CONNECTED,
CONNECTING,
DISCONNECTED,
@@ -30,8 +31,16 @@ public:
ErrorCode start(const QJsonObject &vpnConfig);
void stop();
void setNotificationText(const QString &title, const QString &message, int timerSec);
void saveFile(const QString& fileName, const QString &data);
void saveFile(const QString &fileName, const QString &data);
QString openFile(const QString &filter);
bool isCameraPresent();
void startQrReaderActivity();
void setSaveLogs(bool enabled);
void exportLogsFile(const QString &fileName);
void clearLogs();
static bool initLogging();
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
signals:
void connectionStateChanged(Vpn::ConnectionState state);
@@ -43,6 +52,7 @@ signals:
void vpnDisconnected();
void vpnReconnecting();
void statisticsUpdated(quint64 rxBytes, quint64 txBytes);
void fileOpened(QString uri);
void configImported(QString config);
void importConfigFromOutside(QString config);
void initConnectionState(Vpn::ConnectionState state);
@@ -50,6 +60,13 @@ signals:
private:
bool isWaitingStatus = true;
static jclass log;
static jmethodID logDebug;
static jmethodID logInfo;
static jmethodID logWarning;
static jmethodID logError;
static jmethodID logFatal;
void qtAndroidControllerInitialized();
static Vpn::ConnectionState convertState(ConnectionState state);
@@ -65,11 +82,11 @@ private:
static void onVpnReconnecting(JNIEnv *env, jobject thiz);
static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes);
static void onConfigImported(JNIEnv *env, jobject thiz, jstring data);
static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri);
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
template <typename Ret, typename ...Args>
static auto callActivityMethod(const char *methodName, const char *signature,
const std::function<Ret()> &defValue, Args &&...args);
static auto callActivityMethod(const char *methodName, const char *signature, Args &&...args);
template <typename ...Args>
static void callActivityMethod(const char *methodName, const char *signature, Args &&...args);
};
@@ -0,0 +1,30 @@
#include <QCoreApplication>
#include "android_utils.h"
namespace AndroidUtils
{
QJniObject getActivity()
{
return QNativeInterface::QAndroidApplication::context();
}
QString convertJString(JNIEnv *env, jstring data)
{
int len = env->GetStringLength(data);
QString res(len, Qt::Uninitialized);
env->GetStringRegion(data, 0, len, reinterpret_cast<jchar *>(res.data()));
return res;
}
void runOnAndroidThreadSync(const std::function<void()> &runnable)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable).waitForFinished();
}
void runOnAndroidThreadAsync(const std::function<void()> &runnable)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable);
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef ANDROID_UTILS_H
#define ANDROID_UTILS_H
#include <QJniObject>
namespace AndroidUtils
{
QJniObject getActivity();
QString convertJString(JNIEnv *env, jstring data);
void runOnAndroidThreadSync(const std::function<void()> &runnable);
void runOnAndroidThreadAsync(const std::function<void()> &runnable);
};
#endif // ANDROID_UTILS_H
-183
View File
@@ -1,183 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "androidutils.h"
#include <QGuiApplication>
#include <QJniEnvironment>
#include <QJniObject>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkCookieJar>
#include <QTimer>
#include <QUrlQuery>
#include "jni.h"
namespace
{
AndroidUtils *s_instance = nullptr;
} // namespace
// static
QString AndroidUtils::GetDeviceName()
{
QJniEnvironment env;
jclass BUILD = env->FindClass("android/os/Build");
jfieldID model = env->GetStaticFieldID(BUILD, "MODEL", "Ljava/lang/String;");
jstring value = (jstring)env->GetStaticObjectField(BUILD, model);
if (!value) {
return QString("Android Device");
}
const char *buffer = env->GetStringUTFChars(value, nullptr);
if (!buffer) {
return QString("Android Device");
}
QString res(buffer);
env->ReleaseStringUTFChars(value, buffer);
return res;
};
// static
AndroidUtils *AndroidUtils::instance()
{
if (!s_instance) {
Q_ASSERT(qApp);
s_instance = new AndroidUtils(qApp);
}
return s_instance;
}
AndroidUtils::AndroidUtils(QObject *parent) : QObject(parent)
{
Q_ASSERT(!s_instance);
s_instance = this;
}
AndroidUtils::~AndroidUtils()
{
Q_ASSERT(s_instance == this);
s_instance = nullptr;
}
// static
void AndroidUtils::dispatchToMainThread(std::function<void()> callback)
{
QTimer *timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=]() {
callback();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
}
// static
QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv *env, jstring data)
{
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
qDebug() << "getQByteArrayFromJString - failed to parse data.";
return QByteArray();
}
QByteArray out(buffer);
env->ReleaseStringUTFChars(data, buffer);
return out;
}
// static
QString AndroidUtils::getQStringFromJString(JNIEnv *env, jstring data)
{
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
qDebug() << "getQStringFromJString - failed to parse data.";
return QString();
}
QString out(buffer);
env->ReleaseStringUTFChars(data, buffer);
return out;
}
// static
QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv *env, jstring data)
{
QByteArray raw(getQByteArrayFromJString(env, data));
QJsonParseError jsonError;
QJsonDocument json = QJsonDocument::fromJson(raw, &jsonError);
if (QJsonParseError::NoError != jsonError.error) {
qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: " << jsonError.error
<< "Offset: " << jsonError.offset << "Message: " << jsonError.errorString() << "Data: " << raw;
return QJsonObject();
}
if (!json.isObject()) {
qDebug() << "getQJsonObjectFromJString - object expected.";
return QJsonObject();
}
return json.object();
}
QJniObject AndroidUtils::getActivity()
{
return QNativeInterface::QAndroidApplication::context();
}
int AndroidUtils::GetSDKVersion()
{
QJniEnvironment env;
jclass versionClass = env->FindClass("android/os/Build$VERSION");
jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
int sdk = env->GetStaticIntField(versionClass, sdkIntFieldID);
return sdk;
}
QString AndroidUtils::GetManufacturer()
{
QJniEnvironment env;
jclass buildClass = env->FindClass("android/os/Build");
jfieldID manuFacturerField = env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;");
jstring value = (jstring)env->GetStaticObjectField(buildClass, manuFacturerField);
const char *buffer = env->GetStringUTFChars(value, nullptr);
if (!buffer) {
qDebug() << "Failed to fetch MANUFACTURER";
return QByteArray();
}
QString res(buffer);
qDebug() << "MANUFACTURER: " << res;
env->ReleaseStringUTFChars(value, buffer);
return res;
}
void AndroidUtils::runOnAndroidThreadSync(const std::function<void()> runnable)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable).waitForFinished();
}
void AndroidUtils::runOnAndroidThreadAsync(const std::function<void()> runnable)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable);
}
// Static
// Creates a copy of the passed QByteArray in the JVM and passes back a ref
jbyteArray AndroidUtils::tojByteArray(const QByteArray &data)
{
QJniEnvironment env;
jbyteArray out = env->NewByteArray(data.size());
env->SetByteArrayRegion(out, 0, data.size(), reinterpret_cast<const jbyte *>(data.constData()));
return out;
}
-49
View File
@@ -1,49 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ANDROIDUTILS_H
#define ANDROIDUTILS_H
#include <jni.h>
#include <QJniEnvironment>
#include <QJniObject>
#include <QObject>
#include <QString>
#include <QUrl>
class AndroidUtils final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(AndroidUtils)
public:
static QString GetDeviceName();
static int GetSDKVersion();
static QString GetManufacturer();
static AndroidUtils* instance();
static void dispatchToMainThread(std::function<void()> callback);
static QByteArray getQByteArrayFromJString(JNIEnv* env, jstring data);
static jbyteArray tojByteArray(const QByteArray& data);
static QString getQStringFromJString(JNIEnv* env, jstring data);
static QJsonObject getQJsonObjectFromJString(JNIEnv* env, jstring data);
static QJniObject getActivity();
static void runOnAndroidThreadSync(const std::function<void()> runnable);
static void runOnAndroidThreadAsync(const std::function<void()> runnable);
private:
AndroidUtils(QObject* parent);
~AndroidUtils();
};
#endif // ANDROIDUTILS_H
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
#include <stdbool.h>
#include <stdint.h>
+4 -2
View File
@@ -14,12 +14,14 @@ constexpr const char *keyChainName = "AmneziaVPN-Keychain";
class SecureQSettings : public QObject
{
Q_OBJECT
public:
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
QObject *parent = nullptr);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
void remove(const QString &key);
void sync();
@@ -1,5 +1,6 @@
if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\
elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\
elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi
if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi
+1
View File
@@ -1,6 +1,7 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="> /dev/null 2>&1"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
+42 -9
View File
@@ -1,4 +1,8 @@
#include "settings.h"
#include "QThread"
#include "QCoreApplication"
#include "utilities.h"
#include "version.h"
@@ -12,10 +16,10 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N
{
// Import old settings
if (serversCount() == 0) {
QString user = m_settings.value("Server/userName").toString();
QString password = m_settings.value("Server/password").toString();
QString serverName = m_settings.value("Server/serverName").toString();
int port = m_settings.value("Server/serverPort").toInt();
QString user = value("Server/userName").toString();
QString password = value("Server/password").toString();
QString serverName = value("Server/serverName").toString();
int port = value("Server/serverPort").toInt();
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
QJsonObject server;
@@ -211,7 +215,8 @@ QString Settings::nextAvailableServerName() const
void Settings::setSaveLogs(bool enabled)
{
m_settings.setValue("Conf/saveLogs", enabled);
setValue("Conf/saveLogs", enabled);
#ifndef Q_OS_ANDROID
if (!isSaveLogs()) {
Logger::deInit();
} else {
@@ -219,7 +224,8 @@ void Settings::setSaveLogs(bool enabled)
qWarning() << "Initialization of debug subsystem failed";
}
}
emit saveLogsChanged();
#endif
emit saveLogsChanged(enabled);
}
QString Settings::routeModeString(RouteMode mode) const
@@ -233,7 +239,7 @@ QString Settings::routeModeString(RouteMode mode) const
Settings::RouteMode Settings::routeMode() const
{
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
}
bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
@@ -321,12 +327,12 @@ void Settings::removeAllVpnSites(RouteMode mode)
QString Settings::primaryDns() const
{
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
return value("Conf/primaryDns", cloudFlareNs1).toString();
}
QString Settings::secondaryDns() const
{
return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString();
return value("Conf/secondaryDns", cloudFlareNs2).toString();
}
void Settings::clearSettings()
@@ -351,3 +357,30 @@ ServerCredentials Settings::serverCredentials(int index) const
return credentials;
}
QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
{
QVariant returnValue;
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
returnValue = m_settings.value(key, defaultValue);
} else {
QMetaObject::invokeMethod(&m_settings, "value",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(const QString&, key),
Q_ARG(const QVariant&, defaultValue));
}
return returnValue;
}
void Settings::setValue(const QString &key, const QVariant &value)
{
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
m_settings.setValue(key, value);
} else {
QMetaObject::invokeMethod(&m_settings, "setValue",
Qt::BlockingQueuedConnection,
Q_ARG(const QString&, key),
Q_ARG(const QVariant&, value));
}
}
+25 -22
View File
@@ -29,11 +29,11 @@ public:
QJsonArray serversArray() const
{
return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array();
return QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
}
void setServersArray(const QJsonArray &servers)
{
m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson());
setValue("Servers/serversList", QJsonDocument(servers).toJson());
}
// Servers section
@@ -45,11 +45,11 @@ public:
int defaultServerIndex() const
{
return m_settings.value("Servers/defaultServerIndex", 0).toInt();
return value("Servers/defaultServerIndex", 0).toInt();
}
void setDefaultServer(int index)
{
m_settings.setValue("Servers/defaultServerIndex", index);
setValue("Servers/defaultServerIndex", index);
}
QJsonObject defaultServer() const
{
@@ -78,25 +78,25 @@ public:
// App settings section
bool isAutoConnect() const
{
return m_settings.value("Conf/autoConnect", false).toBool();
return value("Conf/autoConnect", false).toBool();
}
void setAutoConnect(bool enabled)
{
m_settings.setValue("Conf/autoConnect", enabled);
setValue("Conf/autoConnect", enabled);
}
bool isStartMinimized() const
{
return m_settings.value("Conf/startMinimized", false).toBool();
return value("Conf/startMinimized", false).toBool();
}
void setStartMinimized(bool enabled)
{
m_settings.setValue("Conf/startMinimized", enabled);
setValue("Conf/startMinimized", enabled);
}
bool isSaveLogs() const
{
return m_settings.value("Conf/saveLogs", false).toBool();
return value("Conf/saveLogs", false).toBool();
}
void setSaveLogs(bool enabled);
@@ -110,15 +110,15 @@ public:
QString routeModeString(RouteMode mode) const;
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); }
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
QVariantMap vpnSites(RouteMode mode) const
{
return m_settings.value("Conf/" + routeModeString(mode)).toMap();
return value("Conf/" + routeModeString(mode)).toMap();
}
void setVpnSites(RouteMode mode, const QVariantMap &sites)
{
m_settings.setValue("Conf/" + routeModeString(mode), sites);
setValue("Conf/" + routeModeString(mode), sites);
m_settings.sync();
}
bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = "");
@@ -132,11 +132,11 @@ public:
bool useAmneziaDns() const
{
return m_settings.value("Conf/useAmneziaDns", true).toBool();
return value("Conf/useAmneziaDns", true).toBool();
}
void setUseAmneziaDns(bool enabled)
{
m_settings.setValue("Conf/useAmneziaDns", enabled);
setValue("Conf/useAmneziaDns", enabled);
}
QString primaryDns() const;
@@ -145,13 +145,13 @@ public:
// QString primaryDns() const { return m_primaryDns; }
void setPrimaryDns(const QString &primaryDns)
{
m_settings.setValue("Conf/primaryDns", primaryDns);
setValue("Conf/primaryDns", primaryDns);
}
// QString secondaryDns() const { return m_secondaryDns; }
void setSecondaryDns(const QString &secondaryDns)
{
m_settings.setValue("Conf/secondaryDns", secondaryDns);
setValue("Conf/secondaryDns", secondaryDns);
}
static const char cloudFlareNs1[];
@@ -171,29 +171,32 @@ public:
QLocale getAppLanguage()
{
return m_settings.value("Conf/appLanguage", QLocale()).toLocale();
return value("Conf/appLanguage", QLocale()).toLocale();
};
void setAppLanguage(QLocale locale)
{
m_settings.setValue("Conf/appLanguage", locale);
setValue("Conf/appLanguage", locale);
};
bool isScreenshotsEnabled() const
{
return m_settings.value("Conf/screenshotsEnabled", false).toBool();
return value("Conf/screenshotsEnabled", false).toBool();
}
void setScreenshotsEnabled(bool enabled)
{
m_settings.setValue("Conf/screenshotsEnabled", enabled);
setValue("Conf/screenshotsEnabled", enabled);
}
void clearSettings();
signals:
void saveLogsChanged();
void saveLogsChanged(bool enabled);
private:
SecureQSettings m_settings;
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
mutable SecureQSettings m_settings;
};
#endif // SETTINGS_H
+79 -71
View File
@@ -946,6 +946,11 @@ Already installed containers were found on the server. All installed containers
<source>Show other methods on Github</source>
<translation>نمایش متدهای دیگر در گیت هاب</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="104"/>
<source>https://github.com/amnezia-vpn/amnezia-client#donate</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="113"/>
<source>Contacts</source>
@@ -1854,6 +1859,11 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>I have nothing</source>
<translation>من هیچی ندارم</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="138"/>
<source>https://amnezia.org/instructions/0_starter-guide</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardTextKey</name>
@@ -2178,38 +2188,38 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::DeletePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="104"/>
<source>Password entry not found</source>
<translation>Password entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="108"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="108"/>
<source>Could not decrypt data</source>
<translation>Could not decrypt data</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="552"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="560"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="585"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="593"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="578"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="614"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="177"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="177"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="173"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="173"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="179"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="179"/>
<source>Could not remove private key from keystore</source>
<translation>Could not remove private key from keystore</translation>
</message>
@@ -2217,12 +2227,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::JobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="265"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="295"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="509"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="542"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
@@ -2230,27 +2240,27 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::PlainTextStore</name>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="65"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="65"/>
<source>Could not store data in settings: access error</source>
<translation>Could not store data in settings: access error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="67"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="67"/>
<source>Could not store data in settings: format error</source>
<translation>Could not store data in settings: format error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="85"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="85"/>
<source>Could not delete data from settings: access error</source>
<translation>Could not delete data from settings: access error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="87"/>
<source>Could not delete data from settings: format error</source>
<translation>Could not delete data from settings: format error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="104"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
@@ -2258,80 +2268,80 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="32"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="32"/>
<source>Password entry not found</source>
<translation>Password entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="139"/>
<source>Could not decrypt data</source>
<translation>Could not decrypt data</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="178"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="205"/>
<source>D-Bus is not running</source>
<translation>D-Bus is not running</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="187"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="197"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="214"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="224"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="286"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="316"/>
<source>No keychain service available</source>
<translation>No keychain service available</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="288"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="318"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="333"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="363"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="354"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="384"/>
<source>Could not determine data type: %1; %2</source>
<translation>Could not determine data type: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="363"/>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="52"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="393"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="52"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="372"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="402"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation>Unsupported entry type &apos;Map&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="375"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="405"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation>Unknown kwallet entry type &apos;%1&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="96"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="96"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="60"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="60"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="68"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="68"/>
<source>Could not retrieve private key from keystore</source>
<translation>Could not retrieve private key from keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="75"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="75"/>
<source>Could not create decryption cipher</source>
<translation>Could not create decryption cipher</translation>
</message>
@@ -2339,73 +2349,73 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::WritePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="78"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="78"/>
<source>Credential size exceeds maximum size of %1</source>
<translation>Credential size exceeds maximum size of %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="87"/>
<source>Credential key exceeds maximum size of %1</source>
<translation>Credential key exceeds maximum size of %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="92"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="92"/>
<source>Writing credentials failed: Win32 error code %1</source>
<translation>Writing credentials failed: Win32 error code %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="162"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="162"/>
<source>Encryption failed</source>
<translation>Encryption failed</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="415"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="445"/>
<source>D-Bus is not running</source>
<translation>D-Bus is not running</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="425"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="452"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="455"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="482"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="468"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="501"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="144"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="144"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="95"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="95"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="124"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="124"/>
<source>Could not create private key generator</source>
<translation>Could not create private key generator</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="131"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="131"/>
<source>Could not generate new private key</source>
<translation>Could not generate new private key</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="139"/>
<source>Could not retrieve private key from keystore</source>
<translation>Could not retrieve private key from keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="147"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="147"/>
<source>Could not create encryption cipher</source>
<translation>Could not create encryption cipher</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="155"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="155"/>
<source>Could not encrypt data</source>
<translation>Could not encrypt data</translation>
</message>
@@ -2855,74 +2865,72 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<translation>سرویس Sftp</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/libsecret.cpp" line="119"/>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="225"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="255"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="227"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="257"/>
<source>No keyring daemon</source>
<translation>No keyring daemon</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="229"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="259"/>
<source>Already unlocked</source>
<translation>Already unlocked</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="231"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="261"/>
<source>No such keyring</source>
<translation>No such keyring</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="233"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="263"/>
<source>Bad arguments</source>
<translation>Bad arguments</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="235"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="265"/>
<source>I/O error</source>
<translation>I/O error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="237"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="267"/>
<source>Cancelled</source>
<translation>Cancelled</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="239"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="269"/>
<source>Keyring already exists</source>
<translation>Keyring already exists</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="241"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="271"/>
<source>No match</source>
<translation>No match</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="246"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="276"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="72"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="72"/>
<source>error 0x%1: %2</source>
<translation>error 0x%1: %2</translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="39"/>
<source>WireGuard Configuration Highlighter</source>
<translation>هایلایتر پیکربندی WireGuard</translation>
<translation type="vanished">هایلایتر پیکربندی WireGuard</translation>
</message>
<message>
<location filename="../3rd/wireguard-tools/contrib/highlighter/gui/highlight.cpp" line="82"/>
<source>&amp;Randomize colors</source>
<translation>رنگهای تصادفی</translation>
<translation type="vanished">رنگهای تصادفی</translation>
</message>
</context>
<context>
@@ -2936,13 +2944,13 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<context>
<name>Settings</name>
<message>
<location filename="../settings.cpp" line="26"/>
<location filename="../settings.cpp" line="30"/>
<source>Server #1</source>
<translation>Server #1</translation>
</message>
<message>
<location filename="../settings.cpp" line="202"/>
<location filename="../settings.cpp" line="209"/>
<location filename="../settings.cpp" line="206"/>
<location filename="../settings.cpp" line="213"/>
<source>Server</source>
<translation>Server</translation>
</message>
@@ -2950,22 +2958,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<context>
<name>SettingsController</name>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="25"/>
<location filename="../ui/controllers/settingsController.cpp" line="26"/>
<source>Software version</source>
<translation>نسخه نرمافزار</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="137"/>
<location filename="../ui/controllers/settingsController.cpp" line="139"/>
<source>All settings have been reset to default values</source>
<translation>تمام تنظیمات به مقادیر پیش فرض ریست شد</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="143"/>
<location filename="../ui/controllers/settingsController.cpp" line="145"/>
<source>Cached profiles cleared</source>
<translation>پروفایل ذخیره شده پاک شد</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="122"/>
<location filename="../ui/controllers/settingsController.cpp" line="123"/>
<source>Backup file is corrupted</source>
<translation>فایل بکآپ خراب شده است</translation>
</message>
+113 -95
View File
@@ -15,15 +15,13 @@
<context>
<name>AndroidController</name>
<message>
<location filename="../platforms/android/android_controller.cpp" line="236"/>
<source>AmneziaVPN</source>
<translation>AmneziaVPN</translation>
<translation type="vanished">AmneziaVPN</translation>
</message>
<message>
<location filename="../platforms/android/android_controller.cpp" line="239"/>
<source>VPN Connected</source>
<extracomment>Refers to the app - which is currently running the background and waiting</extracomment>
<translation>VPN Подключен</translation>
<translation type="vanished">VPN Подключен</translation>
</message>
</context>
<context>
@@ -151,7 +149,7 @@
<context>
<name>ImportController</name>
<message>
<location filename="../ui/controllers/importController.cpp" line="435"/>
<location filename="../ui/controllers/importController.cpp" line="411"/>
<source>Scanned %1 of %2.</source>
<translation>Отсканировано %1 из%2.</translation>
</message>
@@ -946,6 +944,11 @@ Already installed containers were found on the server. All installed containers
<source>Show other methods on Github</source>
<translation>Показать другие способы на Github</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="104"/>
<source>https://github.com/amnezia-vpn/amnezia-client#donate</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="113"/>
<source>Contacts</source>
@@ -1428,22 +1431,22 @@ Already installed containers were found on the server. All installed containers
<translation>Имя сервера</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="110"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="111"/>
<source>Save</source>
<translation>Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="137"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="142"/>
<source>Protocols</source>
<translation>Протоколы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="143"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="148"/>
<source>Services</source>
<translation>Сервисы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="147"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="152"/>
<source>Data</source>
<translation>Данные</translation>
</message>
@@ -1854,6 +1857,11 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>I have nothing</source>
<translation>У меня ничего нет</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="138"/>
<source>https://amnezia.org/instructions/0_starter-guide</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardTextKey</name>
@@ -1941,8 +1949,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="vanished">Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="279"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="280"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="281"/>
<source>Server</source>
<translation>Сервер</translation>
</message>
@@ -2017,7 +2025,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="231"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="483"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="486"/>
<source>Users</source>
<translation type="unfinished">Пользователи</translation>
</message>
@@ -2027,47 +2035,52 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished">Имя пользователя</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="499"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="502"/>
<source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="595"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="584"/>
<source>Creation date: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="598"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="624"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="627"/>
<source>Client name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="632"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="636"/>
<source>Save</source>
<translation type="unfinished">Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="660"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="668"/>
<source>Revoke</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="663"/>
<source>Revoke the config for a user - </source>
<location filename="../ui/qml/Pages2/PageShare.qml" line="671"/>
<source>Revoke the config for a user - %1?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="664"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="672"/>
<source>The user will no longer be able to connect to your server.</source>
<translation type="unfinished">Пользователь больше не сможет подключаться к вашему серверу</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="665"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="673"/>
<source>Continue</source>
<translation type="unfinished">Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="666"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="674"/>
<source>Cancel</source>
<translation type="unfinished">Отменить</translation>
</message>
@@ -2081,20 +2094,20 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Поделиться доступом к VPN, без возможности управления сервером</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="331"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="332"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="333"/>
<source>Protocol</source>
<translation>Протокол</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="428"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="429"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="430"/>
<source>Connection format</source>
<translation>Формат подключения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="468"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="469"/>
<source>Share</source>
<translation>Поделиться</translation>
</message>
@@ -2124,12 +2137,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished">Сервер</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="102"/>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="100"/>
<source>Accessing </source>
<translation type="unfinished">Доступ </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="103"/>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="101"/>
<source>File with accessing settings to </source>
<translation type="unfinished"></translation>
</message>
@@ -2160,38 +2173,38 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::DeletePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="104"/>
<source>Password entry not found</source>
<translation>Password entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="108"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="108"/>
<source>Could not decrypt data</source>
<translation>Could not decrypt data</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="552"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="560"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="585"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="593"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="578"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="614"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="177"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="177"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="173"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="173"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="179"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="179"/>
<source>Could not remove private key from keystore</source>
<translation>Could not remove private key from keystore</translation>
</message>
@@ -2199,12 +2212,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::JobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="265"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="295"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="509"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="542"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
@@ -2212,27 +2225,27 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::PlainTextStore</name>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="65"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="65"/>
<source>Could not store data in settings: access error</source>
<translation>Could not store data in settings: access error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="67"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="67"/>
<source>Could not store data in settings: format error</source>
<translation>Could not store data in settings: format error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="85"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="85"/>
<source>Could not delete data from settings: access error</source>
<translation>Could not delete data from settings: access error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="87"/>
<source>Could not delete data from settings: format error</source>
<translation>Could not delete data from settings: format error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="104"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
@@ -2240,80 +2253,80 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="32"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="32"/>
<source>Password entry not found</source>
<translation>Password entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="139"/>
<source>Could not decrypt data</source>
<translation>Could not decrypt data</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="178"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="205"/>
<source>D-Bus is not running</source>
<translation>D-Bus is not running</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="187"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="197"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="214"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="224"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="286"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="316"/>
<source>No keychain service available</source>
<translation>No keychain service available</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="288"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="318"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="333"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="363"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="354"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="384"/>
<source>Could not determine data type: %1; %2</source>
<translation>Could not determine data type: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="363"/>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="52"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="393"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="52"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="372"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="402"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation>Unsupported entry type &apos;Map&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="375"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="405"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation>Unknown kwallet entry type &apos;%1&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="96"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="96"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="60"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="60"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="68"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="68"/>
<source>Could not retrieve private key from keystore</source>
<translation>Could not retrieve private key from keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="75"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="75"/>
<source>Could not create decryption cipher</source>
<translation>Could not create decryption cipher</translation>
</message>
@@ -2321,73 +2334,73 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::WritePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="78"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="78"/>
<source>Credential size exceeds maximum size of %1</source>
<translation>Credential size exceeds maximum size of %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="87"/>
<source>Credential key exceeds maximum size of %1</source>
<translation>Credential key exceeds maximum size of %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="92"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="92"/>
<source>Writing credentials failed: Win32 error code %1</source>
<translation>Writing credentials failed: Win32 error code %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="162"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="162"/>
<source>Encryption failed</source>
<translation>Encryption failed</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="415"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="445"/>
<source>D-Bus is not running</source>
<translation>D-Bus is not running</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="425"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="452"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="455"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="482"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="468"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="501"/>
<source>Could not open wallet: %1; %2</source>
<translation>Could not open wallet: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="144"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="144"/>
<source>Password not found</source>
<translation>Password not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="95"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="95"/>
<source>Could not open keystore</source>
<translation>Could not open keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="124"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="124"/>
<source>Could not create private key generator</source>
<translation>Could not create private key generator</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="131"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="131"/>
<source>Could not generate new private key</source>
<translation>Could not generate new private key</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="139"/>
<source>Could not retrieve private key from keystore</source>
<translation>Could not retrieve private key from keystore</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="147"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="147"/>
<source>Could not create encryption cipher</source>
<translation>Could not create encryption cipher</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="155"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="155"/>
<source>Could not encrypt data</source>
<translation>Could not encrypt data</translation>
</message>
@@ -2594,7 +2607,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>VPN pool error: no available addresses</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="64"/>
<location filename="../core/errorstrings.cpp" line="63"/>
<source>VPN connection error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="67"/>
<source>Internal error</source>
<translation>Internal error</translation>
</message>
@@ -2822,62 +2840,62 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<translation>Сервис SFTP</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/libsecret.cpp" line="119"/>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>
<source>Entry not found</source>
<translation>Entry not found</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="225"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="255"/>
<source>Access to keychain denied</source>
<translation>Access to keychain denied</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="227"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="257"/>
<source>No keyring daemon</source>
<translation>No keyring daemon</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="229"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="259"/>
<source>Already unlocked</source>
<translation>Already unlocked</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="231"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="261"/>
<source>No such keyring</source>
<translation>No such keyring</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="233"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="263"/>
<source>Bad arguments</source>
<translation>Bad arguments</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="235"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="265"/>
<source>I/O error</source>
<translation>I/O error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="237"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="267"/>
<source>Cancelled</source>
<translation>Cancelled</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="239"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="269"/>
<source>Keyring already exists</source>
<translation>Keyring already exists</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="241"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="271"/>
<source>No match</source>
<translation>No match</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="246"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="276"/>
<source>Unknown error</source>
<translation>Unknown error</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="72"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="72"/>
<source>error 0x%1: %2</source>
<translation>error 0x%1: %2</translation>
</message>
@@ -2893,13 +2911,13 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<context>
<name>Settings</name>
<message>
<location filename="../settings.cpp" line="26"/>
<location filename="../settings.cpp" line="30"/>
<source>Server #1</source>
<translation>Server #1</translation>
</message>
<message>
<location filename="../settings.cpp" line="202"/>
<location filename="../settings.cpp" line="209"/>
<location filename="../settings.cpp" line="206"/>
<location filename="../settings.cpp" line="213"/>
<source>Server</source>
<translation>Server</translation>
</message>
@@ -2907,22 +2925,22 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<context>
<name>SettingsController</name>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="25"/>
<location filename="../ui/controllers/settingsController.cpp" line="26"/>
<source>Software version</source>
<translation>Версия ПО</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="137"/>
<location filename="../ui/controllers/settingsController.cpp" line="139"/>
<source>All settings have been reset to default values</source>
<translation>Все настройки были сброшены к значению &quot;По умолчанию&quot;</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="143"/>
<location filename="../ui/controllers/settingsController.cpp" line="145"/>
<source>Cached profiles cleared</source>
<translation>Кэш профиля очищен</translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="122"/>
<location filename="../ui/controllers/settingsController.cpp" line="123"/>
<source>Backup file is corrupted</source>
<translation>Backup файл поврежден</translation>
</message>
@@ -3054,7 +3072,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<context>
<name>VpnConnection</name>
<message>
<location filename="../vpnconnection.cpp" line="429"/>
<location filename="../vpnconnection.cpp" line="432"/>
<source>Mbps</source>
<translation>Mbps</translation>
</message>
+112 -98
View File
@@ -11,15 +11,9 @@
<context>
<name>AndroidController</name>
<message>
<location filename="../platforms/android/android_controller.cpp" line="236"/>
<source>AmneziaVPN</source>
<translation></translation>
</message>
<message>
<location filename="../platforms/android/android_controller.cpp" line="239"/>
<source>VPN Connected</source>
<extracomment>Refers to the app - which is currently running the background and waiting</extracomment>
<translation>VPN已连接</translation>
<translation type="vanished">VPN已连接</translation>
</message>
</context>
<context>
@@ -158,7 +152,7 @@
<context>
<name>ImportController</name>
<message>
<location filename="../ui/controllers/importController.cpp" line="435"/>
<location filename="../ui/controllers/importController.cpp" line="411"/>
<source>Scanned %1 of %2.</source>
<translation> %1 of %2.</translation>
</message>
@@ -997,6 +991,11 @@ And if you don&apos;t like the app, all the more support it - the donation will
<source>Show other methods on Github</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="104"/>
<source>https://github.com/amnezia-vpn/amnezia-client#donate</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="113"/>
<source>Contacts</source>
@@ -1511,22 +1510,22 @@ And if you don&apos;t like the app, all the more support it - the donation will
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="110"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="111"/>
<source>Save</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="137"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="142"/>
<source>Protocols</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="143"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="148"/>
<source>Services</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="147"/>
<location filename="../ui/qml/Pages2/PageSettingsServerInfo.qml" line="152"/>
<source>Data</source>
<translation></translation>
</message>
@@ -1957,6 +1956,11 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>I have nothing</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="138"/>
<source>https://amnezia.org/instructions/0_starter-guide</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardTextKey</name>
@@ -2078,7 +2082,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="231"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="483"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="486"/>
<source>Users</source>
<translation type="unfinished"></translation>
</message>
@@ -2088,47 +2092,52 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation> VPN 访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="499"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="502"/>
<source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="595"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="584"/>
<source>Creation date: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="598"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="624"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="627"/>
<source>Client name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="632"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="636"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="660"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="668"/>
<source>Revoke</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="663"/>
<source>Revoke the config for a user - </source>
<location filename="../ui/qml/Pages2/PageShare.qml" line="671"/>
<source>Revoke the config for a user - %1?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="664"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="672"/>
<source>The user will no longer be able to connect to your server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="665"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="673"/>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="666"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="674"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
@@ -2170,8 +2179,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="obsolete"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="279"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="280"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="281"/>
<source>Server</source>
<translation></translation>
</message>
@@ -2193,8 +2202,8 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="obsolete"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="331"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="332"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="333"/>
<source>Protocol</source>
<translation></translation>
</message>
@@ -2214,14 +2223,14 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="428"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="429"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="430"/>
<source>Connection format</source>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="186"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="468"/>
<location filename="../ui/qml/Pages2/PageShare.qml" line="469"/>
<source>Share</source>
<translation></translation>
</message>
@@ -2251,12 +2260,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="102"/>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="100"/>
<source>Accessing </source>
<translation type="unfinished">访</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="103"/>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="101"/>
<source>File with accessing settings to </source>
<translation type="unfinished">访:</translation>
</message>
@@ -2287,38 +2296,38 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::DeletePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="104"/>
<source>Password entry not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="108"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="108"/>
<source>Could not decrypt data</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="552"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="560"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="585"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="593"/>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="578"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="614"/>
<source>Could not open wallet: %1; %2</source>
<translation>: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="177"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="177"/>
<source>Password not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="173"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="173"/>
<source>Could not open keystore</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="179"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="179"/>
<source>Could not remove private key from keystore</source>
<translation></translation>
</message>
@@ -2326,12 +2335,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::JobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="265"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="295"/>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="509"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="542"/>
<source>Access to keychain denied</source>
<translation>访</translation>
</message>
@@ -2339,27 +2348,27 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::PlainTextStore</name>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="65"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="65"/>
<source>Could not store data in settings: access error</source>
<translation>访</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="67"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="67"/>
<source>Could not store data in settings: format error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="85"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="85"/>
<source>Could not delete data from settings: access error</source>
<translation>访</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="87"/>
<source>Could not delete data from settings: format error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/plaintextstore.cpp" line="104"/>
<location filename="../3rd/qtkeychain/qtkeychain/plaintextstore.cpp" line="104"/>
<source>Entry not found</source>
<translation></translation>
</message>
@@ -2367,80 +2376,80 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="32"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="32"/>
<source>Password entry not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="36"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="139"/>
<source>Could not decrypt data</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="178"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="205"/>
<source>D-Bus is not running</source>
<translation>D-Bus未运行</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="187"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="197"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="214"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="224"/>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="286"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="316"/>
<source>No keychain service available</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="288"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="318"/>
<source>Could not open wallet: %1; %2</source>
<translation>: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="333"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="363"/>
<source>Access to keychain denied</source>
<translation>访</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="354"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="384"/>
<source>Could not determine data type: %1; %2</source>
<translation>: %1 %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="363"/>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="52"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="393"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="52"/>
<source>Entry not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="372"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="402"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation> &apos;Map&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="375"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="405"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation> &apos;%1&apos;</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="96"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="96"/>
<source>Password not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="60"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="60"/>
<source>Could not open keystore</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="68"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="68"/>
<source>Could not retrieve private key from keystore</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="75"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="75"/>
<source>Could not create decryption cipher</source>
<translation></translation>
</message>
@@ -2448,73 +2457,73 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<context>
<name>QKeychain::WritePasswordJobPrivate</name>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="78"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="78"/>
<source>Credential size exceeds maximum size of %1</source>
<translation>: %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="87"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="87"/>
<source>Credential key exceeds maximum size of %1</source>
<translation>: %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="92"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="92"/>
<source>Writing credentials failed: Win32 error code %1</source>
<translation>Win32错误码: %1</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_win.cpp" line="162"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_win.cpp" line="162"/>
<source>Encryption failed</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="415"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="445"/>
<source>D-Bus is not running</source>
<translation>D-Bus未运行</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="425"/>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="452"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="455"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="482"/>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="468"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="501"/>
<source>Could not open wallet: %1; %2</source>
<translation>: %1; %2</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="144"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="144"/>
<source>Password not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="95"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="95"/>
<source>Could not open keystore</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="124"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="124"/>
<source>Could not create private key generator</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="131"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="131"/>
<source>Could not generate new private key</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="139"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="139"/>
<source>Could not retrieve private key from keystore</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="147"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="147"/>
<source>Could not create encryption cipher</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_android.cpp" line="155"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_android.cpp" line="155"/>
<source>Could not encrypt data</source>
<translation></translation>
</message>
@@ -2666,6 +2675,11 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<source>Sftp error: No media was in remote drive</source>
<translation>Sftp 错误: 远程驱动器中没有媒介</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="63"/>
<source>VPN connection error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to save config to disk</source>
<translation type="vanished"></translation>
@@ -2730,7 +2744,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation type="vanished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="64"/>
<location filename="../core/errorstrings.cpp" line="67"/>
<source>Internal error</source>
<translation></translation>
</message>
@@ -2970,62 +2984,62 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<translation>Sftp - FTP </translation>
</message>
<message>
<location filename="../3rd/qtkeychain/libsecret.cpp" line="119"/>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>
<source>Entry not found</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="225"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="255"/>
<source>Access to keychain denied</source>
<translation>访</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="227"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="257"/>
<source>No keyring daemon</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="229"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="259"/>
<source>Already unlocked</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="231"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="261"/>
<source>No such keyring</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="233"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="263"/>
<source>Bad arguments</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="235"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="265"/>
<source>I/O error</source>
<translation>I/O错误</translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="237"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="267"/>
<source>Cancelled</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="239"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="269"/>
<source>Keyring already exists</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="241"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="271"/>
<source>No match</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_unix.cpp" line="246"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_unix.cpp" line="276"/>
<source>Unknown error</source>
<translation></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/keychain_haiku.cpp" line="72"/>
<location filename="../3rd/qtkeychain/qtkeychain/keychain_haiku.cpp" line="72"/>
<source>error 0x%1: %2</source>
<translation> 0x%1: %2</translation>
</message>
@@ -3041,13 +3055,13 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<context>
<name>Settings</name>
<message>
<location filename="../settings.cpp" line="26"/>
<location filename="../settings.cpp" line="30"/>
<source>Server #1</source>
<translation></translation>
</message>
<message>
<location filename="../settings.cpp" line="202"/>
<location filename="../settings.cpp" line="209"/>
<location filename="../settings.cpp" line="206"/>
<location filename="../settings.cpp" line="213"/>
<source>Server</source>
<translation></translation>
</message>
@@ -3055,22 +3069,22 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<context>
<name>SettingsController</name>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="25"/>
<location filename="../ui/controllers/settingsController.cpp" line="26"/>
<source>Software version</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="122"/>
<location filename="../ui/controllers/settingsController.cpp" line="123"/>
<source>Backup file is corrupted</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="137"/>
<location filename="../ui/controllers/settingsController.cpp" line="139"/>
<source>All settings have been reset to default values</source>
<translation></translation>
</message>
<message>
<location filename="../ui/controllers/settingsController.cpp" line="143"/>
<location filename="../ui/controllers/settingsController.cpp" line="145"/>
<source>Cached profiles cleared</source>
<translation></translation>
</message>
@@ -3206,7 +3220,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<context>
<name>VpnConnection</name>
<message>
<location filename="../vpnconnection.cpp" line="429"/>
<location filename="../vpnconnection.cpp" line="432"/>
<source>Mbps</source>
<translation></translation>
</message>
+1 -1
View File
@@ -15,7 +15,7 @@
#include "core/errorstrings.h"
#include "systemController.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/androidutils.h"
#include "platforms/android/android_utils.h"
#endif
#include "qrcodegen.hpp"
+6 -1
View File
@@ -239,7 +239,12 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data)
// && !configMap.value("PresharedKey").isEmpty() && !configMap.value("PublicKey").isEmpty()) {
lastConfig[config_key::client_priv_key] = configMap.value("PrivateKey");
lastConfig[config_key::client_ip] = configMap.value("Address");
lastConfig[config_key::psk_key] = configMap.value("PresharedKey");
if (!configMap.value("PresharedKey").isEmpty()) {
lastConfig[config_key::psk_key] = configMap.value("PresharedKey");
} else if (!configMap.value("PreSharedKey").isEmpty()) {
lastConfig[config_key::psk_key] = configMap.value("PreSharedKey");
}
lastConfig[config_key::server_pub_key] = configMap.value("PublicKey");
// } else {
// qDebug() << "Failed to import profile";
+1 -1
View File
@@ -7,7 +7,7 @@
#endif
#ifdef Q_OS_ANDROID
#include "../../platforms/android/androidutils.h"
#include "platforms/android/android_utils.h"
#include <QJniObject>
#endif
#if defined Q_OS_MAC
+24 -2
View File
@@ -7,19 +7,21 @@
#include "ui/qautostart.h"
#include "version.h"
#ifdef Q_OS_ANDROID
#include "../../platforms/android/android_controller.h"
#include "../../platforms/android/androidutils.h"
#include "platforms/android/android_utils.h"
#include "platforms/android/android_controller.h"
#include <QJniObject>
#endif
SettingsController::SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_languageModel(languageModel),
m_sitesModel(sitesModel),
m_settings(settings)
{
m_appVersion = QString("%1: %2 (%3)").arg(tr("Software version"), QString(APP_VERSION), __DATE__);
@@ -90,13 +92,21 @@ void SettingsController::openLogsFolder()
void SettingsController::exportLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getLogFile());
#endif
}
void SettingsController::clearLogs()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs();
#else
Logger::clearLogs();
Logger::clearServiceLogs();
#endif
}
void SettingsController::backupAppConfig(const QString &fileName)
@@ -134,6 +144,7 @@ void SettingsController::clearSettings()
m_serversModel->resetModel();
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_sitesModel->setRouteMode(Settings::RouteMode::VpnAllSites);
emit changeSettingsFinished(tr("All settings have been reset to default values"));
}
@@ -195,3 +206,14 @@ void SettingsController::toggleScreenshotsEnabled(bool enable)
});
#endif
}
bool SettingsController::isCameraPresent()
{
#if defined Q_OS_IOS
return true;
#elif defined Q_OS_ANDROID
return AndroidController::instance()->isCameraPresent();
#else
return false;
#endif
}
@@ -6,6 +6,7 @@
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/servers_model.h"
#include "ui/models/sites_model.h"
class SettingsController : public QObject
{
@@ -14,6 +15,7 @@ public:
explicit SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged)
@@ -57,6 +59,8 @@ public slots:
bool isScreenshotsEnabled();
void toggleScreenshotsEnabled(bool enable);
bool isCameraPresent();
signals:
void primaryDnsChanged();
void secondaryDnsChanged();
@@ -76,6 +80,7 @@ private:
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<SitesModel> m_sitesModel;
std::shared_ptr<Settings> m_settings;
QString m_appVersion;
+5 -14
View File
@@ -60,6 +60,11 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString
const QString &selectedFile, const bool isSaveMode, const QString &defaultSuffix)
{
QString fileName;
#ifdef Q_OS_ANDROID
Q_ASSERT(!isSaveMode);
return AndroidController::instance()->openFile(nameFilter);
#endif
#ifdef Q_OS_IOS
MobileUtils mobileUtils;
@@ -108,20 +113,6 @@ QString SystemController::getFileName(const QString &acceptLabel, const QString
}
fileName = mainFileDialog->property("selectedFile").toString();
#ifdef Q_OS_ANDROID
// patch for files containing spaces etc
const QString sep { "raw%3A%2F" };
if (fileName.startsWith("content://") && fileName.contains(sep)) {
QString contentUrl = fileName.split(sep).at(0);
QString rawUrl = fileName.split(sep).at(1);
rawUrl.replace(" ", "%20");
fileName = contentUrl + sep + rawUrl;
}
return fileName;
#endif
return QUrl(fileName).toLocalFile();
}
+29 -9
View File
@@ -231,7 +231,7 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt
}
}
beginInsertRows(QModelIndex(), rowCount(), 1);
beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1);
QJsonObject client;
client[configKey::clientId] = clientId;
@@ -245,8 +245,13 @@ ErrorCode ClientManagementModel::appendClient(const QString &clientId, const QSt
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
ServerController serverController(m_settings);
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
}
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
@@ -273,8 +278,13 @@ ErrorCode ClientManagementModel::renameClient(const int row, const QString &clie
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
ServerController serverController(m_settings);
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
}
ErrorCode error =
serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
@@ -345,8 +355,13 @@ ErrorCode ClientManagementModel::revokeOpenVpn(const int row, const DockerContai
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
}
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
@@ -395,8 +410,13 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont
const QByteArray clientsTableString = QJsonDocument(m_clientsTable).toJson();
const QString clientsTableFile =
QString("/opt/amnezia/%1/clientsTable").arg(ContainerProps::containerTypeToString(container));
QString clientsTableFile = QString("/opt/amnezia/%1/clientsTable");
if (container == DockerContainer::OpenVpn || container == DockerContainer::ShadowSocks
|| container == DockerContainer::Cloak) {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(DockerContainer::OpenVpn));
} else {
clientsTableFile = clientsTableFile.arg(ContainerProps::containerTypeToString(container));
}
error = serverController.uploadTextFileToContainer(container, credentials, clientsTableString, clientsTableFile);
if (error != ErrorCode::NoError) {
logger.error() << "Failed to upload the clientsTable file to the server";
+10
View File
@@ -15,6 +15,10 @@ ServersModel::ServersModel(std::shared_ptr<Settings> settings, QObject *parent)
auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString());
emit ServersModel::defaultContainerChanged(defaultContainer);
});
connect(this, &ServersModel::currentlyProcessedServerIndexChanged, this, [this](const int serverIndex) {
auto defaultContainer = ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString());
emit ServersModel::defaultContainerChanged(defaultContainer);
});
}
int ServersModel::rowCount(const QModelIndex &parent) const
@@ -269,6 +273,7 @@ void ServersModel::removeServer()
if (m_settings->serversCount() == 0) {
setDefaultServerIndex(-1);
}
setCurrentlyProcessedServerIndex(m_defaultServerIndex);
endResetModel();
}
@@ -526,3 +531,8 @@ void ServersModel::toggleAmneziaDns(bool enabled)
emit defaultServerDescriptionChanged();
}
bool ServersModel::isDefaultServerFromApi()
{
return m_settings->server(m_defaultServerIndex).value(config_key::configVersion).toInt();
}
+2
View File
@@ -97,6 +97,8 @@ public slots:
void toggleAmneziaDns(bool enabled);
bool isDefaultServerFromApi();
protected:
QHash<int, QByteArray> roleNames() const override;
@@ -26,6 +26,22 @@ ListView {
id: containersRadioButtonGroup
}
Connections {
target: ServersModel
function onCurrentlyProcessedServerIndexChanged() {
menuContent.checkCurrentItem()
}
}
function checkCurrentItem() {
var item = menuContent.itemAtIndex(currentIndex)
if (item !== null) {
var radioButton = item.children[0].children[0]
radioButton.checked = true
}
}
delegate: Item {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
+1
View File
@@ -31,6 +31,7 @@ PageType {
containersDropDown.rootButtonClickedFunction()
}
}
function onForceCloseDrawer() {
buttonContent.state = "collapsed"
}
+1 -1
View File
@@ -101,7 +101,7 @@ PageType {
text: qsTr("Show other methods on Github")
onClicked: Qt.openUrlExternally("https://github.com/amnezia-vpn/amnezia-client#donate")
onClicked: Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client#donate"))
}
ParagraphTextType {
+8
View File
@@ -28,6 +28,14 @@ PageType {
anchors.bottom: parent.bottom
contentHeight: content.height
enabled: !ServersModel.isDefaultServerFromApi()
Component.onCompleted: {
if (ServersModel.isDefaultServerFromApi()) {
PageController.showNotificationMessage(qsTr("Default server does not support custom dns"))
}
}
ColumnLayout {
id: content
@@ -55,6 +55,9 @@ PageType {
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }
]
}
Component.onCompleted: updateContainersModelFilters()
@@ -21,7 +21,13 @@ PageType {
id: root
property bool pageEnabled: {
return !ConnectionController.isConnected
return !ConnectionController.isConnected && !ServersModel.isDefaultServerFromApi()
}
Component.onCompleted: {
if (ServersModel.isDefaultServerFromApi()) {
PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function"))
}
}
Connections {
@@ -90,7 +90,7 @@ It's okay as long as it's from someone you trust.")
LabelWithButtonType {
Layout.fillWidth: true
visible: GC.isMobile()
visible: SettingsController.isCameraPresent()
text: qsTr("QR-code")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
@@ -105,7 +105,7 @@ It's okay as long as it's from someone you trust.")
}
DividerType {
visible: GC.isMobile()
visible: SettingsController.isCameraPresent()
}
LabelWithButtonType {
+7 -5
View File
@@ -117,12 +117,14 @@ PageType {
}
}
}
}
Component.onCompleted: {
if (index === containers.currentIndex) {
card.checked = true
card.clicked()
}
Component.onCompleted: {
var item = containers.itemAtIndex(containers.currentIndex)
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
}
@@ -42,6 +42,7 @@ PageType {
function onInstallServerFinished(finishedMessage) {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
@@ -135,7 +135,7 @@ PageType {
text: qsTr("I have nothing")
onClicked: Qt.openUrlExternally("https://amnezia.org/instructions/0_starter-guide")
onClicked: Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide"))
}
}
@@ -26,6 +26,7 @@ PageType {
function onImportFinished() {
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.currentlyProcessedIndex = ServersModel.defaultIndex
}
PageController.goToStartPage()
+18 -15
View File
@@ -64,24 +64,26 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
IpcClient::Interface()->resetIpStack();
IpcClient::Interface()->flushDns();
if (m_settings->routeMode() != Settings::VpnAllSites) {
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
}
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
if (!m_vpnConfiguration.value(config_key::configVersion).toInt()) {
if (m_settings->routeMode() != Settings::VpnAllSites) {
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
}
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
QTimer::singleShot(1000, m_vpnProtocol.data(),
[this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); });
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
QTimer::singleShot(1000, m_vpnProtocol.data(),
[this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); });
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode());
IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode());
}
}
} else if (state == Vpn::ConnectionState::Error) {
@@ -296,6 +298,7 @@ QJsonObject VpnConnection::createVpnConfiguration(int serverIndex, const ServerC
vpnConfiguration[config_key::hostName] = server.value(config_key::hostName).toString();
vpnConfiguration[config_key::description] = server.value(config_key::description).toString();
vpnConfiguration[config_key::configVersion] = server.value(config_key::configVersion).toInt();
// TODO: try to get hostName, port, description for 3rd party configs
// vpnConfiguration[config_key::port] = ...;
+20 -9
View File
@@ -7,15 +7,18 @@ usage() {
cat <<EOT
Usage:
build_android [options]
build_android [options] <artifact_types>
Build AmneziaVPN android client. By default, a signed Android App Bundle (AAB) is built.
Build AmneziaVPN android client.
Options:
-d, --debug Build debug version
Artifact types:
-u, --aab Build Android App Bundle (AAB)
-a, --apk (<abi_list> | all) Build APKs for the specified ABIs or for all available ABIs
Available ABIs: 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
<abi_list> - list of ABIs delimited by ';'
Options:
-d, --debug Build debug version
-b, --build-platform <platform> The SDK platform used for building the Java code of the application
By default, the latest available platform is used
-m, --move Move the build result to the root of the build directory
@@ -25,14 +28,14 @@ EOT
}
BUILD_TYPE="release"
AAB=1
opts=$(getopt -l debug,apk:,build-platform:,move,help -o "da:b:mh" -- "$@")
opts=$(getopt -l debug,aab,apk:,build-platform:,move,help -o "dua:b:mh" -- "$@")
eval set -- "$opts"
while true; do
case "$1" in
-d | --debug) BUILD_TYPE="debug"; shift;;
-a | --apk) ABIS=$2; unset AAB; shift 2;;
-u | --aab) AAB=1; shift;;
-a | --apk) ABIS=$2; shift 2;;
-b | --build-platform) ANDROID_BUILD_PLATFORM=$2; shift 2;;
-m | --move) MOVE_RESULT=1; shift;;
-h | --help) usage; exit 0;;
@@ -49,6 +52,11 @@ if [[ -v ABIS && \
exit 1
fi
# At least one artifact type must be specified
if [[ ! (-v AAB || -v ABIS) ]]; then
usage; exit 0
fi
echo "Build script started..."
PROJECT_DIR=$(pwd)
@@ -137,7 +145,8 @@ gradle_opts=()
if [ -v AAB ]; then
gradle_opts+=(bundle"${BUILD_TYPE^}")
else
fi
if [ -v ABIS ]; then
gradle_opts+=(assemble"${BUILD_TYPE^}")
fi
@@ -151,7 +160,9 @@ if [[ -v CI || -v MOVE_RESULT ]]; then
if [ -v AAB ]; then
mv -u $OUT_APP_DIR/android-build/build/outputs/bundle/$BUILD_TYPE/AmneziaVPN-$BUILD_TYPE.aab \
$PROJECT_DIR/deploy/build/
else
fi
if [ -v ABIS ]; then
if [ "$ABIS" = "all" ]; then
ABIS="x86;x86_64;armeabi-v7a;arm64-v8a"
fi
+1 -3
View File
@@ -83,6 +83,4 @@ ldd $CQTDEPLOYER_DIR/bin/binarycreator
cp -r $PROJECT_DIR/deploy/installer $BUILD_DIR
$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer
$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $BUILD_DIR/installer/config/linux.xml -p $BUILD_DIR/installer/packages -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer.bin