Compare commits

..

1 Commits

Author SHA1 Message Date
vladimir.kuznetsov
74c55edc0c updated SingleApplication submodule 2024-08-29 13:21:05 +04:00
107 changed files with 1915 additions and 3353 deletions

View File

@@ -297,24 +297,24 @@ jobs:
env:
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.7.2
QT_VERSION: 6.6.2
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
steps:
- name: 'Install desktop Qt'
uses: jurplel/install-qt-action@v4
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'desktop'
arch: 'linux_gcc_64'
arch: 'gcc_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v4
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -325,7 +325,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v4
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -336,7 +336,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v4
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
@@ -347,7 +347,7 @@ jobs:
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v4
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'

3
.gitmodules vendored
View File

@@ -16,3 +16,6 @@
[submodule "client/3rd/QSimpleCrypto"]
path = client/3rd/QSimpleCrypto
url = https://github.com/amnezia-vpn/QSimpleCrypto.git
[submodule "client/3rd/SingleApplication"]
path = client/3rd/SingleApplication
url = git@github.com:itay-grudev/SingleApplication.git

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.0.0
project(${PROJECT} VERSION 4.7.0.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 58)
set(APP_ANDROID_VERSION_CODE 57)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View File

@@ -10,10 +10,10 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
<br>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_4.7.0.0.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.7.0.0/AmneziaVPN_Linux_4.7.0.0.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.7.0.0"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_4.6.0.3_x64.exe"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/win.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_4.6.0.3.dmg"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/mac.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/download/4.6.0.3/AmneziaVPN_Linux_4.6.0.3.tar.zip"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/lin.png" width="150" style="max-width: 100%;"></a>
<a href="https://github.com/amnezia-vpn/amnezia-client/releases/tag/4.6.0.3"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/andr.png" width="150" style="max-width: 100%;"></a>
<br>

1
client/3rd/SingleApplication vendored Submodule

View File

@@ -1,25 +0,0 @@
include_directories(${CMAKE_CURRENT_LIST_DIR})
find_package(Qt6 REQUIRED COMPONENTS
Core Network
)
set(LIBS ${LIBS} Qt6::Core Qt6::Network)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/singleapplication.h
${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/singleapplication.cpp
${CMAKE_CURRENT_LIST_DIR}/singleapplication_p.cpp
)
if(WIN32)
if(MSVC)
set(LIBS ${LIBS} Advapi32.lib)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(LIBS ${LIBS} advapi32)
endif()
endif()

View File

@@ -1,274 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <QtCore/QElapsedTimer>
#include <QtCore/QByteArray>
#include <QtCore/QSharedMemory>
#include "singleapplication.h"
#include "singleapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program
* if another instance already exists
* @param argc
* @param argv
* @param allowSecondary Whether to enable secondary instance support
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
{
Q_D( SingleApplication );
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
// On Android and iOS since the library is not supported fallback to
// standard QApplication behaviour by simply returning at this point.
qWarning() << "SingleApplication is not supported on Android and iOS systems.";
return;
#endif
// Store the current mode of the program
d->options = options;
// Add any unique user data
if ( ! userData.isEmpty() )
d->addAppData( userData );
// Generating an application ID used for identifying the shared memory
// block and QLocalServer
d->genBlockServerName();
// To mitigate QSharedMemory issues with large amount of processes
// attempting to attach at the same time
SingleApplicationPrivate::randomSleep();
#ifdef Q_OS_UNIX
// By explicitly attaching it and then deleting it we make sure that the
// memory is deleted even after the process has crashed on Unix.
d->memory = new QSharedMemory( d->blockServerName );
d->memory->attach();
delete d->memory;
#endif
// Guarantee thread safe behaviour with a shared memory block.
d->memory = new QSharedMemory( d->blockServerName );
// Create a shared memory block
if( d->memory->create( sizeof( InstancesInfo ) )){
// Initialize the shared memory block
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after create.";
abortSafely();
}
d->initializeMemoryBlock();
} else {
if( d->memory->error() == QSharedMemory::AlreadyExists ){
// Attempt to attach to the memory segment
if( ! d->memory->attach() ){
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
abortSafely();
}
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
abortSafely();
}
} else {
qCritical() << "SingleApplication: Unable to create block.";
abortSafely();
}
}
auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state
while( true ){
// If the shared memory block's checksum is valid continue
if( d->blockChecksum() == inst->checksum ) break;
// If more than 5s have elapsed, assume the primary instance crashed and
// assume it's position
if( time.elapsed() > 5000 ){
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
d->initializeMemoryBlock();
}
// Otherwise wait for a random period and try again. The random sleep here
// limits the probability of a collision between two racing apps and
// allows the app to initialise faster
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
qDebug() << d->memory->errorString();
}
SingleApplicationPrivate::randomSleep();
if( ! d->memory->lock() ){
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
abortSafely();
}
}
if( inst->primary == false ){
d->startPrimary();
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
qDebug() << d->memory->errorString();
}
return;
}
// Check if another instance can be started
if( allowSecondary ){
d->startSecondary();
if( d->options & Mode::SecondaryNotification ){
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
qDebug() << d->memory->errorString();
}
return;
}
if( ! d->memory->unlock() ){
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
qDebug() << d->memory->errorString();
}
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
delete d;
::exit( EXIT_SUCCESS );
}
SingleApplication::~SingleApplication()
{
Q_D( SingleApplication );
delete d;
}
/**
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
bool SingleApplication::isPrimary() const
{
Q_D( const SingleApplication );
return d->server != nullptr;
}
/**
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
bool SingleApplication::isSecondary() const
{
Q_D( const SingleApplication );
return d->server == nullptr;
}
/**
* Allows you to identify an instance by returning unique consecutive instance
* ids. It is reset when the first (primary) instance of your app starts and
* only incremented afterwards.
* @return Returns a unique instance id.
*/
quint32 SingleApplication::instanceId() const
{
Q_D( const SingleApplication );
return d->instanceNumber;
}
/**
* Returns the OS PID (Process Identifier) of the process running the primary
* instance. Especially useful when SingleApplication is coupled with OS.
* specific APIs.
* @return Returns the primary instance PID.
*/
qint64 SingleApplication::primaryPid() const
{
Q_D( const SingleApplication );
return d->primaryPid();
}
/**
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
QString SingleApplication::primaryUser() const
{
Q_D( const SingleApplication );
return d->primaryUser();
}
/**
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
QString SingleApplication::currentUser() const
{
return SingleApplicationPrivate::getUsername();
}
/**
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfuly, false otherwise.
*/
bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
{
Q_D( SingleApplication );
// Nobody to connect to
if( isPrimary() ) return false;
// Make sure the socket is connected
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false;
d->socket->write( message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
}
/**
* Cleans up the shared memory block and exits with a failure.
* This function halts program execution.
*/
void SingleApplication::abortSafely()
{
Q_D( SingleApplication );
qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
delete d;
::exit( EXIT_FAILURE );
}
QStringList SingleApplication::userData() const
{
Q_D( const SingleApplication );
return d->appData();
}

View File

@@ -1,154 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2018
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H
#include <QtCore/QtGlobal>
#include <QtNetwork/QLocalSocket>
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QApplication
#endif
#include QT_STRINGIFY(QAPPLICATION_CLASS)
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multiple instances of the same
* Application
* @see QCoreApplication
*/
class SingleApplication : public QAPPLICATION_CLASS
{
Q_OBJECT
using app_t = QAPPLICATION_CLASS;
public:
/**
* @brief Mode of operation of SingleApplication.
* Whether the block should be user-wide or system-wide and whether the
* primary instance should be notified when a secondary instance had been
* started.
* @note Operating system can restrict the shared memory blocks to the same
* user, in which case the User/System modes will have no effect and the
* block will be user wide.
* @enum
*/
enum Mode {
User = 1 << 0,
System = 1 << 1,
SecondaryNotification = 1 << 2,
ExcludeAppVersion = 1 << 3,
ExcludeAppPath = 1 << 4
};
Q_DECLARE_FLAGS(Options, Mode)
/**
* @brief Intitializes a SingleApplication instance with argc command line
* arguments in argv
* @arg {int &} argc - Number of arguments in argv
* @arg {const char *[]} argv - Supplied command line arguments
* @arg {bool} allowSecondary - Whether to start the instance as secondary
* if there is already a primary instance.
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
* User wide or System wide.
* @arg {int} timeout - Timeout to wait in milliseconds.
* @note argc and argv may be changed as Qt removes arguments that it
* recognizes
* @note Mode::SecondaryNotification only works if set on both the primary
* instance and the secondary instance.
* @note The timeout is just a hint for the maximum time of blocking
* operations. It does not guarantee that the SingleApplication
* initialisation will be completed in given time, though is a good hint.
* Usually 4*timeout would be the worst case (fail) scenario.
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
*/
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
~SingleApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting
* @returns {bool}
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage( const QByteArray &message, int timeout = 100 );
/**
* @brief Get the set user data.
* @returns {QStringList}
*/
QStringList userData() const;
Q_SIGNALS:
void instanceStarted();
void receivedMessage( quint32 instanceId, QByteArray message );
private:
SingleApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleApplication)
void abortSafely();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
#endif // SINGLE_APPLICATION_H

View File

@@ -1,15 +0,0 @@
QT += core network
CONFIG += c++11
HEADERS += \
$$PWD/singleapplication.h \
$$PWD/singleapplication_p.h
SOURCES += $$PWD/singleapplication.cpp \
$$PWD/singleapplication_p.cpp
INCLUDEPATH += $$PWD
win32 {
msvc:LIBS += Advapi32.lib
gcc:LIBS += -ladvapi32
}

View File

@@ -1,486 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <cstdlib>
#include <cstddef>
#include <QtCore/QDir>
#include <QtCore/QThread>
#include <QtCore/QByteArray>
#include <QtCore/QDataStream>
#include <QtCore/QElapsedTimer>
#include <QtCore/QCryptographicHash>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
#include <QtCore/QRandomGenerator>
#else
#include <QtCore/QDateTime>
#endif
#include "singleapplication.h"
#include "singleapplication_p.h"
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif
#ifdef Q_OS_WIN
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#include <lmcons.h>
#endif
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
: q_ptr( q_ptr )
{
server = nullptr;
socket = nullptr;
memory = nullptr;
instanceNumber = 0;
}
SingleApplicationPrivate::~SingleApplicationPrivate()
{
if( socket != nullptr ){
socket->close();
delete socket;
}
if( memory != nullptr ){
memory->lock();
auto *inst = static_cast<InstancesInfo*>(memory->data());
if( server != nullptr ){
server->close();
delete server;
inst->primary = false;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
memory->unlock();
delete memory;
}
}
QString SingleApplicationPrivate::getUsername()
{
#ifdef Q_OS_WIN
wchar_t username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = UNLEN + 1;
if( GetUserNameW( username, &usernameLength ) )
return QString::fromWCharArray( username );
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
#else
return qEnvironmentVariable( "USERNAME" );
#endif
#endif
#ifdef Q_OS_UNIX
QString username;
uid_t uid = geteuid();
struct passwd *pw = getpwuid( uid );
if( pw )
username = QString::fromLocal8Bit( pw->pw_name );
if ( username.isEmpty() ){
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
username = QString::fromLocal8Bit( qgetenv( "USER" ) );
#else
username = qEnvironmentVariable( "USER" );
#endif
}
return username;
#endif
}
void SingleApplicationPrivate::genBlockServerName()
{
QCryptographicHash appData( QCryptographicHash::Sha256 );
appData.addData( "SingleApplication", 17 );
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
if ( ! appDataList.isEmpty() )
appData.addData( appDataList.join( "" ).toUtf8() );
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
}
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
#ifdef Q_OS_WIN
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
#else
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
#endif
}
// User level block requires a user specific data in the hash
if( options & SingleApplication::Mode::User ){
appData.addData( getUsername().toUtf8() );
}
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
// server naming requirements.
blockServerName = appData.result().toBase64().replace("/", "_");
}
void SingleApplicationPrivate::initializeMemoryBlock() const
{
auto *inst = static_cast<InstancesInfo*>( memory->data() );
inst->primary = false;
inst->secondary = 0;
inst->primaryPid = -1;
inst->primaryUser[0] = '\0';
inst->checksum = blockChecksum();
}
void SingleApplicationPrivate::startPrimary()
{
// Reset the number of connections
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->primary = true;
inst->primaryPid = QCoreApplication::applicationPid();
qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
inst->checksum = blockChecksum();
instanceNumber = 0;
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
// Restrict access to the socket according to the
// SingleApplication::Mode::User flag on User level or no restrictions
if( options & SingleApplication::Mode::User ){
server->setSocketOptions( QLocalServer::UserAccessOption );
} else {
server->setSocketOptions( QLocalServer::WorldAccessOption );
}
server->listen( blockServerName );
QObject::connect(
server,
&QLocalServer::newConnection,
this,
&SingleApplicationPrivate::slotConnectionEstablished
);
}
void SingleApplicationPrivate::startSecondary()
{
auto *inst = static_cast <InstancesInfo*>( memory->data() );
inst->secondary += 1;
inst->checksum = blockChecksum();
instanceNumber = inst->secondary;
}
bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
{
QElapsedTimer time;
time.start();
// Connect to the Local Server of the Primary Instance if not already
// connected.
if( socket == nullptr ){
socket = new QLocalSocket();
}
if( socket->state() == QLocalSocket::ConnectedState ) return true;
if( socket->state() != QLocalSocket::ConnectedState ){
while( true ){
randomSleep();
if( socket->state() != QLocalSocket::ConnectingState )
socket->connectToServer( blockServerName );
if( socket->state() == QLocalSocket::ConnectingState ){
socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
}
// If connected break out of the loop
if( socket->state() == QLocalSocket::ConnectedState ) break;
// If elapsed time since start is longer than the method timeout return
if( time.elapsed() >= msecs ) return false;
}
}
// Initialisation message according to the SingleApplication protocol
QByteArray initMsg;
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
writeStream.setVersion(QDataStream::Qt_5_6);
#endif
writeStream << blockServerName.toLatin1();
writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif
writeStream << checksum;
// The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6);
#endif
headerStream << static_cast <quint64>( initMsg.length() );
socket->write( header );
socket->write( initMsg );
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
socket->flush();
return result;
}
quint16 SingleApplicationPrivate::blockChecksum() const
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
#else
quint16 checksum = qChecksum(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum));
#endif
return checksum;
}
qint64 SingleApplicationPrivate::primaryPid() const
{
qint64 pid;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
pid = inst->primaryPid;
memory->unlock();
return pid;
}
QString SingleApplicationPrivate::primaryUser() const
{
QByteArray username;
memory->lock();
auto *inst = static_cast<InstancesInfo*>( memory->data() );
username = inst->primaryUser;
memory->unlock();
return QString::fromUtf8( username );
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished()
{
QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
}
);
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
[nextConnSocket, this](){
connectionMap.remove(nextConnSocket);
}
);
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
[nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket];
switch(info.stage){
case StageHeader:
readInitMessageHeader(nextConnSocket);
break;
case StageBody:
readInitMessageBody(nextConnSocket);
break;
case StageConnected:
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
break;
default:
break;
};
}
);
}
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )){
return;
}
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
return;
}
QDataStream headerStream( sock );
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion( QDataStream::Qt_5_6 );
#endif
// Read the header to know the message length
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock];
info.stage = StageBody;
info.msgLen = msgLen;
if ( sock->bytesAvailable() >= (qint64) msgLen ){
readInitMessageBody( sock );
}
}
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{
Q_Q(SingleApplication);
if (!connectionMap.contains( sock )){
return;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return;
}
// Read the message body
QByteArray msgBytes = sock->read(info.msgLen);
QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
readStream.setVersion( QDataStream::Qt_5_6 );
#endif
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast <ConnectionType>( connTypeVal );
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif
bool isValid = readStream.status() == QDataStream::Ok &&
QLatin1String(latin1Name) == blockServerName &&
msgChecksum == actualChecksum;
if( !isValid ){
sock->close();
return;
}
info.instanceId = instanceId;
info.stage = StageConnected;
if( connectionType == NewInstance ||
( connectionType == SecondaryInstance &&
options & SingleApplication::Mode::SecondaryNotification ) )
{
Q_EMIT q->instanceStarted();
}
if (sock->bytesAvailable() > 0){
Q_EMIT this->slotDataAvailable( sock, instanceId );
}
}
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{
Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
}
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{
if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId );
}
void SingleApplicationPrivate::randomSleep()
{
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
#endif
}
void SingleApplicationPrivate::addAppData(const QString &data)
{
appDataList.push_back(data);
}
QStringList SingleApplicationPrivate::appData() const
{
return appDataList;
}

View File

@@ -1,104 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the SingleApplication API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#ifndef SINGLEAPPLICATION_P_H
#define SINGLEAPPLICATION_P_H
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include "singleapplication.h"
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
char primaryUser[128];
quint16 checksum; // Must be the last field
};
struct ConnectionInfo {
qint64 msgLen = 0;
quint32 instanceId = 0;
quint8 stage = 0;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
enum ConnectionType : quint8 {
InvalidConnection = 0,
NewInstance = 1,
SecondaryInstance = 2,
Reconnect = 3
};
enum ConnectionStage : quint8 {
StageHeader = 0,
StageBody = 1,
StageConnected = 2,
};
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr );
~SingleApplicationPrivate() override;
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary( int msecs, ConnectionType connectionType );
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket);
static void randomSleep();
void addAppData(const QString &data);
QStringList appData() const;
SingleApplication *q_ptr;
QSharedMemory *memory;
QLocalSocket *socket;
QLocalServer *server;
quint32 instanceNumber;
QString blockServerName;
SingleApplication::Options options;
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
QStringList appDataList;
public Q_SLOTS:
void slotConnectionEstablished();
void slotDataAvailable( QLocalSocket*, quint32 );
void slotClientConnectionClosed( QLocalSocket*, quint32 );
};
#endif // SINGLEAPPLICATION_P_H

View File

@@ -27,9 +27,6 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
add_definitions(-DPROD_AGW_PUBLIC_KEY="$ENV{PROD_AGW_PUBLIC_KEY}")
add_definitions(-DPROD_PROXY_STORAGE_KEY="$ENV{PROD_PROXY_STORAGE_KEY}")
add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
@@ -113,7 +110,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/../ipc
${CMAKE_CURRENT_LIST_DIR}/../common/logger
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
@@ -135,6 +131,7 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
${CMAKE_CURRENT_BINARY_DIR}/version.h
@@ -143,7 +140,6 @@ set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/core/serialization/serialization.h
${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
)
# Mozilla headres
@@ -194,7 +190,6 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/core/serialization/trojan.cpp
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
)
# Mozilla sources
@@ -241,14 +236,8 @@ file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
)
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h
${CMAKE_CURRENT_LIST_DIR}/ui/models/localServices/*.h
)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/models/localServices/*.cpp
)
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
set(HEADERS ${HEADERS}
${COMMON_FILES_H}
@@ -273,12 +262,10 @@ if(WIN32)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h
${CMAKE_CURRENT_LIST_DIR}/localServices/goodByeDpi.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
${CMAKE_CURRENT_LIST_DIR}/localServices/goodByeDpi.cpp
)
set(RESOURCES ${RESOURCES}

View File

@@ -164,7 +164,7 @@ void AmneziaApplication::init()
bool enabled = m_settings->isSaveLogs();
#ifndef Q_OS_ANDROID
if (enabled) {
if (!Logger::init(false)) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
@@ -235,7 +235,6 @@ void AmneziaApplication::registerTypes()
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
PageLoader::declareQmlFolderEnum();
}
void AmneziaApplication::loadFonts()
@@ -456,13 +455,4 @@ void AmneziaApplication::initControllers()
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_localServicesController.reset(new LocalServicesController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("LocalServicesController", m_localServicesController.get());
connect(m_connectionController.get(), &ConnectionController::startLocalService, m_localServicesController.get(),
&LocalServicesController::start);
connect(m_connectionController.get(), &ConnectionController::stopLocalService, m_localServicesController.get(),
&LocalServicesController::stop);
connect(m_localServicesController.get(), &LocalServicesController::serviceStateChanged, m_connectionController.get(),
&ConnectionController::connectionStateChanged);
}

View File

@@ -24,7 +24,6 @@
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/localServicesController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
@@ -54,9 +53,9 @@
#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"
#define AMNEZIA_BASE_CLASS SingleApplication
//#define QAPPLICATION_CLASS QGuiApplication
#endif
class AmneziaApplication : public AMNEZIA_BASE_CLASS
@@ -137,7 +136,6 @@ private:
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<LocalServicesController> m_localServicesController;
QNetworkAccessManager *m_nam;

View File

@@ -3,6 +3,7 @@
<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%% --"
android:installLocation="auto">
@@ -45,7 +46,7 @@
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
android:windowSoftInputMode="stateUnchanged|adjustResize"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
@@ -67,6 +68,9 @@
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
</activity>
<activity
@@ -84,13 +88,6 @@
android:exported="false"
android:theme="@style/Translucent" />
<activity android:name=".AuthActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />
<activity
android:name=".ImportConfigActivity"
android:excludeFromRecents="true"

View File

@@ -3,6 +3,3 @@
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging
// package name for androiddeployqt
namespace = "org.amnezia.vpn"

View File

@@ -115,11 +115,9 @@ dependencies {
implementation(project(":xray"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
implementation(libs.androidx.fragment)
implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.protobuf)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
implementation(libs.androidx.datastore)
implementation(libs.androidx.biometric)
}

View File

@@ -1,28 +1,24 @@
[versions]
agp = "8.5.2"
kotlin = "1.9.24"
androidx-core = "1.13.1"
androidx-activity = "1.9.1"
androidx-annotation = "1.8.2"
androidx-biometric = "1.2.0-alpha05"
androidx-camera = "1.3.4"
androidx-fragment = "1.8.2"
agp = "8.2.0"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-activity = "1.8.1"
androidx-annotation = "1.7.0"
androidx-camera = "1.3.0"
androidx-security-crypto = "1.1.0-alpha06"
androidx-datastore = "1.1.1"
kotlinx-coroutines = "1.8.1"
androidx-datastore = "1.1.0-beta01"
kotlinx-coroutines = "1.7.3"
kotlinx-serialization = "1.6.3"
google-mlkit = "17.3.0"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
androidx-biometric = { module = "androidx.biometric:biometric-ktx", version.ref = "androidx-biometric" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,8 +84,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

View File

@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail

View File

@@ -58,7 +58,7 @@ open class OpenVpn : Protocol() {
scope = CoroutineScope(Dispatchers.IO)
}
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(

View File

@@ -42,7 +42,7 @@ abstract class Protocol {
protected abstract fun internalInit()
abstract suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()

View File

@@ -21,5 +21,5 @@ android {
}
dependencies {
api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
}

View File

@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF0E0E11</color>
<style name="NoActionBar">
<item name="android:windowBackground">@color/black</item>
<item name="android:colorBackground">@color/black</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>

View File

@@ -22,7 +22,7 @@ dependencyResolutionManagement {
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.5.2"
id("com.android.settings") version "8.2.0"
id("settings-property-delegate")
}

View File

@@ -158,10 +158,6 @@ class AmneziaActivity : QtActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "Create Amnezia activity: $intent")
window.apply {
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = getColor(R.color.black)
}
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val proto = mainScope.async(Dispatchers.IO) {
VpnStateStore.getVpnState().vpnProto
@@ -614,14 +610,6 @@ class AmneziaActivity : QtActivity() {
}
}
@Suppress("unused")
fun setNavigationBarColor(color: Int) {
Log.v(TAG, "Change navigation bar color: ${"#%08X".format(color)}")
mainScope.launch {
window.navigationBarColor = color
}
}
@Suppress("unused")
fun minimizeApp() {
Log.v(TAG, "Minimize application")
@@ -696,17 +684,6 @@ class AmneziaActivity : QtActivity() {
.show()
}
@Suppress("unused")
fun requestAuthentication() {
Log.v(TAG, "Request authentication")
mainScope.launch {
qtInitialized.await()
Intent(this@AmneziaActivity, AuthActivity::class.java).also {
startActivity(it)
}
}
}
/**
* Utils methods
*/

View File

@@ -31,7 +31,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.drop
@@ -112,10 +111,6 @@ open class AmneziaVpnService : VpnService() {
get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME }
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
connectionJob?.cancel()
connectionJob = null
disconnectionJob?.cancel()
disconnectionJob = null
protocolState.value = DISCONNECTED
when (e) {
is IllegalArgumentException,
@@ -536,7 +531,7 @@ open class AmneziaVpnService : VpnService() {
protocolState.value = DISCONNECTING
disconnectionJob = connectionScope.launch {
connectionJob?.cancelAndJoin()
connectionJob?.join()
connectionJob = null
vpnProto?.protocol?.stopVpn()

View File

@@ -1,97 +0,0 @@
package org.amnezia.vpn
import android.os.Build
import android.os.Bundle
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AuthenticationResult
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import org.amnezia.vpn.qt.QtAndroidController
import org.amnezia.vpn.util.Log
private const val TAG = "AuthActivity"
private const val AUTHENTICATORS = BIOMETRIC_STRONG or DEVICE_CREDENTIAL
class AuthActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val biometricManager = BiometricManager.from(applicationContext)
when (biometricManager.canAuthenticate(AUTHENTICATORS)) {
BiometricManager.BIOMETRIC_SUCCESS -> {
showBiometricPrompt(biometricManager)
return
}
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
Log.w(TAG, "Unknown biometric status")
showBiometricPrompt(biometricManager)
return
}
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
Log.e(TAG, "The specified options are incompatible with the current Android " +
"version ${Build.VERSION.SDK_INT}")
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
Log.w(TAG, "The hardware is unavailable")
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
Log.w(TAG, "No biometric or device credential is enrolled")
}
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
Log.w(TAG, "There is no suitable hardware")
}
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
Log.w(TAG, "A security vulnerability has been discovered with one or " +
"more hardware sensors")
}
}
QtAndroidController.onAuthResult(true)
finish()
}
private fun showBiometricPrompt(biometricManager: BiometricManager) {
val executor = ContextCompat.getMainExecutor(applicationContext)
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d(TAG, "Authentication succeeded")
QtAndroidController.onAuthResult(true)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.w(TAG, "Authentication failed")
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.e(TAG, "Authentication error $errorCode: $errString")
QtAndroidController.onAuthResult(false)
finish()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setAllowedAuthenticators(AUTHENTICATORS)
.setTitle("AmneziaVPN")
.setSubtitle(biometricManager.getStrings(AUTHENTICATORS)?.promptMessage)
.build()
biometricPrompt.authenticate(promptInfo)
}
}

View File

@@ -0,0 +1,24 @@
package org.amnezia.vpn;
import android.content.Context;
import android.app.KeyguardManager;
import android.content.Intent;
import org.qtproject.qt.android.bindings.QtActivity;
import static android.content.Context.KEYGUARD_SERVICE;
public class AuthHelper extends QtActivity {
static final String TAG = "AuthHelper";
public static Intent getAuthIntent(Context context) {
KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE);
if (mKeyguardManager.isDeviceSecure()) {
return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
} else {
return null;
}
}
}

View File

@@ -33,10 +33,10 @@ class ImportConfigActivity : ComponentActivity() {
intent?.let(::readConfig)
}
override fun onNewIntent(intent: Intent) {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.d(TAG, "onNewIntent: $intent")
intent.let(::readConfig)
intent?.let(::readConfig)
}
private fun readConfig(intent: Intent) {

View File

@@ -25,7 +25,5 @@ object QtAndroidController {
external fun onConfigImported(data: String)
external fun onAuthResult(result: Boolean)
external fun decodeQrCode(data: String): Boolean
}

View File

@@ -88,7 +88,7 @@ class NetworkState(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val numberAttempts = 300
val numberAttempts = 3
var attemptCount = 0
while(true) {
try {

View File

@@ -1,12 +1,7 @@
package org.amnezia.vpn.protocol.wireguard
import android.net.VpnService.Builder
import java.io.IOException
import java.util.Locale
import java.util.TreeMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.amnezia.awg.GoBackend
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
@@ -84,44 +79,12 @@ open class Wireguard : Protocol() {
if (!isInitialized) loadSharedLibrary(context, "wg-go")
}
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val wireguardConfig = parseConfig(config)
val startTime = System.currentTimeMillis()
start(wireguardConfig, vpnBuilder, protect)
waitForConnection(startTime)
state.value = CONNECTED
}
private suspend fun waitForConnection(startTime: Long) {
Log.d(TAG, "Waiting for connection")
withContext(Dispatchers.IO) {
val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0)
try {
delay(1000)
var log = getLogcat(time)
Log.d(TAG, "First waiting log: $log")
// check that there is a connection log,
// to avoid infinite connection
if (!log.contains("Attaching to interface")) {
Log.w(TAG, "Logs do not contain a connection log")
return@withContext
}
while (!log.contains("Received handshake response")) {
delay(1000)
log = getLogcat(time)
}
} catch (e: IOException) {
Log.e(TAG, "Failed to get logcat: $e")
}
}
}
private fun getLogcat(time: String): String =
ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time)
.redirectErrorStream(true)
.start()
.inputStream.reader().readText()
protected open fun parseConfig(config: JSONObject): WireguardConfig {
val configDataJson = config.getJSONObject("wireguard_config_data")
val configData = parseConfigData(configDataJson.getString("config"))

View File

@@ -109,7 +109,7 @@ class Xray : Protocol() {
}
}
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
if (isRunning) {
Log.w(TAG, "XRay already running")
return

View File

@@ -3,7 +3,10 @@ set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
if(NOT IOS AND NOT ANDROID)
include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake)
message(${QT_DEFAULT_MAJOR_VERSION})
set(QAPPLICATION_CLASS QGuiApplication)
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SingleApplication)
set(LIBS ${LIBS} SingleApplication::SingleApplication)
endif()
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)

View File

@@ -27,6 +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_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h
${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h
)
@@ -34,6 +35,7 @@ set(HEADERS ${HEADERS}
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.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
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
)

View File

@@ -1,7 +1,7 @@
#include "containers_defs.h"
#include "QJsonDocument"
#include "QJsonObject"
#include "QJsonDocument"
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c)
{
@@ -96,7 +96,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{ DockerContainer::Awg, "AmneziaWG" },
{ DockerContainer::Xray, "XRay" },
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
{ DockerContainer::SSXray, "Shadowsocks" },
{ DockerContainer::SSXray, "Shadowsocks"},
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("AmneziaDNS") },
@@ -124,24 +124,27 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
"but very resistant to blockages. "
"Recommended for regions with high levels of censorship.") },
{ DockerContainer::Xray,
QObject::tr(
"XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
"signal loss. It has native support on the latest versions of Android and iOS.") },
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
{ DockerContainer::Dns, QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp, QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy, QObject::tr("") } };
{ DockerContainer::Dns,
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp,
QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy,
QObject::tr("") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{
return {
{ DockerContainer::OpenVpn,
QObject::tr("OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
QObject::tr(
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
"It employs its unique security protocol, "
"leveraging the strength of SSL/TLS for encryption and key exchange. "
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
@@ -157,8 +160,7 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"* Can operate over both TCP and UDP network protocols.") },
{ DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS "
"connection."
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
"* Available in the AmneziaVPN only on desktop platforms\n"
@@ -215,18 +217,15 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"* Works over UDP network protocol.") },
{ DockerContainer::Xray,
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, "
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to "
"evasion.\n"
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate "
"clients while diverting censors to genuine websites like google.com, "
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.\n"
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
"thus presenting an authentic TLS certificate and data. \n"
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as "
"coming from random, "
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
"legitimate sites without the need for specific configurations. \n"
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection "
"by sophisticated DPI systems employing active probing techniques. "
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.") },
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
},
{ DockerContainer::Ipsec,
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
@@ -288,8 +287,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::Cloak: return true;
case DockerContainer::SSXray:
return true;
case DockerContainer::SSXray: return true;
// case DockerContainer::ShadowSocks: return true;
default: return false;
}
@@ -385,8 +383,10 @@ bool ContainerProps::isShareable(DockerContainer container)
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
{
QString protocolConfigString =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
.toObject()
.value(config_key::last_config)
.toString();
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
}

View File

@@ -9,8 +9,8 @@
#include "QRsa.h"
#include "amnezia_application.h"
#include "configurators/wireguard_configurator.h"
#include "core/enums/apiEnums.h"
#include "configurators/wireguard_configurator.h"
#include "version.h"
namespace
@@ -42,7 +42,7 @@ namespace
constexpr char keyPayload[] = "key_payload";
}
const QStringList proxyStorageUrl = { "" };
const QStringList proxyStorageUrl = {""};
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{
@@ -65,8 +65,7 @@ namespace
}
}
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment)
ApiController::ApiController(const QString &gatewayEndpoint, QObject *parent) : QObject(parent), m_gatewayEndpoint(gatewayEndpoint)
{
}
@@ -144,7 +143,7 @@ QStringList ApiController::getProxyUrls()
QEventLoop wait;
QList<QSslError> sslErrors;
QNetworkReply *reply;
QNetworkReply* reply;
for (const auto &proxyStorageUrl : proxyStorageUrl) {
request.setUrl(proxyStorageUrl);
@@ -282,7 +281,7 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
QNetworkReply *reply;
QNetworkReply* reply;
reply = amnApp->manager()->get(request);
QEventLoop wait;
@@ -301,8 +300,7 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
break;
}
reply->deleteLater();
@@ -357,7 +355,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
EVP_PKEY *publicKey = nullptr;
try {
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
QByteArray key = PROD_AGW_PUBLIC_KEY;
QSimpleCrypto::QRsa rsa;
publicKey = rsa.getPublicKeyFromByteArray(key);
} catch (...) {
@@ -377,7 +375,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
QNetworkReply *reply = manager.post(request, QJsonDocument(requestBody).toJson());
QNetworkReply* reply = manager.post(request, QJsonDocument(requestBody).toJson());
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
@@ -397,8 +395,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
if (reply->error() != QNetworkReply::NetworkError::TimeoutError
&& reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
if (reply->error() != QNetworkReply::NetworkError::TimeoutError && reply->error() != QNetworkReply::NetworkError::OperationCanceledError) {
break;
}
reply->deleteLater();

View File

@@ -14,7 +14,7 @@ class ApiController : public QObject
Q_OBJECT
public:
explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
explicit ApiController(const QString &gatewayEndpoint, QObject *parent = nullptr);
public slots:
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
@@ -44,7 +44,6 @@ private:
QString m_gatewayEndpoint;
QStringList m_proxyUrls;
bool m_isDevEnvironment;
};
#endif // APICONTROLLER_H

View File

@@ -83,6 +83,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
}
qDebug().noquote() << lineToExec;
Logger::appendSshLog("Run command:" + lineToExec);
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) {
@@ -99,6 +100,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
{
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e)

View File

@@ -80,7 +80,6 @@ namespace amnezia
ExecutableMissing = 604,
XrayExecutableMissing = 605,
Tun2SockExecutableMissing = 606,
GoodByeDPIExecutableMissing = 607,
// VPN errors
OpenVpnAdaptersInUseError = 700,

View File

@@ -43,7 +43,6 @@ QString errorString(ErrorCode code) {
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
case (ErrorCode::GoodByeDPIExecutableMissing): errorMessage = QObject::tr("GoodbyeDPI executable missing"); break;
// VPN errors
case (ErrorCode::OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break;

View File

@@ -1,62 +0,0 @@
#include "goodByeDpi.h"
#include "core/ipcclient.h"
#include "utilities.h"
GoodByeDpi::GoodByeDpi(QObject *parent) : QObject { parent }
{
}
amnezia::ErrorCode GoodByeDpi::start(const QString &blackListFile, const int modset)
{
if (!QFileInfo::exists(Utils::goodbyedpiPath())) {
return amnezia::ErrorCode::GoodByeDPIExecutableMissing;
}
m_goodbyeDPIProcess = IpcClient::CreatePrivilegedProcess();
if (!m_goodbyeDPIProcess) {
return amnezia::ErrorCode::AmneziaServiceConnectionFailed;
}
m_goodbyeDPIProcess->waitForSource(1000);
if (!m_goodbyeDPIProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
return amnezia::ErrorCode::AmneziaServiceConnectionFailed;
}
m_goodbyeDPIProcess->setProgram(amnezia::PermittedProcess::GoodbyeDPI);
QStringList arguments;
arguments << QString("-%1").arg(modset);
arguments << QString("--blacklist %1").arg(blackListFile);
m_goodbyeDPIProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::stateChanged, [&](QProcess::ProcessState newState) {
qDebug() << "PrivilegedProcess stateChanged" << newState;
if (newState == QProcess::Running) {
qDebug() << "PrivilegedProcess running";
emit serviceStateChanged(Vpn::ConnectionState::Connected);
}
});
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::finished, this, [&]() {
qDebug() << "PrivilegedProcess finished";
emit serviceStateChanged(Vpn::ConnectionState::Disconnected);
});
m_goodbyeDPIProcess->start();
return amnezia::ErrorCode::NoError;
}
void GoodByeDpi::stop()
{
if (m_goodbyeDPIProcess) {
m_goodbyeDPIProcess->close();
}
}

View File

@@ -1,25 +0,0 @@
#ifndef GOODBYEDPI_H
#define GOODBYEDPI_H
#include <QObject>
#include "core/defs.h"
#include "core/privileged_process.h"
#include "protocols/vpnprotocol.h"
class GoodByeDpi : public QObject
{
Q_OBJECT
public:
explicit GoodByeDpi(QObject *parent = nullptr);
amnezia::ErrorCode start(const QString &blackListFile, const int modset);
void stop();
private:
QSharedPointer<PrivilegedProcess> m_goodbyeDPIProcess;
signals:
void serviceStateChanged(Vpn::ConnectionState state);
};
#endif // GOODBYEDPI_H

View File

@@ -4,18 +4,18 @@
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QJsonDocument>
#include <QMetaEnum>
#include <QJsonDocument>
#include <QStandardPaths>
#include <QUrl>
#include <iostream>
#include "utilities.h"
#include "version.h"
#include "utilities.h"
#ifdef AMNEZIA_DESKTOP
#include <core/ipcclient.h>
#include <core/ipcclient.h>
#endif
#ifdef Q_OS_IOS
@@ -25,9 +25,8 @@
QFile Logger::m_file;
QTextStream Logger::m_textStream;
QString Logger::m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
QString Logger::m_serviceLogFileName = QString("%1.log").arg(SERVICE_NAME);
void debugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
if (msg.simplified().isEmpty()) {
return;
@@ -38,12 +37,12 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext &context, cons
return;
}
if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font")
|| msg.startsWith("stale focus object")) {
if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font") || msg.startsWith("stale focus object")) {
return;
}
Logger::m_textStream << qFormatLogMessage(type, context, msg) << Qt::endl << Qt::flush;
Logger::appendAllLog(qFormatLogMessage(type, context, msg));
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
}
@@ -54,24 +53,36 @@ Logger &Logger::Instance()
return s;
}
bool Logger::init(bool isServiceLogger)
void Logger::appendSshLog(const QString &log)
{
QString path = isServiceLogger ? systemLogDir() : userLogsDir();
QString logFileName = isServiceLogger ? m_serviceLogFileName : m_logFileName ;
QString dt = QDateTime::currentDateTime().toString();
Instance().m_sshLog.append(dt + ": " + log + "\n");
emit Instance().sshLogChanged(Instance().sshLog());
}
void Logger::appendAllLog(const QString &log)
{
Instance().m_allLog.append(log + "\n");
emit Instance().allLogChanged(Instance().allLog());
}
bool Logger::init()
{
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
QString path = userLogsDir();
QDir appDir(path);
if (!appDir.mkpath(path)) {
return false;
}
m_file.setFileName(appDir.filePath(logFileName));
m_file.setFileName(appDir.filePath(m_logFileName));
if (!m_file.open(QIODevice::Append)) {
qWarning() << "Cannot open log file:" << logFileName;
qWarning() << "Cannot open log file:" << m_logFileName;
return false;
}
m_file.setTextModeEnabled(true);
m_textStream.setDevice(&m_file);
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
#if !defined(QT_DEBUG) || defined(Q_OS_IOS)
qInstallMessageHandler(debugMessageHandler);
@@ -88,8 +99,7 @@ void Logger::deInit()
m_file.close();
}
bool Logger::setServiceLogsEnabled(bool enabled)
{
bool Logger::setServiceLogsEnabled(bool enabled) {
#ifdef AMNEZIA_DESKTOP
IpcClient *m_IpcClient = new IpcClient;
@@ -102,7 +112,8 @@ bool Logger::setServiceLogsEnabled(bool enabled)
if (m_IpcClient->Interface()) {
m_IpcClient->Interface()->setLogsEnabled(enabled);
} else {
}
else {
qWarning() << "Error occurred setting up service logs";
return false;
}
@@ -116,32 +127,11 @@ QString Logger::userLogsDir()
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
}
QString Logger::systemLogDir()
{
#ifdef Q_OS_WIN
QStringList locationList = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
QString primaryLocation = "ProgramData";
foreach (const QString &location, locationList) {
if (location.contains(primaryLocation)) {
return QString("%1/%2/log").arg(location).arg(APPLICATION_NAME);
}
}
return QString();
#else
return QString("/var/log/%1").arg(APPLICATION_NAME);
#endif
}
QString Logger::userLogsFilePath()
{
return userLogsDir() + QDir::separator() + m_logFileName;
}
QString Logger::serviceLogsFilePath()
{
return systemLogDir() + QDir::separator() + m_serviceLogFileName;
}
QString Logger::getLogFile()
{
m_file.flush();
@@ -149,32 +139,18 @@ QString Logger::getLogFile()
file.open(QIODevice::ReadOnly);
QString qtLog = file.readAll();
#ifdef Q_OS_IOS
return QString().fromStdString(AmneziaVPN::swiftUpdateLogData(qtLog.toStdString()));
#else
return qtLog;
#endif
}
QString Logger::getServiceLogFile()
bool Logger::openLogsFolder()
{
m_file.flush();
QFile file(serviceLogsFilePath());
file.open(QIODevice::ReadOnly);
QString qtLog = file.readAll();
#ifdef Q_OS_IOS
return QString().fromStdString(AmneziaVPN::swiftUpdateLogData(qtLog.toStdString()));
#else
return qtLog;
#endif
}
bool Logger::openLogsFolder(bool isServiceLogger)
{
QString path = isServiceLogger ? systemLogDir() : userLogsDir();
QString path = userLogsDir();
#ifdef Q_OS_WIN
path = "file:///" + path;
#endif
@@ -185,23 +161,38 @@ bool Logger::openLogsFolder(bool isServiceLogger)
return true;
}
void Logger::clearLogs(bool isServiceLogger)
bool Logger::openServiceLogsFolder()
{
QString path = Utils::systemLogPath();
#ifdef Q_OS_WIN
path = "file:///" + path;
#endif
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
return true;
}
QString Logger::appLogFileNamePath()
{
return m_file.fileName();
}
void Logger::clearLogs()
{
bool isLogActive = m_file.isOpen();
m_file.close();
QFile file(isServiceLogger ? serviceLogsFilePath() : userLogsFilePath());
QFile file(userLogsFilePath());
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.resize(0);
file.close();
#ifdef Q_OS_IOS
AmneziaVPN::swiftDeleteLog();
#endif
if (isLogActive) {
init(isServiceLogger);
init();
}
}
@@ -219,7 +210,8 @@ void Logger::clearServiceLogs()
if (m_IpcClient->Interface()) {
m_IpcClient->Interface()->clearLogs();
} else {
}
else {
qWarning() << "Error occurred cleaning up service logs";
}
#endif
@@ -227,41 +219,26 @@ void Logger::clearServiceLogs()
void Logger::cleanUp()
{
clearLogs(false);
clearLogs();
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
dir.removeRecursively();
clearLogs(true);
clearServiceLogs();
}
Logger::Log::Log(Logger *logger, LogLevel logLevel) : m_logger(logger), m_logLevel(logLevel), m_data(new Data())
{
}
Logger::Log::Log(Logger* logger, LogLevel logLevel)
: m_logger(logger), m_logLevel(logLevel), m_data(new Data()) {}
Logger::Log::~Log()
{
Logger::Log::~Log() {
qDebug() << "Amnezia" << m_logger->className() << m_data->m_buffer.trimmed();
delete m_data;
}
Logger::Log Logger::error()
{
return Log(this, LogLevel::Error);
}
Logger::Log Logger::warning()
{
return Log(this, LogLevel::Warning);
}
Logger::Log Logger::info()
{
return Log(this, LogLevel::Info);
}
Logger::Log Logger::debug()
{
return Log(this, LogLevel::Debug);
}
QString Logger::sensitive(const QString &input)
{
Logger::Log Logger::error() { return Log(this, LogLevel::Error); }
Logger::Log Logger::warning() { return Log(this, LogLevel::Warning); }
Logger::Log Logger::info() { return Log(this, LogLevel::Info); }
Logger::Log Logger::debug() { return Log(this, LogLevel::Debug); }
QString Logger::sensitive(const QString& input) {
#ifdef Q_DEBUG
return input;
#else
@@ -270,51 +247,48 @@ QString Logger::sensitive(const QString &input)
#endif
}
#define CREATE_LOG_OP_REF(x) \
Logger::Log &Logger::Log::operator<<(x t) \
{ \
m_data->m_ts << t << ' '; \
return *this; \
}
#define CREATE_LOG_OP_REF(x) \
Logger::Log& Logger::Log::operator<<(x t) { \
m_data->m_ts << t << ' '; \
return *this; \
}
CREATE_LOG_OP_REF(uint64_t);
CREATE_LOG_OP_REF(const char *);
CREATE_LOG_OP_REF(const QString &);
CREATE_LOG_OP_REF(const QByteArray &);
CREATE_LOG_OP_REF(const void *);
CREATE_LOG_OP_REF(const char*);
CREATE_LOG_OP_REF(const QString&);
CREATE_LOG_OP_REF(const QByteArray&);
CREATE_LOG_OP_REF(const void*);
#undef CREATE_LOG_OP_REF
Logger::Log &Logger::Log::operator<<(const QStringList &t)
{
Logger::Log& Logger::Log::operator<<(const QStringList& t) {
m_data->m_ts << '[' << t.join(",") << ']' << ' ';
return *this;
}
Logger::Log &Logger::Log::operator<<(const QJsonObject &t)
{
Logger::Log& Logger::Log::operator<<(const QJsonObject& t) {
m_data->m_ts << QJsonDocument(t).toJson(QJsonDocument::Indented) << ' ';
return *this;
}
Logger::Log &Logger::Log::operator<<(QTextStreamFunction t)
{
Logger::Log& Logger::Log::operator<<(QTextStreamFunction t) {
m_data->m_ts << t;
return *this;
}
void Logger::Log::addMetaEnum(quint64 value, const QMetaObject *meta, const char *name)
{
void Logger::Log::addMetaEnum(quint64 value, const QMetaObject* meta,
const char* name) {
QMetaEnum me = meta->enumerator(meta->indexOfEnumerator(name));
QString out;
QTextStream ts(&out);
if (const char *scope = me.scope()) {
if (const char* scope = me.scope()) {
ts << scope << "::";
}
const char *key = me.valueToKey(static_cast<int>(value));
const char* key = me.valueToKey(static_cast<int>(value));
const bool scoped = me.isScoped();
if (scoped || !key) {
ts << me.enumName() << (!key ? "(" : "::");

107
client/logger.h Normal file
View File

@@ -0,0 +1,107 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QString>
#include <QTextStream>
#include "ui/property_helper.h"
#include "mozilla/shared/loglevel.h"
class Logger : public QObject
{
Q_OBJECT
AUTO_PROPERTY(QString, sshLog)
AUTO_PROPERTY(QString, allLog)
public:
static Logger& Instance();
static void appendSshLog(const QString &log);
static void appendAllLog(const QString &log);
static bool init();
static void deInit();
static bool setServiceLogsEnabled(bool enabled);
static bool openLogsFolder();
static bool openServiceLogsFolder();
static QString appLogFileNamePath();
static void clearLogs();
static void clearServiceLogs();
static void cleanUp();
static QString userLogsFilePath();
static QString getLogFile();
// compat with Mozilla logger
Logger(const QString &className) { m_className = className; }
const QString& className() const { return m_className; }
class Log {
public:
Log(Logger* logger, LogLevel level);
~Log();
Log& operator<<(uint64_t t);
Log& operator<<(const char* t);
Log& operator<<(const QString& t);
Log& operator<<(const QStringList& t);
Log& operator<<(const QByteArray& t);
Log& operator<<(const QJsonObject& t);
Log& operator<<(QTextStreamFunction t);
Log& operator<<(const void* t);
// Q_ENUM
template <typename T>
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, Log&>::type
operator<<(T t) {
const QMetaObject* meta = qt_getEnumMetaObject(t);
const char* name = qt_getEnumName(t);
addMetaEnum(typename QFlags<T>::Int(t), meta, name);
return *this;
}
private:
void addMetaEnum(quint64 value, const QMetaObject* meta, const char* name);
Logger* m_logger;
LogLevel m_logLevel;
struct Data {
Data() : m_ts(&m_buffer, QIODevice::WriteOnly) {}
QString m_buffer;
QTextStream m_ts;
};
Data* m_data;
};
Log error();
Log warning();
Log info();
Log debug();
QString sensitive(const QString& input);
private:
Logger() {}
Logger(Logger const &) = delete;
Logger& operator= (Logger const&) = delete;
static QString userLogsDir();
static QFile m_file;
static QTextStream m_textStream;
static QString m_logFileName;
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
// compat with Mozilla logger
QString m_className;
};
#endif // LOGGER_H

View File

@@ -98,7 +98,6 @@ bool AndroidController::initialize()
{"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)},
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
};
@@ -211,11 +210,6 @@ void AndroidController::setScreenshotsEnabled(bool enabled)
callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled);
}
void AndroidController::setNavigationBarColor(unsigned int color)
{
callActivityMethod("setNavigationBarColor", "(I)V", color);
}
void AndroidController::minimizeApp()
{
callActivityMethod("minimizeApp", "()V");
@@ -271,22 +265,6 @@ void AndroidController::requestNotificationPermission()
callActivityMethod("requestNotificationPermission", "()V");
}
bool AndroidController::requestAuthentication()
{
QEventLoop wait;
bool result;
connect(this, &AndroidController::authenticationResult, this,
[&result, &wait](const bool &authResult){
qDebug() << "Android authentication result:" << authResult;
result = authResult;
wait.quit();
},
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
callActivityMethod("requestAuthentication", "()V");
wait.exec();
return result;
}
// Moving log processing to the Android side
jclass AndroidController::log;
jmethodID AndroidController::logDebug;
@@ -484,14 +462,6 @@ void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data
emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data));
}
// static
void AndroidController::onAuthResult(JNIEnv *env, jobject thiz, jboolean result)
{
Q_UNUSED(thiz);
emit AndroidController::instance()->authenticationResult(result);
}
// static
bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
{

View File

@@ -41,13 +41,11 @@ public:
void exportLogsFile(const QString &fileName);
void clearLogs();
void setScreenshotsEnabled(bool enabled);
void setNavigationBarColor(unsigned int color);
void minimizeApp();
QJsonArray getAppList();
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
bool isNotificationPermissionGranted();
void requestNotificationPermission();
bool requestAuthentication();
static bool initLogging();
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
@@ -65,7 +63,6 @@ signals:
void configImported(QString config);
void importConfigFromOutside(QString config);
void initConnectionState(Vpn::ConnectionState state);
void authenticationResult(bool result);
private:
bool isWaitingStatus = true;
@@ -92,7 +89,6 @@ private:
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 void onAuthResult(JNIEnv *env, jobject thiz, jboolean result);
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
template <typename Ret, typename ...Args>

View File

@@ -0,0 +1,16 @@
#include "authResultReceiver.h"
AuthResultReceiver::AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier) : m_notifier(notifier)
{
}
void AuthResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
{
qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode;
if (resultCode == -1) { // ResultOK
emit m_notifier->authSuccessful();
} else {
emit m_notifier->authFailed();
}
}

View File

@@ -0,0 +1,32 @@
#ifndef AUTHRESULTRECEIVER_H
#define AUTHRESULTRECEIVER_H
#include <QJniObject>
#include <private/qandroidextras_p.h>
class AuthResultNotifier : public QObject
{
Q_OBJECT
public:
AuthResultNotifier(QObject *parent = nullptr) : QObject(parent) {};
signals:
void authFailed();
void authSuccessful();
};
/* Auth result handler for Android */
class AuthResultReceiver final : public QAndroidActivityResultReceiver
{
public:
AuthResultReceiver(QSharedPointer<AuthResultNotifier> &notifier);
void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) override;
private:
QSharedPointer<AuthResultNotifier> m_notifier;
};
#endif // AUTHRESULTRECEIVER_H

View File

@@ -12,7 +12,7 @@
#include "Winsvc.h"
/**
* @brief The WindowsServiceManager provides control over the MozillaVPNBroker
* @brief The WindowsServiceManager provides controll over the MozillaVPNBroker
* service via SCM
*/
class WindowsServiceManager : public QObject {

View File

@@ -100,8 +100,6 @@ namespace amnezia
constexpr char clientId[] = "clientId";
constexpr char isGoodbyeDpi[] = "is_goodbye_dpi";
}
namespace protocols
@@ -256,8 +254,7 @@ namespace amnezia
TorWebSite,
Dns,
Sftp,
Socks5Proxy,
GoodyeDPI
Socks5Proxy
};
Q_ENUM_NS(Proto)

View File

@@ -218,7 +218,6 @@
<file>ui/qml/Pages2/PageSettingsApiLanguageList.qml</file>
<file>images/controls/archive-restore.svg</file>
<file>images/controls/help-circle.svg</file>
<file>ui/qml/Pages2/LocalServices/PageGoodByeDpiSettings.qml</file>
</qresource>
<qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file>

View File

@@ -174,25 +174,13 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
QByteArray SecureQSettings::encryptText(const QByteArray &value) const
{
QSimpleCrypto::QBlockCipher cipher;
QByteArray result;
try {
result = cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
} catch (...) { // todo change error handling in QSimpleCrypto?
qCritical() << "error when encrypting the settings value";
}
return result;
return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
}
QByteArray SecureQSettings::decryptText(const QByteArray &ba) const
{
QSimpleCrypto::QBlockCipher cipher;
QByteArray result;
try {
result = cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
} catch (...) { // todo change error handling in QSimpleCrypto?
qCritical() << "error when decrypting the settings value";
}
return result;
return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
}
bool SecureQSettings::encryptionRequired() const

View File

@@ -227,7 +227,7 @@ void Settings::setSaveLogs(bool enabled)
if (!isSaveLogs()) {
Logger::deInit();
} else {
if (!Logger::init(false)) {
if (!Logger::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
}
@@ -519,52 +519,7 @@ void Settings::setGatewayEndpoint(const QString &endpoint)
m_gatewayEndpoint = endpoint;
}
void Settings::setDevGatewayEndpoint()
{
m_gatewayEndpoint = DEV_AGW_ENDPOINT;
}
QString Settings::getGatewayEndpoint()
{
return m_gatewayEndpoint;
}
bool Settings::isDevGatewayEnv()
{
return m_isDevGatewayEnv;
}
void Settings::toggleDevGatewayEnv(bool enabled)
{
m_isDevGatewayEnv = enabled;
}
void Settings::setGoodbyeDpiBlackListFile(const QString &file)
{
setValue("Conf/goodbyeDpiBlackListFile", file);
}
QString Settings::getGoodbyeDpiBlackListFile() const
{
return value("Conf/goodbyeDpiBlackListFile").toString();
}
void Settings::toggleGoodbyeDpi(bool enable)
{
setValue("Conf/isGoodbyeDpiEnabled", enable);
}
bool Settings::isGoodbyeDpiEnabled() const
{
return value("Conf/isGoodbyeDpiEnabled", false).toBool();
}
void Settings::setGoodbyeDpiModset(const int modset)
{
setValue("Conf/goodbyeDpiModset", modset);
}
int Settings::getGoodbyeDpiModset() const
{
return value("Conf/goodbyeDpiModset", 9).toInt();
}

View File

@@ -113,10 +113,7 @@ public:
QString routeModeString(RouteMode mode) const;
RouteMode routeMode() const;
void setRouteMode(RouteMode mode)
{
setValue("Conf/routeMode", mode);
}
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
bool isSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
@@ -186,7 +183,7 @@ public:
bool isScreenshotsEnabled() const
{
return value("Conf/screenshotsEnabled", true).toBool();
return value("Conf/screenshotsEnabled", false).toBool();
}
void setScreenshotsEnabled(bool enabled)
{
@@ -220,19 +217,7 @@ public:
void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint);
void setDevGatewayEndpoint();
QString getGatewayEndpoint();
bool isDevGatewayEnv();
void toggleDevGatewayEnv(bool enabled);
void setGoodbyeDpiBlackListFile(const QString &file);
QString getGoodbyeDpiBlackListFile() const;
void toggleGoodbyeDpi(bool enable);
bool isGoodbyeDpiEnabled() const;
void setGoodbyeDpiModset(const int modset);
int getGoodbyeDpiModset() const;
signals:
void saveLogsChanged(bool enabled);
@@ -249,7 +234,6 @@ private:
mutable SecureQSettings m_settings;
QString m_gatewayEndpoint;
bool m_isDevGatewayEnv;
};
#endif // SETTINGS_H

View File

@@ -6,47 +6,47 @@
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="63"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>شبكة VPN كلاسيكية للعمل المريح وتنزيل الملفات الكبيرة ومشاهدة مقاطع الفيديو. تعمل مع أي موقع. تصل السرعة إلى %1 ميجابت/ثانية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="67"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>شبكة VPN للولوج للمواقع المحظورة في بلاد ذو مستوي عالي من الرقابة علي الانترنت. </translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>Amenzia Premium - شبكة VPN للعمل المريح, تحميل ملفات كبيرة الحجم, ومشاهدة مقاطع الفيديو ب جودة عالية. تعمل لجميع المواقع, حتي في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="75"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>Amnezia Free هو VPN مجاني لتخطي الحظر في البلاد ذو مستوي عالي من الرقابة علي الانترنت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="80"/>
<source>%1 MBit/s</source>
<translation>%1 ميجابت/ثانية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="87"/>
<source>%1 days</source>
<translation>%1 ايام</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>سيقوم VPN فقط بفتح المواقع المشهورة المحظورة في بلدك, مثل Instagram, Facebook, Twitter و مواقع اخري. المواقع الاخري ستٌفتح من عنوان ال IP الحقيقي الخاص بك, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;معلومات اخري علي الموقع.&lt;/a&gt;</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="104"/>
<source>Free</source>
<translation>مجاني</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="106"/>
<source>%1 $/month</source>
<translation>%1 دولار/الشهر</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -325,7 +325,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/controllers/installController.cpp" line="608"/>
<source>Api config removed</source>
<translation>تم حذف تكوين Api</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="630"/>
@@ -345,17 +345,17 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/controllers/installController.cpp" line="845"/>
<source>%1 installed successfully.</source>
<translation>تم تحميل %1 بنجاح</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="877"/>
<source>API config reloaded</source>
<translation>تمت إعادة تحميل تكوين API</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="881"/>
<source>Successfully changed the country of connection to %1</source>
<translation>تم تغيير بلد الاتصال بنجاح إلى %1</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -441,7 +441,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageDevMenu.qml" line="74"/>
<source>Gateway endpoint</source>
<translation>نقطة نهاية البوابة</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -507,47 +507,47 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="147"/>
<source>Jc - Junk packet count</source>
<translation>Jc - عدد الحزم غير المرغوب فيها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="172"/>
<source>Jmin - Junk packet minimum size</source>
<translation>Jmin - الحجم الادني للحزم الغير مرغوب فيها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="193"/>
<source>Jmax - Junk packet maximum size</source>
<translation>Jmax - الحجم الاقصي للحزم الغير مرغوب فيها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="214"/>
<source>S1 - Init packet junk size</source>
<translation>S1 - حجم حزمة البيانات العشوائية الأولية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="235"/>
<source>S2 - Response packet junk size</source>
<translation>S2 - حجم حزمة الاستجابة غير المرغوب فيها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="256"/>
<source>H1 - Init packet magic header</source>
<translation>H1 - حزمة رأس سحرية مبدئية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="277"/>
<source>H2 - Response packet magic header</source>
<translation>H2 - رأس حزمة الاستجابة السحرية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="298"/>
<source>H4 - Transport packet magic header</source>
<translation>H4 - رأس حزمة النقل السحرية</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="320"/>
<source>H3 - Underload packet magic header</source>
<translation>H3 - رأس حزمة السحر غير المحمل</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="363"/>
@@ -1164,7 +1164,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettings.qml" line="137"/>
<source>Dev console</source>
<translation>وحدة تحكم التطوير</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettings.qml" line="158"/>
@@ -1250,74 +1250,74 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>For the region</source>
<translation>للمنطقة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/>
<source>Price</source>
<translation>السعر</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<source>Work period</source>
<translation>مدة العمل</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<source>Speed</source>
<translation>السرعة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
<source>Support tag</source>
<translation>علامة الدعم</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<source>Copied</source>
<translation>تم النسخ</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<source>Reload API config</source>
<translation>إعادة تحميل تكوين API</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<source>Reload API config?</source>
<translation>إعادة تحميل تكوين API</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<source>Continue</source>
<translation>واصل</translation>
<translation type="unfinished">واصل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Cancel</source>
<translation>إلغاء</translation>
<translation type="unfinished">إلغاء</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<source>Cannot reload API config during active connection</source>
<translation>لا يمكن إعادة تحميل تكوين API اثناء تواجد اتصال نشط</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<source>Remove from application</source>
<translation>احذف من التطبيق</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<source>Remove from application?</source>
<translation>احذف من التطبيق؟</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Cannot remove server during active connection</source>
<translation>لا يمكن إزالة الخادم أثناء الاتصال النشط</translation>
<translation type="unfinished">لا يمكن إزالة الخادم أثناء الاتصال النشط</translation>
</message>
</context>
<context>
@@ -1769,7 +1769,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="33"/>
<source>No new installed containers found</source>
<translation>لم يتم العثور علي اي خدمات مٌثبتة سابقاً</translation>
<translation>لم يتم العثور علي اي حاويات جديدة مٌثبتة</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="127"/>
@@ -1941,7 +1941,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="190"/>
<source>Remove %1 from server?</source>
<translation>احذف %1 من الخادم؟</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="191"/>
@@ -2080,32 +2080,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/>
<source>For the region</source>
<translation>للمنطقة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/>
<source>Price</source>
<translation>السعر</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/>
<source>Work period</source>
<translation>مدة العمل</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/>
<source>Speed</source>
<translation>السرعة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/>
<source>Features</source>
<translation>المميزات</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/>
<source>Connect</source>
<translation>اتصل</translation>
<translation type="unfinished">اتصل</translation>
</message>
</context>
<context>
@@ -2113,80 +2113,96 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/>
<source>VPN by Amnezia</source>
<translation>VPN بواسطة Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/>
<source>Choose a VPN service that suits your needs.</source>
<translation>اختر خدمة VPN تلبي احتياجاتك</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PageSetupWizardConfigSource</name>
<message>
<source>Server connection</source>
<translation type="vanished">اتصال الخادم</translation>
</message>
<message>
<source>Do not use connection codes from untrusted sources, as they may be created to intercept your data.</source>
<translation type="vanished">لا تستخدم رموز اتصال من مصادر غير موثوقة, حيث قد يكون تم إنشاؤها لاعتراض بياناتك.</translation>
</message>
<message>
<source>What do you have?</source>
<translation type="vanished">ماذا لديك؟</translation>
</message>
<message>
<source>File with connection settings or backup</source>
<translation type="vanished">ملف إعدادات اتصال او نسخ احتياطي</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/>
<source>Connection</source>
<translation>الاتصال</translation>
<translation type="unfinished">الاتصال</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/>
<source>Insert the key, add a configuration file or scan the QR-code</source>
<translation>أدخل المفتاح، أضف ملف تكوين أو امسح رمز الاستجابة السريعة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/>
<source>Insert key</source>
<translation>أدخل مفتاح</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/>
<source>Insert</source>
<translation>أدخل</translation>
<translation type="unfinished">ادخل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/>
<source>Continue</source>
<translation>واصل</translation>
<translation type="unfinished">واصل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/>
<source>Other connection options</source>
<translation>اختيارات اتصال اخري</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
<source>VPN by Amnezia</source>
<translation>VPN بواسطة Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/>
<source>Connect to classic paid and free VPN services from Amnezia</source>
<translation>اتصل بخدمات VPN الكلاسيكية المدفوعة والمجانية من Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/>
<source>Self-hosted VPN</source>
<translation>VPN ذاتية الاستضافة</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/>
<source>Configure Amnezia VPN on your own server</source>
<translation>قم بتكوين Amnezia VPN على الخادم الخاص بك</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/>
<source>Restore from backup</source>
<translation>استرجاع من ملف يحتوي علي نسخة احتياطية</translation>
<translation type="unfinished">استرجاع من ملف يحتوي علي نسخة احتياطية</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/>
<source>Open backup file</source>
<translation>افتح ملف نسخ احتياطي</translation>
<translation type="unfinished">افتح ملف نسخ احتياطي</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/>
<source>Backup files (*.backup)</source>
<translation>ملفات نٌسخ احتياطية (*.backup)</translation>
<translation type="unfinished">ملفات نٌسخ احتياطية (*.backup)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="198"/>
@@ -2206,7 +2222,11 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/>
<source>I have nothing</source>
<translation>ليس لدي اي شئ</translation>
<translation type="unfinished">ليس لدي اي شئ</translation>
</message>
<message>
<source>Key as text</source>
<translation type="vanished">مفتاح كنص</translation>
</message>
</context>
<context>
@@ -2249,12 +2269,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
<source>How to run your VPN server</source>
<translation>كيف تقوم بتشغيل خادم ال VPN الخاص بك</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
<translation>اين تحصل علي بيانات الاتصال, تعليمات خطوة ب خطوة لشراء VPS</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
@@ -2372,7 +2392,7 @@ Already installed containers were found on the server. All installed containers
<translation>تثبيت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardProtocolSettings.qml" line="268"/>
<location filename="../ui/qml/Pages2/PageSetupWizardProtocolSettings.qml" line="267"/>
<source>The port must be in the range of 1 to 65535</source>
<translation>يجب أن يكون المنفذ في النطاق من 1 إلى 65535</translation>
</message>
@@ -2400,10 +2420,30 @@ Already installed containers were found on the server. All installed containers
</context>
<context>
<name>PageSetupWizardStart</name>
<message>
<source>Settings restored from backup file</source>
<translation type="vanished">تم استرداد الإعدادات من ملف نسخة احتياطية</translation>
</message>
<message>
<source>Free service for creating a personal VPN on your server.</source>
<translation type="vanished">خدمة مجانية لأنشاء VPN شخصي علي الخادم الشخصي.</translation>
</message>
<message>
<source> Helps you access blocked content without revealing your privacy, even to VPN providers.</source>
<translation type="vanished"> يساعدك في الولوج للمحتوي المحظور بدون إظهار خصوصيات, حتي لمزود ال VPN.</translation>
</message>
<message>
<source>I have the data to connect</source>
<translation type="vanished">لدي البيانات المطلوبة للأتصال</translation>
</message>
<message>
<source>I have nothing</source>
<translation type="vanished">ليس لدي اي شئ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/>
<source>Let&apos;s get started</source>
<translation>هيا نبدأ</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -2737,7 +2777,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageStart.qml" line="202"/>
<source>Settings restored from backup file</source>
<translation>تم تحميل الإعدادات من ملف نسخة احتياطية</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -3181,7 +3221,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../core/errorstrings.cpp" line="63"/>
<source>Missing AGW public key</source>
<translation>مفتاح AGW عام مفقود</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="66"/>
@@ -3259,7 +3299,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../containers/containers_defs.cpp" line="127"/>
<source>XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.</source>
<translation>XRay مع REALITY - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة.</translation>
<translation>الأشعة السينية مع الواقع - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="130"/>

View File

@@ -6,47 +6,47 @@
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="63"/>
<source>Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s</source>
<translation>برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیوها، از VPN کلاسیک استفاده کنید. این VPN برای هر سایتی کار میکند و سرعت آن تا %1 مگابیت بر ثانیه است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="67"/>
<source>VPN to access blocked sites in regions with high levels of Internet censorship. </source>
<translation>وی پی ان برای دسترسی به سایتهای مسدود شده در مناطق با سانسور شدید اینترنت.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="72"/>
<source>Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship.</source>
<translation>امنزیا پریمیوم - یک وی پی ان کلاسیک برای کار راحت، دانلود فایلهای بزرگ و تماشای ویدیو با کیفیت بالا. قابل استفاده برای تمامی سایتها، حتی در کشورهایی با بالاترین سطح سانسور اینترنت.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="75"/>
<source>Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship</source>
<translation>امنزیا رایگان یک وی پی ان رایگان برای دور زدن مسدودیتها در کشورهایی با سطح بالای سانسور اینترنت است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="80"/>
<source>%1 MBit/s</source>
<translation>%1 MBit/s</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="87"/>
<source>%1 days</source>
<translation>%1 روز</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="96"/>
<source>VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
<translation>وی پی ان فقط سایتهای محبوبی را که در منطقه شما مسدود شدهاند، مانند اینستاگرام، فیسبوک، توییتر و غیره باز میکند. سایر سایتها با آدرس آیپی واقعی شما باز خواهند شد. &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="104"/>
<source>Free</source>
<translation>رایگان</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/models/apiServicesModel.cpp" line="106"/>
<source>%1 $/month</source>
<translation>%1 $/ماه</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -54,22 +54,22 @@
<message>
<location filename="../ui/controllers/appSplitTunnelingController.cpp" line="23"/>
<source>Application added: %1</source>
<translation>برنامه اضافه شد: %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/appSplitTunnelingController.cpp" line="26"/>
<source>The application has already been added</source>
<translation>برنامه از قبل اضافه شده است</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/appSplitTunnelingController.cpp" line="37"/>
<source>The selected applications have been added</source>
<translation>برنامههای انتخاب شده اضافه شدند</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/appSplitTunnelingController.cpp" line="48"/>
<source>Application removed: %1</source>
<translation>برنامه حذف شد: %1</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -77,7 +77,7 @@
<message>
<location filename="../ui/qml/Components/ConnectButton.qml" line="27"/>
<source>Unable to disconnect during configuration preparation</source>
<translation>در هنگام آمادهسازی پیکربندی، نمیتوان از اتصال خارج شد.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -102,7 +102,7 @@
<message>
<location filename="../ui/controllers/connectionController.cpp" line="110"/>
<source>Preparing...</source>
<translation>در حال آمادهسازی...</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="132"/>
@@ -118,12 +118,12 @@
<message>
<location filename="../ui/controllers/connectionController.cpp" line="209"/>
<source>The selected protocol is not supported on the current platform</source>
<translation>پروتکل انتخابشده در پلتفرم فعلی پشتیبانی نمیشود.</translation>
<translation type="unfinished">پروتکل انتخاب شده بر روی این پلتفرم پشتیبانی نمیشود</translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="233"/>
<source>unable to create configuration</source>
<translation>نمیتوان پیکربندی را ایجاد کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/connectionController.cpp" line="95"/>
@@ -256,13 +256,13 @@ Can&apos;t be disabled for current server</source>
<message>
<location filename="../ui/controllers/importController.cpp" line="87"/>
<source>Unable to open file</source>
<translation>نمیتوان فایل را باز کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/importController.cpp" line="186"/>
<location filename="../ui/controllers/importController.cpp" line="191"/>
<source>Invalid configuration file</source>
<translation>فایل پیکربندی نامعتبر است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/importController.cpp" line="606"/>
@@ -272,7 +272,7 @@ Can&apos;t be disabled for current server</source>
<message>
<location filename="../ui/controllers/importController.cpp" line="641"/>
<source>In the imported configuration, potentially dangerous lines were found:</source>
<translation>در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند:</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -329,12 +329,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/controllers/installController.cpp" line="608"/>
<source>Api config removed</source>
<translation>پیکربندی API حذف شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="630"/>
<source>%1 cached profile cleared</source>
<translation>%1 پروفایل ذخیره شده پاک شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="769"/>
@@ -349,17 +349,17 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/controllers/installController.cpp" line="845"/>
<source>%1 installed successfully.</source>
<translation>%1 با موفقیت نصب شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="877"/>
<source>API config reloaded</source>
<translation>پیکربندی API دوباره بارگذاری شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/controllers/installController.cpp" line="881"/>
<source>Successfully changed the country of connection to %1</source>
<translation>کشور اتصال با موفقیت به %1 تغییر یافت.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -367,17 +367,17 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Components/InstalledAppsDrawer.qml" line="57"/>
<source>Choose application</source>
<translation>انتخاب برنامه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Components/InstalledAppsDrawer.qml" line="139"/>
<source>application name</source>
<translation>نام برنامه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Components/InstalledAppsDrawer.qml" line="152"/>
<source>Add selected</source>
<translation>اضافه کردن انتخاب شده</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -453,7 +453,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="67"/>
<source>Logging enabled</source>
<translation>لاگبرداری فعال شد</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageHome.qml" line="111"/>
@@ -589,7 +589,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="382"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -623,7 +623,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolCloakSettings.qml" line="193"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -794,7 +794,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="445"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove OpenVPN</source>
@@ -895,7 +895,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolShadowSocksSettings.qml" line="181"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -903,22 +903,22 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardSettings.qml" line="94"/>
<source>WG settings</source>
<translation>تنظیمات WG</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardSettings.qml" line="102"/>
<source>Port</source>
<translation>پورت</translation>
<translation type="unfinished">پورت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardSettings.qml" line="123"/>
<source>MTU</source>
<translation></translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardSettings.qml" line="157"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>All users with whom you shared a connection will no longer be able to connect to it.</source>
@@ -939,22 +939,22 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolXraySettings.qml" line="95"/>
<source>XRay settings</source>
<translation>تنظیمات XRay</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolXraySettings.qml" line="103"/>
<source>Disguised as traffic from</source>
<translation>بهعنوان ترافیک از طرف زیر نمایش داده میشود</translation>
<translation type="unfinished">پنهان کردن به عنوان ترافیک از</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolXraySettings.qml" line="129"/>
<source>Save</source>
<translation>ذخیره</translation>
<translation type="unfinished">ذخیره</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolXraySettings.qml" line="137"/>
<source>Unable change settings while there is an active connection</source>
<translation>نمیتوان تنظیمات را تغییر داد در حالی که اتصال فعال است.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1001,7 +1001,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageServiceDnsSettings.qml" line="85"/>
<source>Cannot remove AmneziaDNS from running server</source>
<translation>نمیتوان AmneziaDNS را از سرور در حال اجرا حذف کرد.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1093,18 +1093,18 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="26"/>
<source>Settings updated successfully</source>
<translation>تنظیمات با موفقیت بهروزرسانی شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="93"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="250"/>
<source>SOCKS5 settings</source>
<translation>تنظیمات SOCKS5</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="104"/>
<source>Host</source>
<translation>هاستمیزبان</translation>
<translation type="unfinished">هاست</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="114"/>
@@ -1112,50 +1112,50 @@ Already installed containers were found on the server. All installed containers
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="162"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="189"/>
<source>Copied</source>
<translation>کپی شد</translation>
<translation type="unfinished">کپی شد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="125"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="260"/>
<source>Port</source>
<translation>پورت</translation>
<translation type="unfinished">پورت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="149"/>
<source>User name</source>
<translation>نام کاربری</translation>
<translation type="unfinished">نام کاربری</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="173"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="306"/>
<source>Password</source>
<translation>رمز عبور</translation>
<translation type="unfinished">رمز عبور</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="282"/>
<source>Username</source>
<translation>نام کاربری</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="336"/>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="374"/>
<source>Change connection settings</source>
<translation>تغییر تنظیمات اتصال</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="343"/>
<source>The port must be in the range of 1 to 65535</source>
<translation>پورت باید در محدوده ۱ تا ۶۵۵۳۵ باشد</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="347"/>
<source>Password cannot be empty</source>
<translation>رمز عبور نمیتواند خالی باشد</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageServiceSocksProxySettings.qml" line="350"/>
<source>Username cannot be empty</source>
<translation>نام کاربری نمیتواند خالی باشد</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1285,7 +1285,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="113"/>
<source>https://t.me/amnezia_vpn_en</source>
<translation>https://t.me/amnezia_vpn_ir</translation>
<translation>https://t.me/amnezia_vpn</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="123"/>
@@ -1337,22 +1337,22 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>For the region</source>
<translation>برای منطقه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="54"/>
<source>Price</source>
<translation>قیمت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="63"/>
<source>Work period</source>
<translation>مدت زمان کار</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="74"/>
<source>Speed</source>
<translation>سرعت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="106"/>
@@ -1362,49 +1362,49 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="119"/>
<source>Copied</source>
<translation>کپی شد</translation>
<translation type="unfinished">کپی شد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="139"/>
<source>Reload API config</source>
<translation>بارگذاری مجدد پیکربندی API</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="144"/>
<source>Reload API config?</source>
<translation>آیا میخواهید پیکربندی API را دوباره بارگذاری کنید؟</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="145"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="185"/>
<source>Continue</source>
<translation>ادامه دهید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="146"/>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Cancel</source>
<translation>لغو</translation>
<translation type="unfinished">کنسل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="150"/>
<source>Cannot reload API config during active connection</source>
<translation>نمیتوان پیکربندی API را در حین اتصال فعال دوباره بارگذاری کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="179"/>
<source>Remove from application</source>
<translation>حذف از برنامه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="184"/>
<source>Remove from application?</source>
<translation>آیا میخواهید از برنامه حذف کنید؟</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Cannot remove server during active connection</source>
<translation>نمیتوان سرور را در حین اتصال فعال حذف کرد.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1412,57 +1412,57 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="30"/>
<source>Cannot change split tunneling settings during active connection</source>
<translation>نمی توان تنظیمات تونل تقسیم را در طول اتصال فعال تغییر دادنمیتوان تنظیمات تقسیم تونلینگ را در حین اتصال فعال تغییر داد.</translation>
<translation type="unfinished">نمی توان تنظیمات تونل تقسیم را در طول اتصال فعال تغییر داد</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="51"/>
<source>Only the apps from the list should have access via VPN</source>
<translation>فقط برنامههای موجود در لیست باید از طریق VPN دسترسی داشته باشند.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="56"/>
<source>Apps from the list should not have access via VPN</source>
<translation>برنامههای موجود در لیست نباید از طریق VPN دسترسی داشته باشند.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="93"/>
<source>App split tunneling</source>
<translation>تقسیم تونلینگ برنامهها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="129"/>
<source>Mode</source>
<translation>حالت</translation>
<translation type="unfinished">حالت</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="221"/>
<source>Remove </source>
<translation>حذف </translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="222"/>
<source>Continue</source>
<translation>ادامه دهید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="223"/>
<source>Cancel</source>
<translation>کنسل</translation>
<translation type="unfinished">کنسل</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="267"/>
<source>application name</source>
<translation>نام برنامه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="278"/>
<source>Open executable file</source>
<translation>فایل اجرایی را باز کنید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="279"/>
<source>Executable files (*.*)</source>
<translation>فایلهای اجرایی (*.*)</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1480,12 +1480,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="94"/>
<source>Enable notifications</source>
<translation>فعال کردن اعلانها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="95"/>
<source>Enable notifications to show the VPN state in the status bar</source>
<translation>اعلان ها را فعال کنید تا وضعیت VPN را در نوار وضعیت ببینید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="117"/>
@@ -1565,7 +1565,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="240"/>
<source>Cannot reset settings during active connection</source>
<translation>نمیتوان تنظیمات را در حین اتصال فعال بازنشانی کرد.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1644,7 +1644,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="153"/>
<source>Cannot restore backup settings during active connection</source>
<translation>نمیتوان تنظیمات پشتیبان را در حین اتصال فعال بازیابی کرد.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1682,17 +1682,17 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="154"/>
<source>KillSwitch</source>
<translation>KillSwitch</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="155"/>
<source>Disables your internet if your encrypted VPN connection drops out for any reason.</source>
<translation>اگر به هر دلیلی اتصال VPN رمزگذاری شده شما قطع شود، اینترنت شما را غیرفعال میکند.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="166"/>
<source>Cannot change killSwitch settings during active connection</source>
<translation>نمیتوان تنظیمات Kill Switch را در حین اتصال فعال تغییر داد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="97"/>
@@ -1778,7 +1778,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="24"/>
<source>Logging is enabled. Note that logs will be automatically disabled after 14 days, and all log files will be deleted.</source>
<translation>ثبت وقایع فعال است. توجه داشته باشید که ثبت وقایع بهطور خودکار پس از ۱۴ روز غیرفعال شده و تمام فایلهای ثبت وقایع حذف خواهند شد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="69"/>
@@ -1919,7 +1919,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="134"/>
<source>Cannot reboot server during active connection</source>
<translation>نمیتوان سرور را در حین اتصال فعال راهاندازی مجدد کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="176"/>
@@ -1929,7 +1929,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="183"/>
<source>Cannot remove server during active connection</source>
<translation>نمیتوان سرور را در حین اتصال فعال حذف کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="218"/>
@@ -1944,7 +1944,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="225"/>
<source>Cannot clear server from Amnezia software during active connection</source>
<translation>نمیتوان سرور را در حین اتصال فعال از نرمافزار Amnezia پاک کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="253"/>
@@ -1959,7 +1959,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="266"/>
<source>Cannot reset API config during active connection</source>
<translation>نمیتوان پیکربندی API را در حین اتصال فعال بازنشانی کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="162"/>
@@ -2015,12 +2015,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="135"/>
<source>Clear %1 profile</source>
<translation>پاک کردن پروفایل %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="138"/>
<source>Clear %1 profile?</source>
<translation>آیا میخواهید پروفایل %1 را پاک کنید؟</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="137"/>
@@ -2030,7 +2030,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="145"/>
<source>Unable to clear %1 profile while there is an active connection</source>
<translation>نمیتوان پروفایل %1 را در حین اتصال فعال پاک کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="186"/>
@@ -2050,7 +2050,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="198"/>
<source>Cannot remove active container</source>
<translation>نمیتوان کانتینر فعال را حذف کرد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="140"/>
@@ -2179,32 +2179,32 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="62"/>
<source>For the region</source>
<translation>برای منطقه</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="71"/>
<source>Price</source>
<translation>قیمت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="80"/>
<source>Work period</source>
<translation>مدت زمان کار</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="91"/>
<source>Speed</source>
<translation>سرعت</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="100"/>
<source>Features</source>
<translation>ویژگیها</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="139"/>
<source>Connect</source>
<translation>اتصال</translation>
<translation type="unfinished">اتصال</translation>
</message>
</context>
<context>
@@ -2212,12 +2212,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="52"/>
<source>VPN by Amnezia</source>
<translation>VPN توسط Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardApiServicesList.qml" line="53"/>
<source>Choose a VPN service that suits your needs.</source>
<translation>یک سرویس VPN که مناسب نیازهای شما باشد را انتخاب کنید.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -2254,67 +2254,67 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="57"/>
<source>Connection</source>
<translation>ارتباط</translation>
<translation type="unfinished">ارتباط</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="67"/>
<source>Insert the key, add a configuration file or scan the QR-code</source>
<translation>کلید را وارد کنید، فایل پیکربندی را اضافه کنید یا کد QR را اسکن کنید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="77"/>
<source>Insert key</source>
<translation>کلید را وارد کنید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="78"/>
<source>Insert</source>
<translation>وارد کردن</translation>
<translation type="unfinished">وارد کردن</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="98"/>
<source>Continue</source>
<translation>ادامه دهید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="116"/>
<source>Other connection options</source>
<translation>گزینههای اتصال دیگر</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
<source>VPN by Amnezia</source>
<translation>VPN توسط Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/>
<source>Connect to classic paid and free VPN services from Amnezia</source>
<translation>اتصال به سرویسهای VPN کلاسیک پولی و رایگان از Amnezia</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="153"/>
<source>Self-hosted VPN</source>
<translation>Self-hosted VPN</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="154"/>
<source>Configure Amnezia VPN on your own server</source>
<translation>پیکربندی VPN Amnezia بر روی سرور خودتان</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="174"/>
<source>Restore from backup</source>
<translation>بازیابی از پشتیبان</translation>
<translation type="unfinished">بازیابی از پشتیبان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/>
<source>Open backup file</source>
<translation>باز کردن فایل پشتیبان</translation>
<translation type="unfinished">باز کردن فایل پشتیبان</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="181"/>
<source>Backup files (*.backup)</source>
<translation>Backup files (*.backup)</translation>
<translation type="unfinished">Backup files (*.backup)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="206"/>
@@ -2329,7 +2329,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="248"/>
<source>I have nothing</source>
<translation>من هیچی ندارم</translation>
<translation type="unfinished">من هیچی ندارم</translation>
</message>
<message>
<source>Key as text</source>
@@ -2381,12 +2381,12 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
<source>How to run your VPN server</source>
<translation>چگونه سرور VPN خود را اجرا کنید</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
<translation>دادههای اتصال را از کجا دریافت کنید و دستورالعملهای مرحله به مرحله برای خرید یک VPS</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
@@ -2501,7 +2501,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardProtocolSettings.qml" line="267"/>
<source>The port must be in the range of 1 to 65535</source>
<translation>پورت باید در محدوده ۱ تا ۶۵۵۳۵ باشد</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -2550,7 +2550,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardStart.qml" line="48"/>
<source>Let&apos;s get started</source>
<translation>بیایید شروع کنیم</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -2601,7 +2601,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="123"/>
<source>Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.</source>
<translation>فعالسازی استتار WireGuard. این ممکن است مفید باشد اگر WireGuard توسط ارائهدهنده شما مسدود شده باشد.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardViewConfig.qml" line="144"/>
@@ -2680,7 +2680,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="96"/>
<source>Save XRay config</source>
<translation>ذخیره پیکربندی XRay</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="122"/>
@@ -2705,7 +2705,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="152"/>
<source>XRay native format</source>
<translation>فرمت بومی XRay</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="190"/>
@@ -2741,22 +2741,22 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="817"/>
<source>Creation date: %1</source>
<translation>تاریخ ایجاد: %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="825"/>
<source>Latest handshake: %1</source>
<translation>آخرین ارتباط: %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="833"/>
<source>Data received: %1</source>
<translation>دادههای دریافت شده: %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShare.qml" line="841"/>
<source>Data sent: %1</source>
<translation>دادههای ارسال شده: %1</translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>Creation date: </source>
@@ -2882,12 +2882,12 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../ui/qml/Pages2/PageStart.qml" line="198"/>
<source>Logging was disabled after 14 days, log files were deleted</source>
<translation>ثبت وقایع پس از ۱۴ روز غیرفعال شد و فایلهای ثبت وقایع حذف شدند</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageStart.qml" line="202"/>
<source>Settings restored from backup file</source>
<translation>تنظیمات از فایل پشتیبان بازیابی شد</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -3296,17 +3296,17 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../core/errorstrings.cpp" line="14"/>
<source>Background service is not running</source>
<translation>Background service is not running</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation>Server error: Packet manager error</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="34"/>
<source>SCP error: Generic failure</source>
<translation>SCP error: Generic failure</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="38"/>
@@ -3356,17 +3356,17 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../core/errorstrings.cpp" line="60"/>
<source>In the response from the server, an empty config was received</source>
<translation>در پاسخ از سرور، پیکربندی خالی دریافت شد</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="61"/>
<source>SSL error occurred</source>
<translation type="unfinished">SSL error occurred</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="62"/>
<source>Server response timeout on api request</source>
<translation>Server response timeout on api request</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="63"/>
@@ -3426,12 +3426,12 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<message>
<location filename="../containers/containers_defs.cpp" line="127"/>
<source>XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.</source>
<translation>XRay با REALITY - مناسب برای کشورهایی با بالاترین سطح سانسور اینترنت. استتار ترافیک به عنوان ترافیک وب در سطح TLS و حفاظت در برابر شناسایی با روشهای پروب فعال.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="130"/>
<source>IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS.</source>
<translation>IKEv2/IPsec - پروتکل مدرن و پایدار، کمی سریعتر از سایرین است و پس از قطع شدن سیگنال، اتصال را بازیابی میکند. از پشتیبانی بومی در آخرین نسخههای Android و iOS برخوردار است.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="137"/>
@@ -3504,11 +3504,7 @@ WireGuard به دلیل امضاهای بسته متمایز خود، بسیار
It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data.
This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations.
Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY&apos;s innovative &quot;friend or foe&quot; recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.</source>
<translation>پروتکل REALITY، یک توسعه پیشگامانه توسط خالقان XRay، بهطور خاص برای مقابله با بالاترین سطح سانسور اینترنتی طراحی شده است و از رویکرد نوآورانهای برای دور زدن محدودیتها استفاده میکند.
REALITY بهطور منحصربهفردی سانسورچیان را در مرحله دستدهی TLS شناسایی میکند و بهصورت یکپارچه بهعنوان پراکسی برای کاربران قانونی عمل میکند، در حالی که سانسورچیان را به سایتهای معتبر مانند google.com هدایت میکند و در نتیجه یک گواهی TLS واقعی و دادههای اصلی ارائه میدهد.
این قابلیت پیشرفته، REALITY را از فناوریهای مشابه متمایز میکند، زیرا میتواند ترافیک وب را بدون نیاز به پیکربندیهای خاص، بهعنوان ترافیک از سایتهای تصادفی و معتبر جا بزند. برخلاف پروتکلهای قدیمیتر مانند VMess، VLESS و انتقال XTLS-Vision، تشخیص نوآورانه &quot;دوست یا دشمن&quot; REALITY در مرحله دستدهی TLS امنیت را افزایش داده و از شناسایی توسط سیستمهای پیشرفته DPI که از تکنیکهای پروب فعال استفاده میکنند، جلوگیری میکند. این ویژگی REALITY را به یک راهحل قوی برای حفظ آزادی اینترنت در محیطهایی با سانسور شدید تبدیل میکند.</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="230"/>
@@ -3669,7 +3665,7 @@ For more detailed information, you can
<location filename="../containers/containers_defs.cpp" line="104"/>
<location filename="../containers/containers_defs.cpp" line="248"/>
<source>SOCKS5 proxy server</source>
<translation>سرور پروکسی SOCKS5</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../3rd/qtkeychain/qtkeychain/libsecret.cpp" line="119"/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -44,13 +44,6 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
const auto isGoodbyeDpi = serverConfig.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
emit startLocalService();
return;
}
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
@@ -72,15 +65,6 @@ void ConnectionController::openConnection()
void ConnectionController::closeConnection()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
const auto isGoodbyeDpi = serverConfig.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
emit stopLocalService();
return;
}
emit disconnectFromVpn();
}

View File

@@ -61,9 +61,6 @@ signals:
void updateApiConfigFromTelegram();
void configFromApiUpdated();
void startLocalService();
void stopLocalService();
private:
Vpn::ConnectionState getCurrentConnectionState();
bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container);

View File

@@ -10,6 +10,9 @@
#include "core/controllers/vpnConfigurationController.h"
#include "systemController.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/android_utils.h"
#endif
#include "qrcodegen.hpp"
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
@@ -21,6 +24,12 @@ ExportController::ExportController(const QSharedPointer<ServersModel> &serversMo
m_clientManagementModel(clientManagementModel),
m_settings(settings)
{
#ifdef Q_OS_ANDROID
m_authResultNotifier.reset(new AuthResultNotifier);
m_authResultReceiver.reset(new AuthResultReceiver(m_authResultNotifier));
connect(m_authResultNotifier.get(), &AuthResultNotifier::authFailed, this, [this]() { emit exportErrorOccurred(tr("Access error!")); });
connect(m_authResultNotifier.get(), &AuthResultNotifier::authSuccessful, this, &ExportController::generateFullAccessConfig);
#endif
}
void ExportController::generateFullAccessConfig()
@@ -54,6 +63,26 @@ void ExportController::generateFullAccessConfig()
emit exportConfigChanged();
}
#if defined(Q_OS_ANDROID)
void ExportController::generateFullAccessConfigAndroid()
{
/* We use builtin keyguard for ssh key export protection on Android */
QJniObject activity = AndroidUtils::getActivity();
auto appContext = activity.callObjectMethod("getApplicationContext", "()Landroid/content/Context;");
if (appContext.isValid()) {
auto intent = QJniObject::callStaticObjectMethod("org/amnezia/vpn/AuthHelper", "getAuthIntent",
"(Landroid/content/Context;)Landroid/content/Intent;", appContext.object());
if (intent.isValid()) {
if (intent.object<jobject>() != nullptr) {
QtAndroidPrivate::startActivity(intent.object<jobject>(), 1, m_authResultReceiver.get());
}
} else {
generateFullAccessConfig();
}
}
}
#endif
void ExportController::generateConnectionConfig(const QString &clientName)
{
clearPreviousConfig();

View File

@@ -6,6 +6,9 @@
#include "ui/models/clientManagementModel.h"
#include "ui/models/containers_model.h"
#include "ui/models/servers_model.h"
#ifdef Q_OS_ANDROID
#include "platforms/android/authResultReceiver.h"
#endif
class ExportController : public QObject
{
@@ -22,6 +25,9 @@ public:
public slots:
void generateFullAccessConfig();
#if defined(Q_OS_ANDROID)
void generateFullAccessConfigAndroid();
#endif
void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName);
@@ -68,6 +74,11 @@ private:
QString m_config;
QString m_nativeConfigString;
QList<QString> m_qrCodes;
#ifdef Q_OS_ANDROID
QSharedPointer<AuthResultNotifier> m_authResultNotifier;
QSharedPointer<QAndroidActivityResultReceiver> m_authResultReceiver;
#endif
};
#endif // EXPORTCONTROLLER_H

View File

@@ -799,7 +799,7 @@ void InstallController::addEmptyServer()
bool InstallController::fillAvailableServices()
{
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
ApiController apiController(m_settings->getGatewayEndpoint());
QByteArray responseBody;
ErrorCode errorCode = apiController.getServicesList(responseBody);
@@ -821,7 +821,7 @@ bool InstallController::installServiceFromApi()
return false;
}
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
ApiController apiController(m_settings->getGatewayEndpoint());
QJsonObject serverConfig;
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
@@ -849,7 +849,7 @@ bool InstallController::installServiceFromApi()
bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
ApiController apiController(m_settings->getGatewayEndpoint());
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
@@ -885,7 +885,7 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
void InstallController::updateServiceFromTelegram(const int serverIndex)
{
ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint());
auto serverConfig = m_serversModel->getServerConfig(serverIndex);

View File

@@ -1,99 +0,0 @@
#include "localServicesController.h"
namespace
{
// Logger logger("ServerController");
}
LocalServicesController::LocalServicesController(const QSharedPointer<ServersModel> &serversModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
{
#ifdef Q_OS_WINDOWS
connect(&m_goodbyeDpiService, &GoodByeDpi::serviceStateChanged, this, &LocalServicesController::serviceStateChanged);
#endif
}
LocalServicesController::~LocalServicesController()
{
#ifdef Q_OS_WINDOWS
m_goodbyeDpiService.stop();
#endif
}
void LocalServicesController::toggleGoodbyeDpi(bool enable)
{
if (enable) {
QJsonObject server;
server.insert(config_key::isGoodbyeDpi, true);
server.insert(config_key::description, "GoodbyeDPI service");
server.insert(config_key::name, "GoodbyeDPI");
m_serversModel->addServer(server);
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
m_settings->toggleGoodbyeDpi(true);
emit toggleGoodbyeDpiFinished(tr("GoodbyeDPI added to home page"));
} else {
for (int i = 0; i < m_serversModel->getServersCount(); i++) {
if (m_serversModel->getServerConfig(i).value(config_key::isGoodbyeDpi).toBool(false)) {
m_serversModel->setProcessedServerIndex(i);
m_serversModel->removeServer();
break;
}
}
m_settings->toggleGoodbyeDpi(false);
emit toggleGoodbyeDpiFinished("GoodbyeDPI removed from home page");
}
}
bool LocalServicesController::isGoodbyeDpiEnabled()
{
return m_settings->isGoodbyeDpiEnabled();
}
void LocalServicesController::setGoodbyeDpiBlackListFile(const QString &file)
{
m_settings->setGoodbyeDpiBlackListFile(file);
}
QString LocalServicesController::getGoodbyeDpiBlackListFile()
{
auto file = m_settings->getGoodbyeDpiBlackListFile();
if (file.isEmpty()) {
return m_defaultBlackListFile;
}
return file;
}
void LocalServicesController::resetGoodbyeDpiBlackListFile()
{
m_settings->setGoodbyeDpiBlackListFile(m_defaultBlackListFile);
}
void LocalServicesController::setGoodbyeDpiModset(const int modset)
{
m_settings->setGoodbyeDpiModset(modset);
}
int LocalServicesController::getGoodbyeDpiModset()
{
return m_settings->getGoodbyeDpiModset();
}
void LocalServicesController::start()
{
#ifdef Q_OS_WINDOWS
auto errorCode = m_goodbyeDpiService.start(getGoodbyeDpiBlackListFile(), getGoodbyeDpiModset());
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
}
#endif
}
void LocalServicesController::stop()
{
#ifdef Q_OS_WINDOWS
m_goodbyeDpiService.stop();
#endif
}

View File

@@ -1,58 +0,0 @@
#ifndef LOCALSERVICESCONTROLLER_H
#define LOCALSERVICESCONTROLLER_H
#include <QObject>
#ifdef Q_OS_WINDOWS
#include "localServices/goodByeDpi.h"
#endif
#include "protocols/vpnprotocol.h"
#include "settings.h"
#include "ui/models/servers_model.h"
class LocalServicesController : public QObject
{
Q_OBJECT
public:
LocalServicesController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
~LocalServicesController();
Q_PROPERTY(bool isGoodbyeDpiEnabled READ isGoodbyeDpiEnabled NOTIFY toggleGoodbyeDpiFinished)
public slots:
void toggleGoodbyeDpi(bool enable);
bool isGoodbyeDpiEnabled();
void setGoodbyeDpiBlackListFile(const QString &file);
QString getGoodbyeDpiBlackListFile();
void resetGoodbyeDpiBlackListFile();
void setGoodbyeDpiModset(const int modset);
int getGoodbyeDpiModset();
void start();
void stop();
signals:
void errorOccurred(ErrorCode errorCode);
void toggleGoodbyeDpiFinished(const QString &message);
void serviceStateChanged(Vpn::ConnectionState state);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServersModel> m_serversModel;
#ifdef Q_OS_WINDOWS
GoodByeDpi m_goodbyeDpiService;
#endif
bool m_isGoodbyeDpiServiceEnabled = false;
#ifdef Q_OS_WINDOWS
QString m_defaultBlackListFile = QCoreApplication::applicationDirPath() + "/goodbyedpi/blacklist.txt";
#else
QString m_defaultBlackListFile;
#endif
};
#endif // LOCALSERVICESCONTROLLER_H

View File

@@ -10,6 +10,8 @@
#ifdef Q_OS_ANDROID
#include "platforms/android/android_controller.h"
#include "platforms/android/android_utils.h"
#include <QJniObject>
#endif
#if defined Q_OS_MAC
#include "ui/macos_util.h"
@@ -20,8 +22,18 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
{
#ifdef Q_OS_ANDROID
// Change color of navigation and status bar's
auto initialPageNavigationBarColor = getInitialPageNavigationBarColor();
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
AndroidUtils::runOnAndroidThreadSync([&initialPageNavigationBarColor]() {
QJniObject activity = AndroidUtils::getActivity();
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) {
window.callMethod<void>("addFlags", "(I)V", 0x80000000);
window.callMethod<void>("clearFlags", "(I)V", 0x04000000);
window.callMethod<void>("setStatusBarColor", "(I)V", 0xFF0E0E11);
window.callMethod<void>("setNavigationBarColor", "(I)V", initialPageNavigationBarColor);
}
});
#endif
#if defined Q_OS_MACX
@@ -47,19 +59,11 @@ bool PageController::isStartPageVisible()
}
}
QString PageController::getPagePath(PageLoader::PageEnum page, PageLoader::FolderEnum folder)
QString PageController::getPagePath(PageLoader::PageEnum page)
{
QMetaEnum metaEnum = QMetaEnum::fromType<PageLoader::PageEnum>();
QString pageName = metaEnum.valueToKey(static_cast<int>(page));
metaEnum = QMetaEnum::fromType<PageLoader::FolderEnum>();
QString folderName = "";
if (metaEnum.value(static_cast<int>(folder)) != static_cast<int>(PageLoader::FolderEnum::Root)) {
folderName = metaEnum.valueToKey(static_cast<int>(folder));
folderName += "/";
}
return "qrc:/ui/qml/Pages2/" + folderName + pageName + ".qml";
return "qrc:/ui/qml/Pages2/" + pageName + ".qml";
}
void PageController::closeWindow()
@@ -111,7 +115,14 @@ unsigned int PageController::getInitialPageNavigationBarColor()
void PageController::updateNavigationBarColor(const int color)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->setNavigationBarColor(color);
// Change color of navigation bar
AndroidUtils::runOnAndroidThreadSync([&color]() {
QJniObject activity = AndroidUtils::getActivity();
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) {
window.callMethod<void>("setNavigationBarColor", "(I)V", color);
}
});
#endif
}
@@ -120,7 +131,7 @@ void PageController::showOnStartup()
if (!m_settings->isStartMinimized()) {
emit raiseMainWindow();
} else {
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
#ifdef Q_OS_WIN
emit hideMainWindow();
#elif defined Q_OS_MACX
setDockIconVisible(false);

View File

@@ -61,9 +61,7 @@ namespace PageLoader
PageShareFullAccess,
PageDevMenu,
PageGoodByeDpiSettings
PageDevMenu
};
Q_ENUM_NS(PageEnum)
@@ -71,19 +69,6 @@ namespace PageLoader
{
qmlRegisterUncreatableMetaObject(PageLoader::staticMetaObject, "PageEnum", 1, 0, "PageEnum", "Error: only enums");
}
Q_NAMESPACE
enum class FolderEnum {
Root = 0,
LocalServices
};
Q_ENUM_NS(FolderEnum)
static void declareQmlFolderEnum()
{
qmlRegisterUncreatableMetaObject(PageLoader::staticMetaObject, "FolderEnum", 1, 0, "FolderEnum", "Error: only enums");
}
}
class PageController : public QObject
@@ -95,7 +80,7 @@ public:
public slots:
bool isStartPageVisible();
QString getPagePath(PageLoader::PageEnum page, PageLoader::FolderEnum folder = PageLoader::FolderEnum::Root);
QString getPagePath(PageLoader::PageEnum page);
void closeWindow();
void hideWindow();
@@ -118,7 +103,7 @@ public slots:
void onShowErrorMessage(amnezia::ErrorCode errorCode);
signals:
void goToPage(PageLoader::PageEnum page, PageLoader::FolderEnum folder = PageLoader::FolderEnum::Root, bool slide = true);
void goToPage(PageLoader::PageEnum page, bool slide = true);
void goToStartPage();
void goToPageHome();
void goToPageSettings();

View File

@@ -16,7 +16,8 @@
SettingsController::SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel, const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
@@ -30,8 +31,7 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
checkIfNeedDisableLogs();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this,
&SettingsController::onNotificationStateChanged);
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsController::onNotificationStateChanged);
#endif
}
@@ -88,12 +88,7 @@ void SettingsController::toggleLogging(bool enable)
void SettingsController::openLogsFolder()
{
Logger::openLogsFolder(false);
}
void SettingsController::openServiceLogsFolder()
{
Logger::openLogsFolder(true);
Logger::openLogsFolder();
}
void SettingsController::exportLogsFile(const QString &fileName)
@@ -105,21 +100,12 @@ void SettingsController::exportLogsFile(const QString &fileName)
#endif
}
void SettingsController::exportServiceLogsFile(const QString &fileName)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->exportLogsFile(fileName);
#else
SystemController::saveFile(fileName, Logger::getServiceLogFile());
#endif
}
void SettingsController::clearLogs()
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->clearLogs();
#else
Logger::clearLogs(false);
Logger::clearLogs();
Logger::clearServiceLogs();
#endif
}
@@ -145,7 +131,8 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
bool ok = m_settings->restoreAppConfig(data);
if (ok) {
m_serversModel->resetModel();
m_languageModel->changeLanguage(static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
emit restoreBackupFinished();
} else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@@ -161,7 +148,8 @@ void SettingsController::clearSettings()
{
m_settings->clearSettings();
m_serversModel->resetModel();
m_languageModel->changeLanguage(static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
m_sitesModel->toggleSplitTunneling(false);
@@ -295,31 +283,5 @@ void SettingsController::setGatewayEndpoint(const QString &endpoint)
QString SettingsController::getGatewayEndpoint()
{
return m_settings->isDevGatewayEnv() ? "Dev endpoint" : m_settings->getGatewayEndpoint();
return m_settings->getGatewayEndpoint();
}
bool SettingsController::isDevGatewayEnv()
{
return m_settings->isDevGatewayEnv();
}
void SettingsController::toggleDevGatewayEnv(bool enabled)
{
m_settings->toggleDevGatewayEnv(enabled);
if (enabled) {
m_settings->setDevGatewayEndpoint();
} else {
m_settings->resetGatewayEndpoint();
}
emit gatewayEndpointChanged(m_settings->getGatewayEndpoint());
emit devGatewayEnvChanged(enabled);
}
bool SettingsController::isOnTv()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isOnTv();
#else
return false;
#endif
}

View File

@@ -27,7 +27,6 @@ public:
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
Q_PROPERTY(bool isDevGatewayEnv READ isDevGatewayEnv WRITE toggleDevGatewayEnv NOTIFY devGatewayEnvChanged)
public slots:
void toggleAmneziaDns(bool enable);
@@ -43,9 +42,7 @@ public slots:
void toggleLogging(bool enable);
void openLogsFolder();
void openServiceLogsFolder();
void exportLogsFile(const QString &fileName);
void exportServiceLogsFile(const QString &fileName);
void clearLogs();
void backupAppConfig(const QString &fileName);
@@ -84,10 +81,6 @@ public slots:
void resetGatewayEndpoint();
void setGatewayEndpoint(const QString &endpoint);
QString getGatewayEndpoint();
bool isDevGatewayEnv();
void toggleDevGatewayEnv(bool enabled);
bool isOnTv();
signals:
void primaryDnsChanged();
@@ -110,7 +103,6 @@ signals:
void devModeEnabled();
void gatewayEndpointChanged(const QString &endpoint);
void devGatewayEnvChanged(bool enabled);
private:
QSharedPointer<ServersModel> m_serversModel;

View File

@@ -125,12 +125,3 @@ void SystemController::setQmlRoot(QObject *qmlRoot)
{
m_qmlRoot = qmlRoot;
}
bool SystemController::isAuthenticated()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->requestAuthentication();
#else
return true;
#endif
}

View File

@@ -19,7 +19,6 @@ public slots:
void setQmlRoot(QObject *qmlRoot);
bool isAuthenticated();
signals:
void fileDialogClosed(const bool isAccepted);

View File

@@ -25,8 +25,6 @@ namespace
constexpr char availableCountries[] = "available_countries";
constexpr char storeEndpoint[] = "store_endpoint";
constexpr char isAvailable[] = "is_available";
}
namespace serviceType
@@ -65,12 +63,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
return tr("Classic VPN for comfortable work, downloading large files and watching videos. "
"Works for any sites. Speed up to %1 MBit/s")
.arg(speed);
} else if (serviceType == serviceType::amneziaFree){
QString description = tr("VPN to access blocked sites in regions with high levels of Internet censorship. ");
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a>");
}
return description;
} else {
return tr("VPN to access blocked sites in regions with high levels of Internet censorship. ");
}
}
case ServiceDescriptionRole: {
@@ -81,14 +75,6 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
return tr("Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship");
}
}
case IsServiceAvailableRole: {
if (serviceType == serviceType::amneziaFree) {
if (service.value(configKey::isAvailable).isBool() && !service.value(configKey::isAvailable).toBool()) {
return false;
}
}
return true;
}
case SpeedRole: {
auto speed = serviceInfo.value(configKey::speed).toString();
return tr("%1 MBit/s").arg(speed);
@@ -207,7 +193,6 @@ QHash<int, QByteArray> ApiServicesModel::roleNames() const
roles[NameRole] = "name";
roles[CardDescriptionRole] = "cardDescription";
roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsServiceAvailableRole] = "isServiceAvailable";
roles[SpeedRole] = "speed";
roles[WorkPeriodRole] = "workPeriod";
roles[RegionRole] = "region";

View File

@@ -13,7 +13,6 @@ public:
NameRole = Qt::UserRole + 1,
CardDescriptionRole,
ServiceDescriptionRole,
IsServiceAvailableRole,
SpeedRole,
WorkPeriodRole,
RegionRole,

View File

@@ -88,10 +88,9 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
const QJsonObject server = m_servers.at(index.row()).toObject();
const auto apiConfig = server.value(configKey::apiConfig).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
switch (role) {
case NameRole: {
if (configVersion || isGoodbyeDpi) {
if (configVersion) {
return server.value(config_key::name).toString();
}
auto name = server.value(config_key::description).toString();
@@ -101,10 +100,6 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
return name;
}
case ServerDescriptionRole: {
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
auto description = getServerDescription(server, index.row());
return configVersion ? description : description + server.value(config_key::hostName).toString();
}
@@ -149,9 +144,6 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
QString primaryDns = server.value(config_key::dns1).toString();
return primaryDns == protocols::dns::amneziaDnsIp;
}
case IsGoodByeDpiRole: {
return isGoodbyeDpi;
}
}
return QVariant();
@@ -216,12 +208,6 @@ QString ServersModel::getServerDescription(const QJsonObject &server, const int
const QString ServersModel::getDefaultServerDescriptionCollapsed()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
@@ -236,12 +222,6 @@ const QString ServersModel::getDefaultServerDescriptionCollapsed()
const QString ServersModel::getDefaultServerDescriptionExpanded()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
@@ -390,8 +370,6 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
roles[IsGoodByeDpiRole] = "isGoodbyeDpi";
return roles;
}

View File

@@ -38,9 +38,7 @@ public:
ApiAvailableCountriesRole,
ApiServerCountryCodeRole,
HasAmneziaDns,
IsGoodByeDpiRole
HasAmneziaDns
};
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);

View File

@@ -0,0 +1,27 @@
#ifndef PROPERTY_HELPER_H
#define PROPERTY_HELPER_H
#include <QObject>
#define AUTO_PROPERTY(TYPE, NAME) \
Q_PROPERTY(TYPE NAME READ NAME WRITE set_ ## NAME NOTIFY NAME ## Changed ) \
public: \
TYPE NAME() const { return m_ ## NAME ; } \
void set_ ## NAME(TYPE value) { \
if (m_ ## NAME == value) return; \
m_ ## NAME = value; \
emit NAME ## Changed(value); \
} \
Q_SIGNAL void NAME ## Changed(TYPE value);\
private: \
TYPE m_ ## NAME{};
#define READONLY_PROPERTY(TYPE, NAME) \
Q_PROPERTY(TYPE NAME READ NAME CONSTANT ) \
public: \
TYPE NAME() const { return m_ ## NAME ; } \
private: \
void NAME(TYPE value) {m_ ## NAME = value; } \
TYPE m_ ## NAME{};
#endif // PROPERTY_HELPER_H

View File

@@ -14,7 +14,6 @@ Button {
property string defaultButtonColor: AmneziaStyle.color.paleGray
property string progressButtonColor: AmneziaStyle.color.paleGray
property string connectedButtonColor: AmneziaStyle.color.goldenApricot
property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv())
implicitWidth: 190
implicitHeight: 190
@@ -51,14 +50,14 @@ Button {
verticalOffset: 0
radius: 10
samples: 25
color: root.buttonActiveFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot
color: root.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot
source: backgroundCircle
}
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.paleGray
strokeWidth: root.buttonActiveFocus ? 1 : 0
strokeWidth: root.activeFocus ? 1 : 0
capStyle: ShapePath.RoundCap
PathAngleArc {
@@ -82,14 +81,14 @@ Button {
return defaultButtonColor
}
}
strokeWidth: root.buttonActiveFocus ? 2 : 3
strokeWidth: root.activeFocus ? 2 : 3
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: backgroundCircle.width / 2
centerY: backgroundCircle.height / 2
radiusX: 93 - (root.buttonActiveFocus ? 2 : 0)
radiusY: 93 - (root.buttonActiveFocus ? 2 : 0)
radiusX: 93 - (root.activeFocus ? 2 : 0)
radiusY: 93 - (root.activeFocus ? 2 : 0)
startAngle: 0
sweepAngle: 360
}

View File

@@ -79,7 +79,6 @@ Button {
visible: text !== ""
color: AmneziaStyle.color.mutedGray
textFormat: Text.RichText
Layout.fillWidth: true
Layout.rightMargin: 16

View File

@@ -20,8 +20,7 @@ Item {
property string buttonImageSource
property string rightImageSource
property string leftImageSource
property bool isLeftImageHoverEnabled: true
property bool isSmallLeftImage: false
property bool isLeftImageHoverEnabled: true //todo separete this qml file to 3
property alias rightButton: rightImage
property alias eyeButton: eyeImage
@@ -115,9 +114,9 @@ Item {
visible: leftImageSource ? true : false
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
Layout.preferredHeight: rightImageSource || !isLeftImageHoverEnabled ? leftImage.implicitHeight : 56
Layout.preferredWidth: rightImageSource || !isLeftImageHoverEnabled ? leftImage.implicitWidth : 56
Layout.rightMargin: rightImageSource || !isLeftImageHoverEnabled ? 16 : 0
radius: 12
color: AmneziaStyle.color.transparent

View File

@@ -102,7 +102,8 @@ Switch {
contentItem: ColumnLayout {
id: content
anchors.verticalCenter: parent.verticalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
ListItemTitleType {

View File

@@ -1,159 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../../Controls2"
import "../../Config"
import "../../Controls2/TextTypes"
import "../../Components"
PageType {
id: root
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
property bool isGoodbyeDpiEnabled: LocalServicesController.isGoodbyeDpiEnabled
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16
HeaderType {
Layout.fillWidth: true
headerText: qsTr("GoodbyeDPI settings")
descriptionText: qsTr("Deep Packet Inspection circumvention utility")
}
SwitcherType {
Layout.fillWidth: true
text: qsTr("Enable GoodbyeDPI")
checked: LocalServicesController.isGoodbyeDpiEnabled
onCheckedChanged: {
if (checked !== LocalServicesController.isGoodbyeDpiEnabled) {
LocalServicesController.toggleGoodbyeDpi(checked)
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 16
enabled: !content.isGoodbyeDpiEnabled
ListItemTitleType {
Layout.fillWidth: true
text: LocalServicesController.getGoodbyeDpiBlackListFile()
}
ImageButtonType {
image: "qrc:/images/controls/folder-search-2.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function() {
var fileName = SystemController.getFileName(qsTr("Open black list file"),
qsTr("Text files (*.txt)"))
LocalServicesController.setGoodbyeDpiBlackListFile(fileName)
}
}
ImageButtonType {
image: "qrc:/images/controls/trash.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function() {
LocalServicesController.resetGoodbyeDpiBlackListFile()
}
}
}
DropDownType {
id: modsetDropDown
Layout.fillWidth: true
descriptionText: qsTr("Setup templates")
headerText: qsTr("Modset")
drawerParent: root
enabled: !content.isGoodbyeDpiEnabled
listView: ListViewWithRadioButtonType {
id: modsetListView
rootWidth: root.width
model: ListModel {
ListElement { name : "-p -r -s -f 2 -k 2 -n -e 2" }
ListElement { name : "-p -r -s -f 2 -k 2 -n -e 40" }
ListElement { name : "-p -r -s -e 40" }
ListElement { name : "-p -r -s" }
ListElement { name : "-f 2 -e 2 --auto-ttl --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-chksum --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload -q" }
}
clickedFunction: function() {
modsetDropDown.text = selectedText
LocalServicesController.setGoodbyeDpiModset(currentIndex + 1)
modsetDropDown.close()
}
Component.onCompleted: {
modsetListView.currentIndex = (LocalServicesController.getGoodbyeDpiModset() - 1)
modsetListView.triggerCurrentItem()
}
}
}
BasicButtonType {
id: detailedInstructionsButton
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Description of options")
clickedFunc: function() {
Qt.openUrlExternally("https://github.com/ValdikSS/GoodbyeDPI?tab=readme-ov-file#how-to-use")
}
}
}
}
}

View File

@@ -89,21 +89,6 @@ PageType {
// KeyNavigation.tab: saveButton
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.topMargin: 16
text: qsTr("Dev gateway environment")
checked: SettingsController.isDevGatewayEnv
onToggled: function() {
SettingsController.isDevGatewayEnv = checked
}
}
}
}
}

View File

@@ -105,8 +105,6 @@ PageType {
buttonTextLabel.font.pixelSize: 14
buttonTextLabel.font.weight: 500
visible: !ServersModel.getDefaultServerData("isGoodbyeDpi")
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled || AppSplitTunnelingModel.isTunnelingEnabled ||
ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
@@ -306,7 +304,7 @@ PageType {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
spacing: 8
visible: !ServersModel.isDefaultServerFromApi && !ServersModel.getDefaultServerData("isGoodbyeDpi")
visible: !ServersModel.isDefaultServerFromApi
Item {
id: focusItem1
@@ -537,12 +535,8 @@ PageType {
Keys.onReturnPressed: serverInfoButton.clicked()
onClicked: function() {
if (ServersModel.getDefaultServerData("isGoodbyeDpi")) {
PageController.goToPage(PageEnum.PageGoodByeDpiSettings, PageEnum.LocalServices)
} else {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
drawer.close()
}
}

View File

@@ -95,22 +95,6 @@ PageType {
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
visible: Qt.platform.os === "windows"
text: qsTr("Local bypass services")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/app.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageGoodByeDpiSettings, PageEnum.LocalServices)
}
}
DividerType {}
LabelWithButtonType {
id: backup
Layout.fillWidth: true

View File

@@ -120,7 +120,7 @@ PageType {
id: mailButton
Layout.fillWidth: true
text: qsTr("support@amnezia.org")
text: qsTr("Mail")
descriptionText: qsTr("For reviews and bug reports")
leftImageSource: "qrc:/images/controls/mail.svg"
@@ -128,8 +128,6 @@ PageType {
parentFlickable: fl
clickedFunction: function() {
GC.copyToClipBoard(text)
PageController.showNotificationMessage(qsTr("Copied"))
}
}

View File

@@ -16,6 +16,18 @@ import "../Controls2/TextTypes"
PageType {
id: root
Connections {
target: SettingsController
function onLoggingStateChanged() {
if (SettingsController.isLoggingEnabled) {
var message = qsTr("Logging is enabled. Note that logs will be automatically \
disabled after 14 days, and all log files will be deleted.")
PageController.showNotificationMessage(message)
}
}
}
defaultActiveFocusItem: focusItem
Item {
@@ -46,12 +58,13 @@ PageType {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Logging")
descriptionText: qsTr("Enabling this function will save application's logs automatically. " +
@@ -62,13 +75,11 @@ PageType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable logs")
text: qsTr("Save logs")
checked: SettingsController.isLoggingEnabled
//KeyNavigation.tab: openFolderButton
KeyNavigation.tab: openFolderButton
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
@@ -76,200 +87,132 @@ PageType {
}
}
DividerType {}
LabelWithButtonType {
// id: labelWithButton2
RowLayout {
Layout.fillWidth: true
Layout.topMargin: -8
text: qsTr("Clear logs")
leftImageSource: "qrc:/images/controls/trash.svg"
isSmallLeftImage: true
ColumnLayout {
Layout.alignment: Qt.AlignBaseline
Layout.preferredWidth: GC.isMobile() ? 0 : root.width / 3
visible: !GC.isMobile()
// KeyNavigation.tab: labelWithButton3
ImageButtonType {
id: openFolderButton
Layout.alignment: Qt.AlignHCenter
clickedFunction: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
implicitWidth: 56
implicitHeight: 56
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.clearLogs()
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
image: "qrc:/images/controls/folder-open.svg"
KeyNavigation.tab: saveButton
onClicked: SettingsController.openLogsFolder()
Keys.onReturnPressed: openFolderButton.clicked()
Keys.onEnterPressed: openFolderButton.clicked()
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
CaptionTextType {
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
text: qsTr("Open folder with logs")
color: AmneziaStyle.color.paleGray
}
}
ColumnLayout {
Layout.alignment: Qt.AlignBaseline
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ImageButtonType {
id: saveButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/save.svg"
KeyNavigation.tab: clearButton
Keys.onReturnPressed: saveButton.clicked()
Keys.onEnterPressed: saveButton.clicked()
onClicked: {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
CaptionTextType {
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
ListItemTitleType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Client logs")
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN logs")
}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openLogsFolder()
}
}
DividerType {}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
text: qsTr("Save logs to file")
color: AmneziaStyle.color.paleGray
}
}
}
DividerType {}
ColumnLayout {
Layout.alignment: Qt.AlignBaseline
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ListItemTitleType {
visible: !GC.isMobile()
ImageButtonType {
id: clearButton
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
implicitWidth: 56
implicitHeight: 56
text: qsTr("Service logs")
}
image: "qrc:/images/controls/delete.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
ParagraphTextType {
visible: !GC.isMobile()
Keys.onReturnPressed: clearButton.clicked()
Keys.onEnterPressed: clearButton.clicked()
onClicked: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.clearLogs()
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN-service logs")
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openServiceLogsFolder()
}
}
DividerType {
visible: !GC.isMobile()
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportServiceLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
CaptionTextType {
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
text: qsTr("Clear logs")
color: AmneziaStyle.color.paleGray
}
}
}
DividerType {
visible: !GC.isMobile()
}
}
}
}

View File

@@ -111,8 +111,6 @@ PageType {
id: server
Layout.fillWidth: true
visible: !isGoodbyeDpi
text: name
parentFlickable: fl
descriptionText: {

View File

@@ -88,10 +88,8 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: {
if (isServiceAvailable) {
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
}
}

View File

@@ -55,51 +55,6 @@ PageType {
Layout.leftMargin: 16
headerText: qsTr("Connection")
actionButtonImage: PageController.isStartPageVisible() ? "qrc:/images/controls/more-vertical.svg" : ""
actionButtonFunction: function() {
moreActionsDrawer.open()
}
DrawerType2 {
id: moreActionsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.35
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 32
headerText: qsTr("Settings")
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}
}
}
}
}
}
ParagraphTextType {
@@ -164,6 +119,8 @@ PageType {
CardWithIconsType {
id: apiInstalling
visible: false
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16

View File

@@ -54,7 +54,7 @@ PageType {
function onServerAlreadyExists(serverIndex) {
PageController.goToStartPage()
ServersModel.processedIndex = serverIndex
PageController.goToPage(PageEnum.PageSettingsServerInfo, PageEnum.LocalServices, false)
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
PageController.showErrorMessage(qsTr("The server has already been added to the application"))
}

View File

@@ -140,23 +140,22 @@ PageType {
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
PageController.showBusyIndicator(true)
if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) {
PageController.showBusyIndicator(false)
ExportController.exportErrorOccurred(qsTr("Access error!"))
return
} else {
ExportController.generateFullAccessConfig()
}
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.open()
shareConnectionDrawer.contentVisible = true
shareConnectionDrawer.contentVisible = false
PageController.showBusyIndicator(true)
if (Qt.platform.os === "android") {
ExportController.generateFullAccessConfigAndroid();
} else {
ExportController.generateFullAccessConfig();
}
PageController.showBusyIndicator(false)
shareConnectionDrawer.contentVisible = true
}
}
}

View File

@@ -60,8 +60,8 @@ PageType {
tabBarStackView.pop()
}
function onGoToPage(page, folder, slide) {
var pagePath = PageController.getPagePath(page, folder)
function onGoToPage(page, slide) {
var pagePath = PageController.getPagePath(page)
if (slide) {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
@@ -202,22 +202,6 @@ PageType {
PageController.showNotificationMessage(qsTr("Settings restored from backup file"))
PageController.goToPageHome()
}
function onLoggingStateChanged() {
if (SettingsController.isLoggingEnabled) {
var message = qsTr("Logging is enabled. Note that logs will be automatically" +
"disabled after 14 days, and all log files will be deleted.")
PageController.showNotificationMessage(message)
}
}
}
Connections {
target: LocalServicesController
function onErrorOccurred(error) {
PageController.showErrorMessage(error)
}
}
StackViewType {

View File

@@ -69,6 +69,22 @@ QString Utils::JsonToString(const QJsonArray &array, QJsonDocument::JsonFormat f
return doc.toJson(format);
}
QString Utils::systemLogPath()
{
#ifdef Q_OS_WIN
QStringList locationList = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
QString primaryLocation = "ProgramData";
foreach (const QString &location, locationList) {
if (location.contains(primaryLocation)) {
return QString("%1/%2/log").arg(location).arg(APPLICATION_NAME);
}
}
return QString();
#else
return QString("/var/log/%1").arg(APPLICATION_NAME);
#endif
}
bool Utils::initializePath(const QString &path)
{
QDir dir;
@@ -198,15 +214,6 @@ QString Utils::certUtilPath()
#endif
}
QString Utils::goodbyedpiPath()
{
#ifdef Q_OS_WIN
return Utils::executable("goodbyedpi/goodbyedpi", true);
#else
return "";
#endif
}
QString Utils::tun2socksPath()
{
#ifdef Q_OS_WIN

View File

@@ -23,6 +23,7 @@ public:
static QJsonObject JsonFromString(const QString &string);
static QString executable(const QString &baseName, bool absPath);
static QString usrExecutable(const QString &baseName);
static QString systemLogPath();
static bool createEmptyFile(const QString &path);
static bool initializePath(const QString &path);
@@ -33,7 +34,6 @@ public:
static QString wireguardExecPath();
static QString certUtilPath();
static QString tun2socksPath();
static QString goodbyedpiPath();
#ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);

Some files were not shown because too many files have changed in this diff Show More