Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 871586df0c | |||
| 302223051f | |||
| 77f8a1401b | |||
| 776ec50c54 | |||
| f2c1564d6c | |||
| 0afe0d3341 | |||
| 0863e69d0a |
@@ -294,70 +294,6 @@ jobs:
|
|||||||
path: deploy/build/client/AmneziaVPN.app
|
path: deploy/build/client/AmneziaVPN.app
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
Build-MacOS-NE:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
QT_VERSION: 6.4.3
|
|
||||||
QIF_VERSION: 4.6
|
|
||||||
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/
|
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
|
||||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 'Setup Xcode'
|
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: '14.3.1'
|
|
||||||
|
|
||||||
- name: 'Install desktop Qt'
|
|
||||||
uses: jurplel/install-qt-action@v3
|
|
||||||
with:
|
|
||||||
version: ${{ env.QT_VERSION }}
|
|
||||||
host: 'mac'
|
|
||||||
target: 'desktop'
|
|
||||||
modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia'
|
|
||||||
arch: 'clang_64'
|
|
||||||
dir: ${{ runner.temp }}
|
|
||||||
set-env: 'true'
|
|
||||||
extra: '--base ${{ env.QT_MIRROR }}'
|
|
||||||
- name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}'
|
|
||||||
run: |
|
|
||||||
mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework
|
|
||||||
wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip
|
|
||||||
unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/
|
|
||||||
- name: 'Install Go'
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.22.1'
|
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: 'Get sources'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- name: 'Install dependencies'
|
|
||||||
run: pip install jsonschema jinja2
|
|
||||||
|
|
||||||
- name: 'Set execute permissions for deploy script'
|
|
||||||
run: chmod +x deploy/build_macos_ne.sh
|
|
||||||
|
|
||||||
- name: 'Build and deploy macOS NE'
|
|
||||||
run: |
|
|
||||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
|
||||||
export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin"
|
|
||||||
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
|
|
||||||
bash deploy/build_macos_ne.sh
|
|
||||||
env:
|
|
||||||
APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}
|
|
||||||
APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }}
|
|
||||||
APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }}
|
|
||||||
MAC_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
|
|
||||||
MAC_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
|
|
||||||
MAC_SIGNING_CERT_PASSWORD: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }}
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
Build-Android:
|
Build-Android:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
[submodule "client/3rd/OpenVPNAdapter"]
|
[submodule "client/3rd/OpenVPNAdapter"]
|
||||||
path = client/3rd/OpenVPNAdapter
|
path = client/3rd/OpenVPNAdapter
|
||||||
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
url = https://github.com/amnezia-vpn/OpenVPNAdapter.git
|
||||||
branch = macos-dirty-build
|
|
||||||
[submodule "client/3rd/qtkeychain"]
|
[submodule "client/3rd/qtkeychain"]
|
||||||
path = client/3rd/qtkeychain
|
path = client/3rd/qtkeychain
|
||||||
url = https://github.com/frankosterfeld/qtkeychain.git
|
url = https://github.com/frankosterfeld/qtkeychain.git
|
||||||
@@ -11,7 +10,6 @@
|
|||||||
[submodule "client/3rd-prebuilt"]
|
[submodule "client/3rd-prebuilt"]
|
||||||
path = client/3rd-prebuilt
|
path = client/3rd-prebuilt
|
||||||
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
url = https://github.com/amnezia-vpn/3rd-prebuilt
|
||||||
branch = fixbug/mac-network-extension
|
|
||||||
[submodule "client/3rd/amneziawg-apple"]
|
[submodule "client/3rd/amneziawg-apple"]
|
||||||
path = client/3rd/amneziawg-apple
|
path = client/3rd/amneziawg-apple
|
||||||
url = https://github.com/amnezia-vpn/amneziawg-apple
|
url = https://github.com/amnezia-vpn/amneziawg-apple
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.1.9
|
project(${PROJECT} VERSION 4.8.0.5
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
|||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 65)
|
set(APP_ANDROID_VERSION_CODE 61)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
@@ -26,20 +26,18 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
|||||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||||
set(MZ_PLATFORM_NAME "wasm")
|
set(MZ_PLATFORM_NAME "wasm")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Platform: ${MZ_PLATFORM_NAME}")
|
|
||||||
message(STATUS "Version: ${MACOS_NE}")
|
|
||||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if((APPLE AND NOT IOS) OR (DEFINED MACOS_NE AND MACOS_NE AND NOT IOS))
|
if(APPLE AND NOT IOS)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
# Mac OSX with Network Extension don't need service
|
if(NOT IOS AND NOT ANDROID)
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
|
||||||
add_subdirectory(service)
|
add_subdirectory(service)
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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()
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
// 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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// 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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// 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
|
||||||
@@ -30,12 +30,11 @@ 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_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
|
||||||
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
|
||||||
|
|
||||||
if(IOS OR MACOS_NE)
|
if(IOS)
|
||||||
set(PACKAGES ${PACKAGES} Multimedia)
|
set(PACKAGES ${PACKAGES} Multimedia)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#Macos Network Extension doesn't need Widgets
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|
||||||
set(PACKAGES ${PACKAGES} Widgets)
|
set(PACKAGES ${PACKAGES} Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -48,23 +47,18 @@ set(LIBS ${LIBS}
|
|||||||
Qt6::Core5Compat Qt6::Concurrent
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
if(IOS OR MACOS_NE)
|
if(IOS)
|
||||||
set(LIBS ${LIBS} Qt6::Multimedia)
|
set(LIBS ${LIBS} Qt6::Multimedia)
|
||||||
endif()
|
endif()
|
||||||
# message("Client desktop build ", ${MACOS_NE})
|
|
||||||
#Macos Network Extension doesn't need Widgets
|
|
||||||
|
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
message("Run this block when MACOS_NE is not defined or set to FALSE")
|
|
||||||
set(LIBS ${LIBS} Qt6::Widgets)
|
set(LIBS ${LIBS} Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||||
|
|
||||||
# TODO error in there
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|
||||||
message("Run this block when MACOS_NE is not defined or set to FALSE")
|
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
||||||
@@ -102,18 +96,10 @@ qt6_add_resources(QRC ${I18NQRC} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
|
|||||||
# -- i18n end
|
# -- i18n end
|
||||||
|
|
||||||
if(IOS)
|
if(IOS)
|
||||||
message("Building for iOS")
|
|
||||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/ios/scripts/openvpn.sh args
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build openvpn adapter for MacOS Network Extension
|
|
||||||
if(MACOS_NE)
|
|
||||||
message("Building for MacOS Network Extension")
|
|
||||||
execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/macos/scripts/openvpn.sh args
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(IS_CI ${CI})
|
set(IS_CI ${CI})
|
||||||
if(IS_CI)
|
if(IS_CI)
|
||||||
message("Detected CI env")
|
message("Detected CI env")
|
||||||
@@ -174,24 +160,12 @@ include_directories(mozilla)
|
|||||||
include_directories(mozilla/shared)
|
include_directories(mozilla/shared)
|
||||||
include_directories(mozilla/models)
|
include_directories(mozilla/models)
|
||||||
|
|
||||||
if(MACOS_NE)
|
if(NOT IOS)
|
||||||
message("MACOS_NE is ON")
|
|
||||||
add_definitions(-DQ_OS_IOS)
|
|
||||||
add_definitions(-DMACOS_NE)
|
|
||||||
message("Add macros for MacOS Network Extension")
|
|
||||||
else()
|
|
||||||
message("MACOS_NE is OFF")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
if(NOT IOS AND NOT MACOS_NE)
|
|
||||||
message(" Add header for non-IOS and non-MACOS_NE")
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||||
@@ -236,7 +210,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT MACOS_NE)
|
if(NOT IOS)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||||
)
|
)
|
||||||
@@ -344,11 +318,10 @@ if(LINUX AND NOT ANDROID)
|
|||||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Macos Network Extension doesn't need
|
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
message("Client desktop build")
|
||||||
message("-----> Client desktop build")
|
|
||||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||||
message("-----> Add AMNEZIA_DESKTOP")
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
|
${CMAKE_CURRENT_LIST_DIR}/core/ipcclient.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
|
${CMAKE_CURRENT_LIST_DIR}/core/privileged_process.h
|
||||||
@@ -381,12 +354,9 @@ endif()
|
|||||||
if(IOS)
|
if(IOS)
|
||||||
include(cmake/ios.cmake)
|
include(cmake/ios.cmake)
|
||||||
include(cmake/ios-arch-fixup.cmake)
|
include(cmake/ios-arch-fixup.cmake)
|
||||||
elseif(APPLE AND NOT IOS AND NOT DEFINED MACOS_NE)
|
elseif(APPLE AND NOT IOS)
|
||||||
# include(cmake/osxtools.cmake)
|
|
||||||
include(cmake/macos.cmake)
|
|
||||||
elseif(APPLE AND NOT IOS AND MACOS_NE)
|
|
||||||
include(cmake/osxtools.cmake)
|
include(cmake/osxtools.cmake)
|
||||||
include(cmake/macos_ne.cmake)
|
include(cmake/macos.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||||
@@ -405,7 +375,7 @@ elseif(APPLE AND NOT IOS)
|
|||||||
set(DEPLOY_PLATFORM_PATH "macos")
|
set(DEPLOY_PLATFORM_PATH "macos")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
if(NOT IOS AND NOT ANDROID)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${PROJECT} POST_BUILD
|
TARGET ${PROJECT} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||||
@@ -424,36 +394,4 @@ if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||||
if(MACOS_NE)
|
|
||||||
message("Copy MacOS Network Extension files")
|
|
||||||
message("QT_HOST_PATH: $ENV{QT_MACOS_ROOT_DIR}")
|
|
||||||
|
|
||||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
|
||||||
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks"
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
"$ENV{QT_MACOS_ROOT_DIR}/lib/QtConcurrent.framework"
|
|
||||||
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/QtConcurrent.framework"
|
|
||||||
COMMENT "Copying QtConcurrent.framework from QT_MACOS_ROOT_DIR to the bundle's Frameworks directory"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# MacOS specific application deployment
|
|
||||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# MacOS specific code signing for Release
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
SET(SIGN_CMD codesign --deep --force --sign 'Apple Distribution: Privacy Technologies OU \(X7UJ388FXK\)' --timestamp --options runtime $<TARGET_BUNDLE_DIR:AmneziaVPN>)
|
|
||||||
message("Manual signing bundle...")
|
|
||||||
message(${SIGN_CMD})
|
|
||||||
|
|
||||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
COMMAND ${SIGN_CMD}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt_finalize_target(${PROJECT})
|
qt_finalize_target(${PROJECT})
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QLocalSocket>
|
|
||||||
#include <QLocalServer>
|
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "ui/models/installedAppsModel.h"
|
#include "ui/models/installedAppsModel.h"
|
||||||
@@ -30,7 +28,13 @@
|
|||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||||
|
#else
|
||||||
|
AmneziaApplication::AmneziaApplication(int &argc, char *argv[], bool allowSecondary, SingleApplication::Options options, int timeout,
|
||||||
|
const QString &userData)
|
||||||
|
: SingleApplication(argc, argv, allowSecondary, options, timeout, userData)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
setQuitOnLastWindowClosed(false);
|
setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
@@ -176,6 +180,16 @@ void AmneziaApplication::init()
|
|||||||
m_pageController->showOnStartup();
|
m_pageController->showOnStartup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO - fix
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||||
|
if (isPrimary()) {
|
||||||
|
QObject::connect(this, &SingleApplication::instanceStarted, m_pageController.get(), [this]() {
|
||||||
|
qDebug() << "Secondary instance started, showing this window instead";
|
||||||
|
emit m_pageController->raiseMainWindow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Android TextArea clipboard workaround
|
// Android TextArea clipboard workaround
|
||||||
// Text from TextArea always has "text/html" mime-type:
|
// Text from TextArea always has "text/html" mime-type:
|
||||||
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
|
// /qt/6.6.1/Src/qtdeclarative/src/quick/items/qquicktextcontrol.cpp:1865
|
||||||
@@ -280,24 +294,6 @@ bool AmneziaApplication::parseCommands()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
void AmneziaApplication::startLocalServer() {
|
|
||||||
const QString serverName("AmneziaVPNInstance");
|
|
||||||
QLocalServer::removeServer(serverName);
|
|
||||||
|
|
||||||
QLocalServer* server = new QLocalServer(this);
|
|
||||||
server->listen(serverName);
|
|
||||||
|
|
||||||
QObject::connect(server, &QLocalServer::newConnection, this, [server, this]() {
|
|
||||||
if (server) {
|
|
||||||
QLocalSocket* clientConnection = server->nextPendingConnection();
|
|
||||||
clientConnection->deleteLater();
|
|
||||||
}
|
|
||||||
emit m_pageController->raiseMainWindow();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||||
{
|
{
|
||||||
return m_engine;
|
return m_engine;
|
||||||
|
|||||||
@@ -53,14 +53,22 @@
|
|||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||||
#else
|
#else
|
||||||
#define AMNEZIA_BASE_CLASS QApplication
|
#define AMNEZIA_BASE_CLASS SingleApplication
|
||||||
|
#define QAPPLICATION_CLASS QApplication
|
||||||
|
#include "singleapplication.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
AmneziaApplication(int &argc, char *argv[]);
|
AmneziaApplication(int &argc, char *argv[]);
|
||||||
|
#else
|
||||||
|
AmneziaApplication(int &argc, char *argv[], bool allowSecondary = false,
|
||||||
|
SingleApplication::Options options = SingleApplication::User, int timeout = 1000,
|
||||||
|
const QString &userData = {});
|
||||||
|
#endif
|
||||||
virtual ~AmneziaApplication();
|
virtual ~AmneziaApplication();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
@@ -70,10 +78,6 @@ public:
|
|||||||
void updateTranslator(const QLocale &locale);
|
void updateTranslator(const QLocale &locale);
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
void startLocalServer();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QQmlApplicationEngine *qmlEngine() const;
|
QQmlApplicationEngine *qmlEngine() const;
|
||||||
QNetworkAccessManager *manager() { return m_nam; }
|
QNetworkAccessManager *manager() { return m_nam; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
package org.amnezia.vpn.protocol.awg
|
package org.amnezia.vpn.protocol.awg
|
||||||
|
|
||||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||||
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
|
import org.amnezia.vpn.util.optStringOrNull
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Awg : Wireguard() {
|
class Awg : Wireguard() {
|
||||||
|
|
||||||
override val ifName: String = "awg0"
|
override val ifName: String = "awg0"
|
||||||
|
|
||||||
override fun parseConfig(config: JSONObject): WireguardConfig {
|
override fun parseConfig(config: JSONObject): AwgConfig {
|
||||||
val configData = config.getJSONObject("awg_config_data")
|
val configData = config.getJSONObject("awg_config_data")
|
||||||
return WireguardConfig.build {
|
return AwgConfig.build {
|
||||||
setUseProtocolExtension(true)
|
|
||||||
configExtensionParameters(configData)
|
|
||||||
configWireguard(config, configData)
|
configWireguard(config, configData)
|
||||||
configSplitTunneling(config)
|
configSplitTunneling(config)
|
||||||
configAppSplitTunneling(config)
|
configAppSplitTunneling(config)
|
||||||
|
configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
|
||||||
|
configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
|
||||||
|
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
||||||
|
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
||||||
|
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
||||||
|
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
||||||
|
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
||||||
|
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
||||||
|
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package org.amnezia.vpn.protocol.awg
|
||||||
|
|
||||||
|
import org.amnezia.vpn.protocol.BadConfigException
|
||||||
|
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
|
||||||
|
|
||||||
|
class AwgConfig private constructor(
|
||||||
|
wireguardConfigBuilder: WireguardConfig.Builder,
|
||||||
|
val jc: Int,
|
||||||
|
val jmin: Int,
|
||||||
|
val jmax: Int,
|
||||||
|
val s1: Int,
|
||||||
|
val s2: Int,
|
||||||
|
val h1: Long,
|
||||||
|
val h2: Long,
|
||||||
|
val h3: Long,
|
||||||
|
val h4: Long
|
||||||
|
) : WireguardConfig(wireguardConfigBuilder) {
|
||||||
|
|
||||||
|
private constructor(builder: Builder) : this(
|
||||||
|
builder,
|
||||||
|
builder.jc,
|
||||||
|
builder.jmin,
|
||||||
|
builder.jmax,
|
||||||
|
builder.s1,
|
||||||
|
builder.s2,
|
||||||
|
builder.h1,
|
||||||
|
builder.h2,
|
||||||
|
builder.h3,
|
||||||
|
builder.h4
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
|
||||||
|
super.appendDeviceLine(this)
|
||||||
|
appendLine("jc=$jc")
|
||||||
|
appendLine("jmin=$jmin")
|
||||||
|
appendLine("jmax=$jmax")
|
||||||
|
appendLine("s1=$s1")
|
||||||
|
appendLine("s2=$s2")
|
||||||
|
appendLine("h1=$h1")
|
||||||
|
appendLine("h2=$h2")
|
||||||
|
appendLine("h3=$h3")
|
||||||
|
appendLine("h4=$h4")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder : WireguardConfig.Builder() {
|
||||||
|
|
||||||
|
private var _jc: Int? = null
|
||||||
|
internal var jc: Int
|
||||||
|
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
|
||||||
|
private set(value) { _jc = value }
|
||||||
|
|
||||||
|
private var _jmin: Int? = null
|
||||||
|
internal var jmin: Int
|
||||||
|
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
|
||||||
|
private set(value) { _jmin = value }
|
||||||
|
|
||||||
|
private var _jmax: Int? = null
|
||||||
|
internal var jmax: Int
|
||||||
|
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
|
||||||
|
private set(value) { _jmax = value }
|
||||||
|
|
||||||
|
private var _s1: Int? = null
|
||||||
|
internal var s1: Int
|
||||||
|
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
|
||||||
|
private set(value) { _s1 = value }
|
||||||
|
|
||||||
|
private var _s2: Int? = null
|
||||||
|
internal var s2: Int
|
||||||
|
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
|
||||||
|
private set(value) { _s2 = value }
|
||||||
|
|
||||||
|
private var _h1: Long? = null
|
||||||
|
internal var h1: Long
|
||||||
|
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
|
||||||
|
private set(value) { _h1 = value }
|
||||||
|
|
||||||
|
private var _h2: Long? = null
|
||||||
|
internal var h2: Long
|
||||||
|
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
|
||||||
|
private set(value) { _h2 = value }
|
||||||
|
|
||||||
|
private var _h3: Long? = null
|
||||||
|
internal var h3: Long
|
||||||
|
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
|
||||||
|
private set(value) { _h3 = value }
|
||||||
|
|
||||||
|
private var _h4: Long? = null
|
||||||
|
internal var h4: Long
|
||||||
|
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
|
||||||
|
private set(value) { _h4 = value }
|
||||||
|
|
||||||
|
fun setJc(jc: Int) = apply { this.jc = jc }
|
||||||
|
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
|
||||||
|
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
||||||
|
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
||||||
|
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
||||||
|
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
||||||
|
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
||||||
|
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||||
|
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
||||||
|
|
||||||
|
override fun build(): AwgConfig = configBuild().run { AwgConfig(this@Builder) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,16 +3,10 @@ package org.amnezia.vpn.protocol.cloak
|
|||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import net.openvpn.ovpn3.ClientAPI_Config
|
import net.openvpn.ovpn3.ClientAPI_Config
|
||||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||||
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Cloak : OpenVpn() {
|
class Cloak : OpenVpn() {
|
||||||
|
|
||||||
override fun internalInit() {
|
|
||||||
super.internalInit()
|
|
||||||
if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun parseConfig(config: JSONObject): ClientAPI_Config {
|
override fun parseConfig(config: JSONObject): ClientAPI_Config {
|
||||||
val openVpnConfig = ClientAPI_Config()
|
val openVpnConfig = ClientAPI_Config()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.amnezia.vpn.protocol.Protocol
|
|||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
import org.amnezia.vpn.protocol.Statistics
|
import org.amnezia.vpn.protocol.Statistics
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
|
||||||
import org.amnezia.vpn.util.net.InetNetwork
|
import org.amnezia.vpn.util.net.InetNetwork
|
||||||
import org.amnezia.vpn.util.net.getLocalNetworks
|
import org.amnezia.vpn.util.net.getLocalNetworks
|
||||||
import org.amnezia.vpn.util.net.parseInetAddress
|
import org.amnezia.vpn.util.net.parseInetAddress
|
||||||
@@ -35,10 +34,7 @@ open class OpenVpn : Protocol() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun internalInit() {
|
override fun internalInit() {
|
||||||
if (!isInitialized) {
|
if (!isInitialized) loadSharedLibrary(context, "ovpn3")
|
||||||
loadSharedLibrary(context, "ovpn3")
|
|
||||||
loadSharedLibrary(context, "ovpnutil")
|
|
||||||
}
|
|
||||||
if (this::scope.isInitialized) {
|
if (this::scope.isInitialized) {
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.amnezia.vpn.protocol
|
|||||||
|
|
||||||
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
|
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
|
||||||
|
|
||||||
|
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
||||||
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
||||||
|
|
||||||
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
|
||||||
|
|||||||
@@ -158,6 +158,60 @@ abstract class Protocol {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
vpnBuilder.setMetered(false)
|
vpnBuilder.setMetered(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
|
||||||
|
Log.d(TAG, "Extracting library: $libraryName")
|
||||||
|
val apks = hashSetOf<String>()
|
||||||
|
context.applicationInfo.run {
|
||||||
|
sourceDir?.let { apks += it }
|
||||||
|
splitSourceDirs?.let { apks += it }
|
||||||
|
}
|
||||||
|
for (abi in Build.SUPPORTED_ABIS) {
|
||||||
|
for (apk in apks) {
|
||||||
|
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
|
||||||
|
val mappedName = System.mapLibraryName(libraryName)
|
||||||
|
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
|
||||||
|
val zipEntry = zipFile.getEntry(libraryZipPath)
|
||||||
|
zipEntry?.let {
|
||||||
|
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
|
||||||
|
FileOutputStream(destination).use { outStream ->
|
||||||
|
zipFile.getInputStream(zipEntry).use { inStream ->
|
||||||
|
inStream.copyTo(outStream, 32 * 1024)
|
||||||
|
outStream.fd.sync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
|
fun loadSharedLibrary(context: Context, libraryName: String) {
|
||||||
|
Log.d(TAG, "Loading library: $libraryName")
|
||||||
|
try {
|
||||||
|
System.loadLibrary(libraryName)
|
||||||
|
return
|
||||||
|
} catch (_: UnsatisfiedLinkError) {
|
||||||
|
Log.d(TAG, "Failed to load library, try to extract it from apk")
|
||||||
|
}
|
||||||
|
var tempFile: File? = null
|
||||||
|
try {
|
||||||
|
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
|
||||||
|
if (extractLibrary(context, libraryName, tempFile)) {
|
||||||
|
System.load(tempFile.absolutePath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
|
||||||
|
} finally {
|
||||||
|
tempFile?.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
|
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
||||||
|
|
||||||
<array name="bundled_libs">
|
<array name="bundled_libs">
|
||||||
|
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<array name="qt_libs">
|
<array name="qt_libs">
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import org.amnezia.vpn.protocol.getStatistics
|
import org.amnezia.vpn.protocol.getStatistics
|
||||||
import org.amnezia.vpn.protocol.getStatus
|
import org.amnezia.vpn.protocol.getStatus
|
||||||
import org.amnezia.vpn.qt.QtAndroidController
|
import org.amnezia.vpn.qt.QtAndroidController
|
||||||
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.Prefs
|
import org.amnezia.vpn.util.Prefs
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
@@ -159,7 +158,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d(TAG, "Create Amnezia activity: $intent")
|
Log.d(TAG, "Create Amnezia activity: $intent")
|
||||||
loadLibs()
|
|
||||||
window.apply {
|
window.apply {
|
||||||
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
statusBarColor = getColor(R.color.black)
|
statusBarColor = getColor(R.color.black)
|
||||||
@@ -181,17 +179,6 @@ class AmneziaActivity : QtActivity() {
|
|||||||
runBlocking { vpnProto = proto.await() }
|
runBlocking { vpnProto = proto.await() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLibs() {
|
|
||||||
listOf(
|
|
||||||
"rsapss",
|
|
||||||
"crypto_3",
|
|
||||||
"ssl_3",
|
|
||||||
"ssh"
|
|
||||||
).forEach {
|
|
||||||
loadSharedLibrary(this.applicationContext, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun registerBroadcastReceivers() {
|
private fun registerBroadcastReceivers() {
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
registerBroadcastReceiver(
|
registerBroadcastReceiver(
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import androidx.annotation.MainThread
|
|||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import java.net.UnknownHostException
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
@@ -41,6 +40,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import org.amnezia.vpn.protocol.BadConfigException
|
import org.amnezia.vpn.protocol.BadConfigException
|
||||||
|
import org.amnezia.vpn.protocol.LoadLibraryException
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
@@ -50,7 +50,6 @@ import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
|
|||||||
import org.amnezia.vpn.protocol.VpnException
|
import org.amnezia.vpn.protocol.VpnException
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
import org.amnezia.vpn.protocol.putStatus
|
import org.amnezia.vpn.protocol.putStatus
|
||||||
import org.amnezia.vpn.util.LoadLibraryException
|
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.Prefs
|
import org.amnezia.vpn.util.Prefs
|
||||||
import org.amnezia.vpn.util.net.NetworkState
|
import org.amnezia.vpn.util.net.NetworkState
|
||||||
@@ -128,8 +127,6 @@ open class AmneziaVpnService : VpnService() {
|
|||||||
|
|
||||||
is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}")
|
is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}")
|
||||||
|
|
||||||
is UnknownHostException -> onError("Unknown host")
|
|
||||||
|
|
||||||
else -> throw e
|
else -> throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package org.amnezia.vpn.util
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
|
|
||||||
private const val TAG = "LibraryLoader"
|
|
||||||
|
|
||||||
object LibraryLoader {
|
|
||||||
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
|
|
||||||
Log.d(TAG, "Extracting library: $libraryName")
|
|
||||||
val apks = hashSetOf<String>()
|
|
||||||
context.applicationInfo.run {
|
|
||||||
sourceDir?.let { apks += it }
|
|
||||||
splitSourceDirs?.let { apks += it }
|
|
||||||
}
|
|
||||||
for (abi in Build.SUPPORTED_ABIS) {
|
|
||||||
for (apk in apks) {
|
|
||||||
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
|
|
||||||
val mappedName = System.mapLibraryName(libraryName)
|
|
||||||
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
|
|
||||||
val zipEntry = zipFile.getEntry(libraryZipPath)
|
|
||||||
zipEntry?.let {
|
|
||||||
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
|
|
||||||
FileOutputStream(destination).use { outStream ->
|
|
||||||
zipFile.getInputStream(zipEntry).use { inStream ->
|
|
||||||
inStream.copyTo(outStream, 32 * 1024)
|
|
||||||
outStream.fd.sync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
|
||||||
fun loadSharedLibrary(context: Context, libraryName: String) {
|
|
||||||
Log.d(TAG, "Loading library: $libraryName")
|
|
||||||
try {
|
|
||||||
System.loadLibrary(libraryName)
|
|
||||||
return
|
|
||||||
} catch (_: UnsatisfiedLinkError) {
|
|
||||||
Log.d(TAG, "Failed to load library, try to extract it from apk")
|
|
||||||
}
|
|
||||||
var tempFile: File? = null
|
|
||||||
try {
|
|
||||||
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
|
|
||||||
if (extractLibrary(context, libraryName, tempFile)) {
|
|
||||||
System.load(tempFile.absolutePath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
|
|
||||||
} finally {
|
|
||||||
tempFile?.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
|
|
||||||
@@ -3,6 +3,7 @@ package org.amnezia.vpn.protocol.wireguard
|
|||||||
import android.net.VpnService.Builder
|
import android.net.VpnService.Builder
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import java.util.TreeMap
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -12,7 +13,6 @@ import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
|||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
import org.amnezia.vpn.protocol.Statistics
|
import org.amnezia.vpn.protocol.Statistics
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.asSequence
|
import org.amnezia.vpn.util.asSequence
|
||||||
import org.amnezia.vpn.util.net.InetEndpoint
|
import org.amnezia.vpn.util.net.InetEndpoint
|
||||||
@@ -129,29 +129,12 @@ open class Wireguard : Protocol() {
|
|||||||
val port = configData.getInt("port")
|
val port = configData.getInt("port")
|
||||||
setEndpoint(InetEndpoint(host, port))
|
setEndpoint(InetEndpoint(host, port))
|
||||||
|
|
||||||
if (configData.optBoolean("isObfuscationEnabled")) {
|
|
||||||
setUseProtocolExtension(true)
|
|
||||||
configExtensionParameters(configData)
|
|
||||||
}
|
|
||||||
|
|
||||||
configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
|
configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
|
||||||
configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
|
configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
|
||||||
configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
|
configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
|
||||||
configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
|
configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun WireguardConfig.Builder.configExtensionParameters(configData: JSONObject) {
|
|
||||||
configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) }
|
|
||||||
configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) }
|
|
||||||
configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) }
|
|
||||||
configData.optStringOrNull("S1")?.let { setS1(it.toInt()) }
|
|
||||||
configData.optStringOrNull("S2")?.let { setS2(it.toInt()) }
|
|
||||||
configData.optStringOrNull("H1")?.let { setH1(it.toLong()) }
|
|
||||||
configData.optStringOrNull("H2")?.let { setH2(it.toLong()) }
|
|
||||||
configData.optStringOrNull("H3")?.let { setH3(it.toLong()) }
|
|
||||||
configData.optStringOrNull("H4")?.let { setH4(it.toLong()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) {
|
||||||
if (tunnelHandle != -1) {
|
if (tunnelHandle != -1) {
|
||||||
Log.w(TAG, "Tunnel already up")
|
Log.w(TAG, "Tunnel already up")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.amnezia.vpn.protocol.wireguard
|
package org.amnezia.vpn.protocol.wireguard
|
||||||
|
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import org.amnezia.vpn.protocol.BadConfigException
|
|
||||||
import org.amnezia.vpn.protocol.ProtocolConfig
|
import org.amnezia.vpn.protocol.ProtocolConfig
|
||||||
import org.amnezia.vpn.util.net.InetEndpoint
|
import org.amnezia.vpn.util.net.InetEndpoint
|
||||||
|
|
||||||
@@ -13,17 +12,7 @@ open class WireguardConfig protected constructor(
|
|||||||
val persistentKeepalive: Int,
|
val persistentKeepalive: Int,
|
||||||
val publicKeyHex: String,
|
val publicKeyHex: String,
|
||||||
val preSharedKeyHex: String?,
|
val preSharedKeyHex: String?,
|
||||||
val privateKeyHex: String,
|
val privateKeyHex: String
|
||||||
val useProtocolExtension: Boolean,
|
|
||||||
val jc: Int?,
|
|
||||||
val jmin: Int?,
|
|
||||||
val jmax: Int?,
|
|
||||||
val s1: Int?,
|
|
||||||
val s2: Int?,
|
|
||||||
val h1: Long?,
|
|
||||||
val h2: Long?,
|
|
||||||
val h3: Long?,
|
|
||||||
val h4: Long?
|
|
||||||
) : ProtocolConfig(protocolConfigBuilder) {
|
) : ProtocolConfig(protocolConfigBuilder) {
|
||||||
|
|
||||||
protected constructor(builder: Builder) : this(
|
protected constructor(builder: Builder) : this(
|
||||||
@@ -32,17 +21,7 @@ open class WireguardConfig protected constructor(
|
|||||||
builder.persistentKeepalive,
|
builder.persistentKeepalive,
|
||||||
builder.publicKeyHex,
|
builder.publicKeyHex,
|
||||||
builder.preSharedKeyHex,
|
builder.preSharedKeyHex,
|
||||||
builder.privateKeyHex,
|
builder.privateKeyHex
|
||||||
builder.useProtocolExtension,
|
|
||||||
builder.jc,
|
|
||||||
builder.jmin,
|
|
||||||
builder.jmax,
|
|
||||||
builder.s1,
|
|
||||||
builder.s2,
|
|
||||||
builder.h1,
|
|
||||||
builder.h2,
|
|
||||||
builder.h3,
|
|
||||||
builder.h4
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
fun toWgUserspaceString(): String = with(StringBuilder()) {
|
||||||
@@ -54,30 +33,6 @@ open class WireguardConfig protected constructor(
|
|||||||
|
|
||||||
open fun appendDeviceLine(sb: StringBuilder) = with(sb) {
|
open fun appendDeviceLine(sb: StringBuilder) = with(sb) {
|
||||||
appendLine("private_key=$privateKeyHex")
|
appendLine("private_key=$privateKeyHex")
|
||||||
if (useProtocolExtension) {
|
|
||||||
validateProtocolExtensionParameters()
|
|
||||||
appendLine("jc=$jc")
|
|
||||||
appendLine("jmin=$jmin")
|
|
||||||
appendLine("jmax=$jmax")
|
|
||||||
appendLine("s1=$s1")
|
|
||||||
appendLine("s2=$s2")
|
|
||||||
appendLine("h1=$h1")
|
|
||||||
appendLine("h2=$h2")
|
|
||||||
appendLine("h3=$h3")
|
|
||||||
appendLine("h4=$h4")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateProtocolExtensionParameters() {
|
|
||||||
if (jc == null) throw BadConfigException("Parameter jc is undefined")
|
|
||||||
if (jmin == null) throw BadConfigException("Parameter jmin is undefined")
|
|
||||||
if (jmax == null) throw BadConfigException("Parameter jmax is undefined")
|
|
||||||
if (s1 == null) throw BadConfigException("Parameter s1 is undefined")
|
|
||||||
if (s2 == null) throw BadConfigException("Parameter s2 is undefined")
|
|
||||||
if (h1 == null) throw BadConfigException("Parameter h1 is undefined")
|
|
||||||
if (h2 == null) throw BadConfigException("Parameter h2 is undefined")
|
|
||||||
if (h3 == null) throw BadConfigException("Parameter h3 is undefined")
|
|
||||||
if (h4 == null) throw BadConfigException("Parameter h4 is undefined")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun appendPeerLine(sb: StringBuilder) = with(sb) {
|
open fun appendPeerLine(sb: StringBuilder) = with(sb) {
|
||||||
@@ -110,18 +65,6 @@ open class WireguardConfig protected constructor(
|
|||||||
|
|
||||||
override var mtu: Int = WIREGUARD_DEFAULT_MTU
|
override var mtu: Int = WIREGUARD_DEFAULT_MTU
|
||||||
|
|
||||||
internal var useProtocolExtension: Boolean = false
|
|
||||||
|
|
||||||
internal var jc: Int? = null
|
|
||||||
internal var jmin: Int? = null
|
|
||||||
internal var jmax: Int? = null
|
|
||||||
internal var s1: Int? = null
|
|
||||||
internal var s2: Int? = null
|
|
||||||
internal var h1: Long? = null
|
|
||||||
internal var h2: Long? = null
|
|
||||||
internal var h3: Long? = null
|
|
||||||
internal var h4: Long? = null
|
|
||||||
|
|
||||||
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
|
||||||
|
|
||||||
fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive }
|
fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive }
|
||||||
@@ -132,18 +75,6 @@ open class WireguardConfig protected constructor(
|
|||||||
|
|
||||||
fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex }
|
fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex }
|
||||||
|
|
||||||
fun setUseProtocolExtension(useProtocolExtension: Boolean) = apply { this.useProtocolExtension = useProtocolExtension }
|
|
||||||
|
|
||||||
fun setJc(jc: Int) = apply { this.jc = jc }
|
|
||||||
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
|
|
||||||
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
|
|
||||||
fun setS1(s1: Int) = apply { this.s1 = s1 }
|
|
||||||
fun setS2(s2: Int) = apply { this.s2 = s2 }
|
|
||||||
fun setH1(h1: Long) = apply { this.h1 = h1 }
|
|
||||||
fun setH2(h2: Long) = apply { this.h2 = h2 }
|
|
||||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
|
||||||
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
|
||||||
|
|
||||||
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
|||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
|
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)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)
|
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/SortFilterProxyModel)
|
||||||
set(LIBS ${LIBS} SortFilterProxyModel)
|
set(LIBS ${LIBS} SortFilterProxyModel)
|
||||||
include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
message("Client ==> MacOS NE build")
|
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
|
||||||
|
|
||||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
|
||||||
|
|
||||||
|
|
||||||
enable_language(OBJC)
|
|
||||||
# enable_language(OBJCXX)
|
|
||||||
enable_language(Swift)
|
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
|
||||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
|
||||||
|
|
||||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
|
||||||
#find_library(FW_UIKIT UIKit)
|
|
||||||
find_library(FW_AVFOUNDATION AVFoundation)
|
|
||||||
find_library(FW_FOUNDATION Foundation)
|
|
||||||
find_library(FW_STOREKIT StoreKit)
|
|
||||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
|
||||||
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
|
||||||
|
|
||||||
set(LIBS ${LIBS}
|
|
||||||
${FW_AUTHENTICATIONSERVICES}
|
|
||||||
# ${FW_UIKIT}
|
|
||||||
${FW_AVFOUNDATION}
|
|
||||||
${FW_FOUNDATION}
|
|
||||||
${FW_STOREKIT}
|
|
||||||
${FW_USERNOTIFICATIONS}
|
|
||||||
${FW_NETWORKEXTENSION}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
|
||||||
)
|
|
||||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
|
|
||||||
|
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
|
||||||
)
|
|
||||||
|
|
||||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
|
||||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
|
||||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
|
||||||
set(SOURCES ${SOURCES} ${ICON_FILE})
|
|
||||||
|
|
||||||
# set(HEADERS ${HEADERS}
|
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
|
||||||
# )
|
|
||||||
|
|
||||||
# set(SOURCES ${SOURCES}
|
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
|
|
||||||
#MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
|
||||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
|
||||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/main.entitlements"
|
|
||||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
|
||||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
|
||||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
|
||||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
|
||||||
XCODE_GENERATE_SCHEME TRUE
|
|
||||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
|
||||||
#XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
|
||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
|
||||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
|
|
||||||
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
|
|
||||||
|
|
||||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
|
||||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
# XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
|
|
||||||
# XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
|
||||||
# XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
|
|
||||||
|
|
||||||
# XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore AmneziaVPN"
|
|
||||||
# XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "Mac AppStore AmneziaVPN"
|
|
||||||
|
|
||||||
)
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
|
|
||||||
)
|
|
||||||
set_target_properties(${PROJECT} PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
|
||||||
)
|
|
||||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
|
||||||
target_compile_options(${PROJECT} PRIVATE
|
|
||||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
|
||||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE
|
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
|
||||||
)
|
|
||||||
|
|
||||||
target_sources(${PROJECT} PRIVATE
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/macos_ne/app/AmneziaVPNLaunchScreen.storyboard
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Media.xcassets
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/PrivacyInfo.xcprivacy
|
|
||||||
)
|
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/macos/app/AmneziaVPNLaunchScreen.storyboard
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Media.xcassets
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/PrivacyInfo.xcprivacy
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(macos/networkextension)
|
|
||||||
add_dependencies(${PROJECT} networkextension)
|
|
||||||
|
|
||||||
# set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
|
||||||
# "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
|
||||||
# target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
|
||||||
|
|
||||||
get_target_property(QtCore_location Qt6::Core LOCATION)
|
|
||||||
message("QtCore_location")
|
|
||||||
message(${QtCore_location})
|
|
||||||
|
|
||||||
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
|
||||||
|
|
||||||
|
|
||||||
# add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
# COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
# SET(SIGN_CMD codesign --deep --force --sign 'Apple Distribution: Privacy Technologies OU \(X7UJ388FXK\)' --timestamp --options runtime $<TARGET_BUNDLE_DIR:AmneziaVPN>)
|
|
||||||
# message("Manual signing bundle...")
|
|
||||||
# message(${SIGN_CMD})
|
|
||||||
|
|
||||||
|
|
||||||
# add_custom_command(TARGET ${PROJECT} POST_BUILD
|
|
||||||
# COMMAND ${SIGN_CMD}
|
|
||||||
# )
|
|
||||||
# endif()
|
|
||||||
@@ -76,7 +76,7 @@ function(osx_bundle_assetcatalog TARGET)
|
|||||||
)
|
)
|
||||||
|
|
||||||
## Patch the asset catalog into the target bundle.
|
## Patch the asset catalog into the target bundle.
|
||||||
if(NOT IOS AND NOT MACOS_NE)
|
if(NOT IOS)
|
||||||
set(XCASSETS_RESOURCE_DIR "Resources")
|
set(XCASSETS_RESOURCE_DIR "Resources")
|
||||||
endif()
|
endif()
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||||
@@ -141,7 +141,6 @@ function(osx_codesign_target TARGET)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
foreach(FILE ${CODESIGN_FILES})
|
foreach(FILE ${CODESIGN_FILES})
|
||||||
message(STATUS "Signing ${TARGET}: ${FILE}")
|
|
||||||
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
add_custom_command(TARGET ${TARGET} POST_BUILD VERBATIM
|
||||||
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
COMMAND ${COMMENT_ECHO_COMMAND} "Signing ${TARGET}: ${FILE}"
|
||||||
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
COMMAND ${CODESIGN_BIN} ${CODESIGN_ARGS} ${FILE}
|
||||||
|
|||||||
@@ -102,9 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
|||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||||
#elif defined(Q_OS_MACX)
|
#elif defined(Q_OS_MACX)
|
||||||
#if !defined(MACOS_NE)
|
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
env.insert("PATH", pathEnvVar);
|
env.insert("PATH", pathEnvVar);
|
||||||
|
|||||||
@@ -357,9 +357,9 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
|
|||||||
|
|
||||||
EVP_PKEY *publicKey = nullptr;
|
EVP_PKEY *publicKey = nullptr;
|
||||||
try {
|
try {
|
||||||
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||||
QSimpleCrypto::QRsa rsa;
|
QSimpleCrypto::QRsa rsa;
|
||||||
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
|
publicKey = rsa.getPublicKeyFromByteArray(key);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
qCritical() << "error loading public key from environment variables";
|
qCritical() << "error loading public key from environment variables";
|
||||||
return ErrorCode::ApiMissingAgwPublicKey;
|
return ErrorCode::ApiMissingAgwPublicKey;
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<device id="ipad12_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="gZ9-gc-3t5">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1024" height="1366"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch.png" translatesAutoresizingMaskIntoConstraints="NO" id="q5g-aV-39U">
|
|
||||||
<rect key="frame" x="467" y="638" width="90" height="90"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="90" id="VFp-nz-h8O"/>
|
|
||||||
<constraint firstAttribute="height" constant="90" id="ZUg-Ud-mgE"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="Whf-X3-AA4"/>
|
|
||||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="q5g-aV-39U" firstAttribute="centerX" secondItem="gZ9-gc-3t5" secondAttribute="centerX" id="Ayw-bo-LVF"/>
|
|
||||||
<constraint firstItem="q5g-aV-39U" firstAttribute="centerY" secondItem="gZ9-gc-3t5" secondAttribute="centerY" id="YHd-Kc-J0u"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
<resources>
|
|
||||||
<image name="launch.png" width="1024" height="1024"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleAllowMixedLocalizations</key>
|
|
||||||
<true/>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>AmneziaVPNLaunchScreen</string>
|
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
|
||||||
<array/>
|
|
||||||
<key>UIRequiresFullScreen</key>
|
|
||||||
<true/>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array/>
|
|
||||||
<key>UIUserInterfaceStyle</key>
|
|
||||||
<string>Light</string>
|
|
||||||
<key>com.wireguard.ios.app_group_id</key>
|
|
||||||
<string>group.org.amnezia.AmneziaVPN</string>
|
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSAllowsLocalNetworking</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>CFBundleIcons</key>
|
|
||||||
<dict/>
|
|
||||||
<key>CFBundleIcons~ipad</key>
|
|
||||||
<dict/>
|
|
||||||
<key>UTImportedTypeDeclarations</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Amnezia VPN config</string>
|
|
||||||
<key>UTTypeIconFiles</key>
|
|
||||||
<array/>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>vpn</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/plain</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>WireGuard config</string>
|
|
||||||
<key>UTTypeIconFiles</key>
|
|
||||||
<array/>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>conf</string>
|
|
||||||
<string>cfg</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/plain</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>OpenVPN config</string>
|
|
||||||
<key>UTTypeIconFiles</key>
|
|
||||||
<array/>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>ovpn</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/plain</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>AmneziaVPN backup file</string>
|
|
||||||
<key>UTTypeIconFiles</key>
|
|
||||||
<array/>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>backup</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/plain</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Amnezia VPN config</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
|
||||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
|
||||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
|
||||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 767 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,290 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "40.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "60.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "29.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "58.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "87.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "80.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "120.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "57.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "57x57"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "114.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "57x57"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "120.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "180.png",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "20.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "40.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "20x20"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "29.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "58.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "40.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "80.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "50.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "50x50"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "100.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "50x50"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "72.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "72x72"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "144.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "72x72"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "76.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "76x76"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "152.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "76x76"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "167.png",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "83.5x83.5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "1024.png",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "notificationCenter",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "24x24",
|
|
||||||
"subtype" : "38mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "notificationCenter",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "27.5x27.5",
|
|
||||||
"subtype" : "42mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "companionSettings",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "companionSettings",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "29x29"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "notificationCenter",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "33x33",
|
|
||||||
"subtype" : "45mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "40x40",
|
|
||||||
"subtype" : "38mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "44x44",
|
|
||||||
"subtype" : "40mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "46x46",
|
|
||||||
"subtype" : "41mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "50x50",
|
|
||||||
"subtype" : "44mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "51x51",
|
|
||||||
"subtype" : "45mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "appLauncher",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "54x54",
|
|
||||||
"subtype" : "49mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "quickLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "86x86",
|
|
||||||
"subtype" : "38mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "quickLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "98x98",
|
|
||||||
"subtype" : "42mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "quickLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "108x108",
|
|
||||||
"subtype" : "44mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "quickLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "117x117",
|
|
||||||
"subtype" : "45mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "quickLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "129x129",
|
|
||||||
"subtype" : "49mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch-marketing",
|
|
||||||
"scale" : "1x",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "car",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "car",
|
|
||||||
"scale" : "3x",
|
|
||||||
"size" : "60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "watch",
|
|
||||||
"role" : "longLook",
|
|
||||||
"scale" : "2x",
|
|
||||||
"size" : "44x44",
|
|
||||||
"subtype" : "42mm"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPITypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>C617.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>1C8F.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>35F9.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
Before Width: | Height: | Size: 583 KiB |
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
|
||||||
<array>
|
|
||||||
<string>packet-tunnel-provider</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>group.org.amnezia.AmneziaVPN</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
|
||||||
<true/>
|
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(AppIdentifierPrefix)group.org.amnezia.AmneziaVPN</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -3,26 +3,40 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.application-identifier</key>
|
<key>com.apple.application-identifier</key>
|
||||||
<string>X7UJ388FXK.org.amnezia.AmneziaVPN.network-extension</string>
|
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
|
||||||
|
|
||||||
<key>com.apple.developer.networking.networkextension</key>
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
<array>
|
<array>
|
||||||
<string>packet-tunnel-provider</string>
|
<string>packet-tunnel-provider</string>
|
||||||
</array>
|
</array>
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>X7UJ388FXK</string>
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>group.org.amnezia.AmneziaVPN</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.server</key>
|
|
||||||
<true/>
|
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)org.amnezia.AmneziaVPN.network-extension</string>
|
<string>$(DEVELOPMENT_TEAM).*</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
<key>com.apple.developer.team-identifier</key>
|
||||||
|
<string>$(DEVELOPMENT_TEAM)</string>
|
||||||
|
|
||||||
|
<key>com.apple.developer.system-extension.install</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.private.network.socket-delegate</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
enable_language(Swift)
|
|
||||||
message("Client message >> macos build >> networkextension")
|
|
||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
|
||||||
|
|
||||||
add_executable(networkextension)
|
|
||||||
|
|
||||||
configure_file(
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/Info.plist.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/Info.plist
|
|
||||||
)
|
|
||||||
|
|
||||||
if(MACOS_NE)
|
|
||||||
message("MACOS_NE is ON")
|
|
||||||
add_definitions(-DQ_OS_IOS)
|
|
||||||
add_definitions(-DMACOS_NE)
|
|
||||||
message("Add macros for MacOS Network Extension")
|
|
||||||
else()
|
|
||||||
message("MACOS_NE is OFF")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
|
||||||
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
|
|
||||||
BUNDLE_EXTENSION appex
|
|
||||||
|
|
||||||
#MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist
|
|
||||||
|
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
|
|
||||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
|
|
||||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}"
|
|
||||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES"
|
|
||||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
|
||||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
# #XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
|
|
||||||
# #XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
|
||||||
# #XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
|
|
||||||
|
|
||||||
# #XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore AmneziaVPN.network-extension"
|
|
||||||
# #XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "Mac AppStore AmneziaVPN.network-extension"
|
|
||||||
|
|
||||||
XCODE_ATTRIBUTE_INFOPLIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
|
|
||||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
|
||||||
#XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
|
||||||
|
|
||||||
#XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution: Privacy Technologies OU (X7UJ388FXK)"
|
|
||||||
#XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
|
||||||
|
|
||||||
|
|
||||||
#XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "Mac AppStore AmneziaVPN.network-extension"
|
|
||||||
#XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "Mac AppStore AmneziaVPN.network-extension"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
set_target_properties(networkextension PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
|
||||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
|
||||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties("networkextension" PROPERTIES
|
|
||||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
|
|
||||||
find_library(FW_MOBILE_CORE MobileCoreServices)
|
|
||||||
find_library(FW_UI_KIT UIKit)
|
|
||||||
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
|
||||||
|
|
||||||
# set(OpenVPNAdapter_DIR "${CLIENT_ROOT_DIR}/3rd/")
|
|
||||||
|
|
||||||
# find_library(OPENVPN_ADAPTER_LIBRARY OpenVPNAdapter PATHS ${OpenVPNAdapter_DIR})
|
|
||||||
# target_link_libraries(networkextension PRIVATE ${OPENVPN_ADAPTER_LIBRARY})
|
|
||||||
|
|
||||||
# add_custom_command(TARGET networkextension PRE_BUILD
|
|
||||||
# COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:networkextension>/../Frameworks
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
# add_custom_command(TARGET networkextension POST_BUILD
|
|
||||||
# COMMAND ${CMAKE_COMMAND} -E echo "Copying ${OPENVPN_ADAPTER_LIBRARY} to $<TARGET_FILE_DIR:networkextension>/../Frameworks/"
|
|
||||||
# COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
# ${OPENVPN_ADAPTER_LIBRARY}
|
|
||||||
# $<TARGET_FILE_DIR:networkextension>/../Frameworks/
|
|
||||||
# COMMAND ${CMAKE_COMMAND} -E echo "Copy complete"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Set the root directory
|
|
||||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
|
||||||
|
|
||||||
# Embedding the framework using CLIENT_ROOT_DIR
|
|
||||||
set_property(TARGET networkextension PROPERTY XCODE_EMBED_FRAMEWORKS
|
|
||||||
"${CLIENT_ROOT_DIR}/3rd/OpenVPNAdapter/build/Release-macos/OpenVPNAdapter.framework"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Setting the framework search paths using CLIENT_ROOT_DIR
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "${CLIENT_ROOT_DIR}/3rd/OpenVPNAdapter/build/Release-macos")
|
|
||||||
|
|
||||||
# Linking the framework using CLIENT_ROOT_DIR
|
|
||||||
target_link_libraries("networkextension" PRIVATE "${CLIENT_ROOT_DIR}/3rd/OpenVPNAdapter/build/Release-macos/OpenVPNAdapter.framework")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#target_link_libraries(networkextension PRIVATE ${FW_ASSETS_LIBRARY})
|
|
||||||
#target_link_libraries(networkextension PRIVATE ${FW_MOBILE_CORE})
|
|
||||||
#target_link_libraries(networkextension PRIVATE ${FW_UI_KIT})
|
|
||||||
target_link_libraries(networkextension PRIVATE ${FW_LIBRESOLV})
|
|
||||||
|
|
||||||
target_compile_options(networkextension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
|
||||||
target_compile_options(networkextension PRIVATE -DNETWORK_EXTENSION=1)
|
|
||||||
|
|
||||||
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
|
|
||||||
|
|
||||||
message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}")
|
|
||||||
message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}")
|
|
||||||
|
|
||||||
target_sources(networkextension PRIVATE
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
|
||||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
|
||||||
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
|
||||||
)
|
|
||||||
|
|
||||||
target_sources(networkextension PRIVATE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
|
||||||
)
|
|
||||||
|
|
||||||
set_property(TARGET networkextension APPEND PROPERTY RESOURCE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
|
||||||
)
|
|
||||||
|
|
||||||
## Build wireguard-go-version.h
|
|
||||||
execute_process(
|
|
||||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
|
||||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
|
||||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
|
||||||
)
|
|
||||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
target_sources(networkextension PRIVATE
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
|
||||||
|
|
||||||
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
|
|
||||||
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/x86_64/libwg-go.a)
|
|
||||||
|
|
||||||
# Print the root directory for debugging purposes
|
|
||||||
message("---------")
|
|
||||||
message(${CLIENT_ROOT_DIR})
|
|
||||||
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
|
||||||
target_link_libraries("networkextension" PRIVATE "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a")
|
|
||||||
|
|
||||||
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
|
||||||
@@ -3,32 +3,27 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>AmneziaVPNNetworkExtension</string>
|
<string>AmneziaVPNNetworkExtension</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>${BUILD_IOS_APP_IDENTIFIER}.network-extension</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>AmneziaVPNNetworkExtension</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>${APPLE_PROJECT_VERSION}</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>${CMAKE_PROJECT_VERSION_TWEAK}</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>AmneziaVPNNetworkExtension</string>
|
|
||||||
|
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
@@ -36,11 +31,5 @@
|
|||||||
<key>NSExtensionPrincipalClass</key>
|
<key>NSExtensionPrincipalClass</key>
|
||||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
<key>com.wireguard.ios.app_group_id</key>
|
|
||||||
<string>group.org.amnezia.AmneziaVPN</string>
|
|
||||||
|
|
||||||
<key>com.wireguard.macos.app_group_id</key>
|
|
||||||
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPITypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>1C8F.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>C617.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "macos/gobridge/wireguard.h"
|
||||||
#include "wireguard-go-version.h"
|
#include "wireguard-go-version.h"
|
||||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -19,8 +23,3 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex);
|
|||||||
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
||||||
|
|
||||||
void write_msg_to_log(const char* tag, const char* msg);
|
void write_msg_to_log(const char* tag, const char* msg);
|
||||||
|
|
||||||
// Khai báo hàm C để Swift có thể sử dụng
|
|
||||||
void hev_socks5_tunnel_quit(void);
|
|
||||||
// Updated function definition in C
|
|
||||||
int hev_socks5_tunnel_main(const char* configFile, int fd);
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#ifndef WIREGUARD_GO_VERSION
|
|
||||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
|
||||||
#endif // WIREGUARD_GO_VERSION
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# go/clangwrap_macos.sh
|
|
||||||
|
|
||||||
# Lấy đường dẫn SDK cho macOS
|
|
||||||
SDK_PATH=`xcrun --sdk macosx --show-sdk-path`
|
|
||||||
|
|
||||||
# Tìm đường dẫn đến `clang` cho macOS
|
|
||||||
CLANG=`xcrun --sdk macosx --find clang`
|
|
||||||
|
|
||||||
# Xác định kiến trúc máy dựa trên biến GOARCH
|
|
||||||
if [ "$GOARCH" == "amd64" ]; then
|
|
||||||
CARCH="x86_64"
|
|
||||||
elif [ "$GOARCH" == "arm64" ]; then
|
|
||||||
CARCH="arm64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Thực thi `clang` với các tùy chọn cụ thể cho macOS
|
|
||||||
exec $CLANG -arch $CARCH -isysroot $SDK_PATH -mmacosx-version-min=10.15 "$@"
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
XCODEBUILD="/usr/bin/xcodebuild"
|
|
||||||
WORKINGDIR=`pwd`
|
|
||||||
PATCH="/usr/bin/patch"
|
|
||||||
|
|
||||||
echo "Building OpenVPNAdapter for macOS Network Extension (MacNE)..."
|
|
||||||
|
|
||||||
# Copy the Project-MacNE.xcconfig settings to amnezia.xcconfig
|
|
||||||
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project-MacNE.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
|
||||||
|
|
||||||
# Append macOS-specific build directory configurations to amnezia.xcconfig
|
|
||||||
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
|
||||||
PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
|
|
||||||
CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-macos
|
|
||||||
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Release-macos
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Exclude UIKit, include Cocoa for macOS
|
|
||||||
# echo "OTHER_LDFLAGS = -framework Cocoa" >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
|
||||||
|
|
||||||
# Fetch the current macOS SDK version dynamically
|
|
||||||
MACOSX_SDK=$(xcrun --sdk macosx --show-sdk-path | sed -E 's/.*MacOSX([0-9]+\.[0-9]+)\.sdk/\1/')
|
|
||||||
|
|
||||||
echo "Using macOS SDK version: $MACOSX_SDK"
|
|
||||||
|
|
||||||
cd 3rd/OpenVPNAdapter
|
|
||||||
|
|
||||||
# Build for macOS using the correct SDK and destination
|
|
||||||
if $XCODEBUILD -scheme OpenVPNAdapter -configuration Release -xcconfig Configuration/amnezia.xcconfig -sdk macosx$MACOSX_SDK -destination 'generic/platform=macOS' -project OpenVPNAdapter.xcodeproj ; then
|
|
||||||
echo "OpenVPNAdapter built successfully for macOS Network Extension (MacNE)"
|
|
||||||
else
|
|
||||||
echo "OpenVPNAdapter macOS Network Extension (MacNE) build failed..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove CodeSignature if needed for macOS
|
|
||||||
rm -rf ./build/Release-macos/OpenVPNAdapter.framework/Versions/A/_CodeSignature
|
|
||||||
|
|
||||||
cd ../../
|
|
||||||
@@ -15,24 +15,13 @@
|
|||||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
|
||||||
bool isAnotherInstanceRunning()
|
|
||||||
{
|
|
||||||
QLocalSocket socket;
|
|
||||||
socket.connectToServer("AmneziaVPNInstance");
|
|
||||||
if (socket.waitForConnected(500)) {
|
|
||||||
qWarning() << "AmneziaVPN is already running";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Migrations migrationsManager;
|
Migrations migrationsManager;
|
||||||
migrationsManager.doMigrations();
|
migrationsManager.doMigrations();
|
||||||
|
|
||||||
|
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
AllowSetForegroundWindow(ASFW_ANY);
|
AllowSetForegroundWindow(ASFW_ANY);
|
||||||
#endif
|
#endif
|
||||||
@@ -43,14 +32,16 @@ int main(int argc, char *argv[])
|
|||||||
qputenv("ANDROID_OPENSSL_SUFFIX", "_3");
|
qputenv("ANDROID_OPENSSL_SUFFIX", "_3");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
|
#else
|
||||||
|
AmneziaApplication app(argc, argv, true,
|
||||||
|
SingleApplication::Mode::User | SingleApplication::Mode::SecondaryNotification);
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
if (!app.isPrimary()) {
|
||||||
if (isAnotherInstanceRunning()) {
|
|
||||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
app.startLocalServer();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Allow to raise app window if secondary instance launched
|
// Allow to raise app window if secondary instance launched
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ extension PacketTunnelProvider {
|
|||||||
let configuration = OpenVPNConfiguration()
|
let configuration = OpenVPNConfiguration()
|
||||||
configuration.fileContent = ovpnConfiguration
|
configuration.fileContent = ovpnConfiguration
|
||||||
if str.contains("cloak") {
|
if str.contains("cloak") {
|
||||||
// configuration.setPTCloak()
|
configuration.setPTCloak()
|
||||||
}
|
}
|
||||||
|
|
||||||
let evaluation: OpenVPNConfigurationEvaluation?
|
let evaluation: OpenVPNConfigurationEvaluation?
|
||||||
|
|||||||
@@ -1,196 +1,10 @@
|
|||||||
//import Foundation
|
|
||||||
//import NetworkExtension
|
|
||||||
//import WireGuardKitGo
|
|
||||||
//
|
|
||||||
//enum XrayErrors: Error {
|
|
||||||
// case noXrayConfig
|
|
||||||
// case xrayConfigIsWrong
|
|
||||||
// case cantSaveXrayConfig
|
|
||||||
// case cantParseListenAndPort
|
|
||||||
// case cantSaveHevSocksConfig
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//extension Constants {
|
|
||||||
// static let cachesDirectory: URL = {
|
|
||||||
// if let cachesDirectoryURL = FileManager.default.urls(for: .cachesDirectory,
|
|
||||||
// in: .userDomainMask).first {
|
|
||||||
// return cachesDirectoryURL
|
|
||||||
// } else {
|
|
||||||
// fatalError("Unable to retrieve caches directory.")
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//extension PacketTunnelProvider {
|
|
||||||
// func startXray(completionHandler: @escaping (Error?) -> Void) {
|
|
||||||
//
|
|
||||||
// // Xray configuration
|
|
||||||
// guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
|
||||||
// let providerConfiguration = protocolConfiguration.providerConfiguration,
|
|
||||||
// let configData = providerConfiguration[Constants.xrayConfigKey] as? Data else {
|
|
||||||
// xrayLog(.error, message: "Can't get xray configuration")
|
|
||||||
// completionHandler(XrayErrors.noXrayConfig)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Tunnel settings
|
|
||||||
// let ipv6Enabled = false
|
|
||||||
// let hideVPNIcon = false
|
|
||||||
//
|
|
||||||
// let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
|
|
||||||
// settings.mtu = 9000
|
|
||||||
//
|
|
||||||
// settings.ipv4Settings = {
|
|
||||||
// let settings = NEIPv4Settings(addresses: ["198.18.0.1"], subnetMasks: ["255.255.0.0"])
|
|
||||||
// settings.includedRoutes = [NEIPv4Route.default()]
|
|
||||||
// return settings
|
|
||||||
// }()
|
|
||||||
//
|
|
||||||
// settings.ipv6Settings = {
|
|
||||||
// guard ipv6Enabled else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// let settings = NEIPv6Settings(addresses: ["fd6e:a81b:704f:1211::1"], networkPrefixLengths: [64])
|
|
||||||
// settings.includedRoutes = [NEIPv6Route.default()]
|
|
||||||
// if hideVPNIcon {
|
|
||||||
// settings.excludedRoutes = [NEIPv6Route(destinationAddress: "::", networkPrefixLength: 128)]
|
|
||||||
// }
|
|
||||||
// return settings
|
|
||||||
// }()
|
|
||||||
//
|
|
||||||
// do {
|
|
||||||
// let xrayConfig = try JSONDecoder().decode(XrayConfig.self,
|
|
||||||
// from: configData)
|
|
||||||
//
|
|
||||||
// var dnsArray = [String]()
|
|
||||||
// if let dns1 = xrayConfig.dns1 {
|
|
||||||
// dnsArray.append(dns1)
|
|
||||||
// }
|
|
||||||
// if let dns2 = xrayConfig.dns2 {
|
|
||||||
// dnsArray.append(dns2)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// settings.dnsSettings = !dnsArray.isEmpty
|
|
||||||
// ? NEDNSSettings(servers: dnsArray)
|
|
||||||
// : NEDNSSettings(servers: ["1.1.1.1"])
|
|
||||||
//
|
|
||||||
// let xrayConfigData = xrayConfig.config.data(using: .utf8)
|
|
||||||
//
|
|
||||||
// guard let xrayConfigData else {
|
|
||||||
// xrayLog(.error, message: "Can't encode config to data")
|
|
||||||
// completionHandler(XrayErrors.xrayConfigIsWrong)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let jsonDict = try JSONSerialization.jsonObject(with: xrayConfigData,
|
|
||||||
// options: []) as? [String: Any]
|
|
||||||
//
|
|
||||||
// guard var jsonDict else {
|
|
||||||
// xrayLog(.error, message: "Can't parse address and port for hevSocks")
|
|
||||||
// completionHandler(XrayErrors.cantParseListenAndPort)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let port = 10808
|
|
||||||
// let address = "::1"
|
|
||||||
//
|
|
||||||
// if var inboundsArray = jsonDict["inbounds"] as? [[String: Any]], !inboundsArray.isEmpty {
|
|
||||||
// inboundsArray[0]["port"] = port
|
|
||||||
// inboundsArray[0]["listen"] = address
|
|
||||||
// jsonDict["inbounds"] = inboundsArray
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let updatedData = try JSONSerialization.data(withJSONObject: jsonDict, options: [])
|
|
||||||
//
|
|
||||||
// setTunnelNetworkSettings(settings) { [weak self] error in
|
|
||||||
// if let error {
|
|
||||||
// completionHandler(error)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Launch xray
|
|
||||||
// self?.setupAndStartXray(configData: updatedData) { xrayError in
|
|
||||||
// if let xrayError {
|
|
||||||
// completionHandler(xrayError)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Launch hevSocks
|
|
||||||
// self?.setupAndRunTun2socks(configData: updatedData,
|
|
||||||
// address: address,
|
|
||||||
// port: port,
|
|
||||||
// completionHandler: completionHandler)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch {
|
|
||||||
// completionHandler(error)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func stopXray(completionHandler: () -> Void) {
|
|
||||||
// Socks5Tunnel.quit()
|
|
||||||
// LibXrayStopXray()
|
|
||||||
// completionHandler()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func setupAndStartXray(configData: Data,
|
|
||||||
// completionHandler: @escaping (Error?) -> Void) {
|
|
||||||
// let path = Constants.cachesDirectory.appendingPathComponent("config.json", isDirectory: false).path
|
|
||||||
// guard FileManager.default.createFile(atPath: path, contents: configData) else {
|
|
||||||
// xrayLog(.error, message: "Can't save xray configuration")
|
|
||||||
// completionHandler(XrayErrors.cantSaveXrayConfig)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// LibXrayRunXray(nil,
|
|
||||||
// path,
|
|
||||||
// Int64.max)
|
|
||||||
//
|
|
||||||
// completionHandler(nil)
|
|
||||||
// xrayLog(.info, message: "Xray started")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func setupAndRunTun2socks(configData: Data,
|
|
||||||
// address: String,
|
|
||||||
// port: Int,
|
|
||||||
// completionHandler: @escaping (Error?) -> Void) {
|
|
||||||
// let config = """
|
|
||||||
// tunnel:
|
|
||||||
// mtu: 9000
|
|
||||||
// socks5:
|
|
||||||
// port: \(port)
|
|
||||||
// address: \(address)
|
|
||||||
// udp: 'udp'
|
|
||||||
// misc:
|
|
||||||
// task-stack-size: 20480
|
|
||||||
// connect-timeout: 5000
|
|
||||||
// read-write-timeout: 60000
|
|
||||||
// log-file: stderr
|
|
||||||
// log-level: error
|
|
||||||
// limit-nofile: 65535
|
|
||||||
// """
|
|
||||||
//
|
|
||||||
// let configurationFilePath = Constants.cachesDirectory.appendingPathComponent("config.yml", isDirectory: false).path
|
|
||||||
// guard FileManager.default.createFile(atPath: configurationFilePath, contents: config.data(using: .utf8)!) else {
|
|
||||||
// xrayLog(.info, message: "Cant save hevSocks configuration")
|
|
||||||
// completionHandler(XrayErrors.cantSaveHevSocksConfig)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// DispatchQueue.global().async {
|
|
||||||
// xrayLog(.info, message: "Hev socks started")
|
|
||||||
// completionHandler(nil)
|
|
||||||
// Socks5Tunnel.run(withConfig: configurationFilePath)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import WireGuardKitGo
|
import WireGuardKitGo
|
||||||
|
|
||||||
enum XrayErrors: Error {
|
enum XrayErrors: Error {
|
||||||
case noXrayConfig
|
case noXrayConfig
|
||||||
|
case xrayConfigIsWrong
|
||||||
case cantSaveXrayConfig
|
case cantSaveXrayConfig
|
||||||
case cantParseListenAndPort
|
case cantParseListenAndPort
|
||||||
case cantSaveHevSocksConfig
|
case cantSaveHevSocksConfig
|
||||||
@@ -213,14 +27,14 @@ extension PacketTunnelProvider {
|
|||||||
// Xray configuration
|
// Xray configuration
|
||||||
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
let xrayConfigData = providerConfiguration[Constants.xrayConfigKey] as? Data else {
|
let configData = providerConfiguration[Constants.xrayConfigKey] as? Data else {
|
||||||
xrayLog(.error, message: "Can't get xray configuration")
|
xrayLog(.error, message: "Can't get xray configuration")
|
||||||
completionHandler(XrayErrors.noXrayConfig)
|
completionHandler(XrayErrors.noXrayConfig)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tunnel settings
|
// Tunnel settings
|
||||||
let ipv6Enabled = true
|
let ipv6Enabled = false
|
||||||
let hideVPNIcon = false
|
let hideVPNIcon = false
|
||||||
|
|
||||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
|
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "254.1.1.1")
|
||||||
@@ -244,12 +58,29 @@ extension PacketTunnelProvider {
|
|||||||
return settings
|
return settings
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let dns = ["8.8.4.4","1.1.1.1"]
|
|
||||||
settings.dnsSettings = NEDNSSettings(servers: dns)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let port = 10808
|
let xrayConfig = try JSONDecoder().decode(XrayConfig.self,
|
||||||
let address = "::1"
|
from: configData)
|
||||||
|
|
||||||
|
var dnsArray = [String]()
|
||||||
|
if let dns1 = xrayConfig.dns1 {
|
||||||
|
dnsArray.append(dns1)
|
||||||
|
}
|
||||||
|
if let dns2 = xrayConfig.dns2 {
|
||||||
|
dnsArray.append(dns2)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.dnsSettings = !dnsArray.isEmpty
|
||||||
|
? NEDNSSettings(servers: dnsArray)
|
||||||
|
: NEDNSSettings(servers: ["1.1.1.1"])
|
||||||
|
|
||||||
|
let xrayConfigData = xrayConfig.config.data(using: .utf8)
|
||||||
|
|
||||||
|
guard let xrayConfigData else {
|
||||||
|
xrayLog(.error, message: "Can't encode config to data")
|
||||||
|
completionHandler(XrayErrors.xrayConfigIsWrong)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let jsonDict = try JSONSerialization.jsonObject(with: xrayConfigData,
|
let jsonDict = try JSONSerialization.jsonObject(with: xrayConfigData,
|
||||||
options: []) as? [String: Any]
|
options: []) as? [String: Any]
|
||||||
@@ -260,6 +91,9 @@ extension PacketTunnelProvider {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let port = 10808
|
||||||
|
let address = "::1"
|
||||||
|
|
||||||
if var inboundsArray = jsonDict["inbounds"] as? [[String: Any]], !inboundsArray.isEmpty {
|
if var inboundsArray = jsonDict["inbounds"] as? [[String: Any]], !inboundsArray.isEmpty {
|
||||||
inboundsArray[0]["port"] = port
|
inboundsArray[0]["port"] = port
|
||||||
inboundsArray[0]["listen"] = address
|
inboundsArray[0]["listen"] = address
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#if !MACOS_NE
|
|
||||||
#include "QRCodeReaderBase.h"
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
@@ -109,19 +108,3 @@ void QRCodeReader::startReading() {
|
|||||||
void QRCodeReader::stopReading() {
|
void QRCodeReader::stopReading() {
|
||||||
[m_qrCodeReader stopReading];
|
[m_qrCodeReader stopReading];
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#include "QRCodeReaderBase.h"
|
|
||||||
|
|
||||||
QRCodeReader::QRCodeReader()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect QRCodeReader::cameraSize() {
|
|
||||||
return QRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QRCodeReader::startReading() {}
|
|
||||||
void QRCodeReader::stopReading() {}
|
|
||||||
void QRCodeReader::setCameraSize(QRect) {}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#if !MACOS_NE
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#endif
|
|
||||||
@interface QIOSApplicationDelegate
|
@interface QIOSApplicationDelegate
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
||||||
#if !MACOS_NE
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
||||||
@@ -57,5 +57,5 @@
|
|||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
#if MACOS_NE
|
|
||||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScreenProtection {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||||
@@ -95,4 +85,3 @@ struct ProtectionPair {
|
|||||||
textField.removeFromSuperview()
|
textField.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const char* MessageKey::isOnDemand = "is-on-demand";
|
|||||||
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||||
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||||
|
|
||||||
#if !MACOS_NE
|
|
||||||
static UIViewController* getViewController() {
|
static UIViewController* getViewController() {
|
||||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||||
for (UIWindow *window in windows) {
|
for (UIWindow *window in windows) {
|
||||||
@@ -37,7 +36,6 @@ static UIViewController* getViewController() {
|
|||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -501,20 +499,6 @@ bool IosController::setupWireGuard()
|
|||||||
wgConfig.insert(config_key::persistent_keep_alive, "25");
|
wgConfig.insert(config_key::persistent_keep_alive, "25");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.contains(config_key::isObfuscationEnabled) && config.value(config_key::isObfuscationEnabled).toBool()) {
|
|
||||||
wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]);
|
|
||||||
wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]);
|
|
||||||
wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]);
|
|
||||||
wgConfig.insert(config_key::transportPacketMagicHeader, config[config_key::transportPacketMagicHeader]);
|
|
||||||
|
|
||||||
wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]);
|
|
||||||
wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]);
|
|
||||||
|
|
||||||
wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]);
|
|
||||||
wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]);
|
|
||||||
wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonDocument wgConfigDoc(wgConfig);
|
QJsonDocument wgConfigDoc(wgConfig);
|
||||||
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
@@ -791,14 +775,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||||
[sharingItems addObject:logFileUrl];
|
[sharingItems addObject:logFileUrl];
|
||||||
}
|
}
|
||||||
#if !MACOS_NE
|
|
||||||
UIViewController *qtController = getViewController();
|
UIViewController *qtController = getViewController();
|
||||||
if (!qtController) return;
|
if (!qtController) return;
|
||||||
|
|
||||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||||
#endif
|
|
||||||
__block bool isAccepted = false;
|
__block bool isAccepted = false;
|
||||||
#if !MACOS_NE
|
|
||||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||||
isAccepted = completed;
|
isAccepted = completed;
|
||||||
emit finished();
|
emit finished();
|
||||||
@@ -810,7 +794,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
popController.sourceView = qtController.view;
|
popController.sourceView = qtController.view;
|
||||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
@@ -819,7 +803,6 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString IosController::openFile() {
|
QString IosController::openFile() {
|
||||||
#if !MACOS_NE
|
|
||||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||||
|
|
||||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||||
@@ -829,9 +812,9 @@ QString IosController::openFile() {
|
|||||||
if (!qtController) return;
|
if (!qtController) return;
|
||||||
|
|
||||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||||
#endif
|
|
||||||
__block QString filePath;
|
__block QString filePath;
|
||||||
#if !MACOS_NE
|
|
||||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||||
if (path) {
|
if (path) {
|
||||||
filePath = QString::fromUtf8(path.UTF8String);
|
filePath = QString::fromUtf8(path.UTF8String);
|
||||||
@@ -840,7 +823,7 @@ QString IosController::openFile() {
|
|||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
QEventLoop wait;
|
QEventLoop wait;
|
||||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||||
wait.exec();
|
wait.exec();
|
||||||
@@ -866,4 +849,3 @@ void IosController::requestInetAccess() {
|
|||||||
}];
|
}];
|
||||||
[task resume];
|
[task resume];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
#import <NetworkExtension/NetworkExtension.h>
|
#import <NetworkExtension/NetworkExtension.h>
|
||||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#if !MACOS_NE
|
|
||||||
#include <UIKit/UIKit.h>
|
#include <UIKit/UIKit.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
|
|
||||||
class IosController;
|
class IosController;
|
||||||
@@ -21,10 +17,9 @@ class IosController;
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||||
#if !MACOS_NE
|
|
||||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||||
|
|
||||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#if !MACOS_NE
|
|
||||||
@implementation DocumentPickerDelegate
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
@@ -44,4 +43,3 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#import <UserNotifications/UserNotifications.h>
|
#import <UserNotifications/UserNotifications.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#if !MACOS_NE
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface IOSNotificationDelegate
|
@interface IOSNotificationDelegate
|
||||||
@@ -89,86 +87,3 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
|
|
||||||
// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS
|
|
||||||
@interface IOSNotificationDelegate
|
|
||||||
: NSObject <UNUserNotificationCenterDelegate> {
|
|
||||||
IOSNotificationHandler* m_iosNotificationHandler;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation IOSNotificationDelegate
|
|
||||||
|
|
||||||
- (id)initWithObject:(IOSNotificationHandler*)notification {
|
|
||||||
self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific
|
|
||||||
if (self) {
|
|
||||||
m_iosNotificationHandler = notification;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
|
||||||
willPresentNotification:(UNNotification*)notification
|
|
||||||
withCompletionHandler:
|
|
||||||
(void (^)(UNNotificationPresentationOptions options))completionHandler {
|
|
||||||
Q_UNUSED(center)
|
|
||||||
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
|
||||||
didReceiveNotificationResponse:(UNNotificationResponse*)response
|
|
||||||
withCompletionHandler:(void (^)())completionHandler {
|
|
||||||
Q_UNUSED(center)
|
|
||||||
Q_UNUSED(response)
|
|
||||||
completionHandler();
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {
|
|
||||||
|
|
||||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
|
||||||
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
|
|
||||||
UNAuthorizationOptionBadge)
|
|
||||||
completionHandler:^(BOOL granted, NSError* _Nullable error) {
|
|
||||||
Q_UNUSED(granted);
|
|
||||||
if (!error) {
|
|
||||||
m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
IOSNotificationHandler::~IOSNotificationHandler() { }
|
|
||||||
|
|
||||||
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
|
||||||
const QString& message, int timerMsec) {
|
|
||||||
Q_UNUSED(type);
|
|
||||||
|
|
||||||
if (!m_delegate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
|
||||||
content.title = title.toNSString();
|
|
||||||
content.body = message.toNSString();
|
|
||||||
content.sound = [UNNotificationSound defaultSound];
|
|
||||||
|
|
||||||
int timerSec = timerMsec / 1000;
|
|
||||||
UNTimeIntervalNotificationTrigger* trigger =
|
|
||||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
|
||||||
|
|
||||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
|
||||||
content:content
|
|
||||||
trigger:trigger];
|
|
||||||
|
|
||||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
|
||||||
center.delegate = (id<UNUserNotificationCenterDelegate>)m_delegate;
|
|
||||||
|
|
||||||
[center addNotificationRequest:request
|
|
||||||
withCompletionHandler:^(NSError* _Nullable error) {
|
|
||||||
if (error) {
|
|
||||||
NSLog(@"Local Notification failed");
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ namespace amnezia
|
|||||||
constexpr char last_config[] = "last_config";
|
constexpr char last_config[] = "last_config";
|
||||||
|
|
||||||
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
|
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
|
||||||
constexpr char isObfuscationEnabled[] = "isObfuscationEnabled";
|
|
||||||
|
|
||||||
constexpr char junkPacketCount[] = "Jc";
|
constexpr char junkPacketCount[] = "Jc";
|
||||||
constexpr char junkPacketMinSize[] = "Jmin";
|
constexpr char junkPacketMinSize[] = "Jmin";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
#include "openvpnovercloakprotocol.h"
|
#include "openvpnovercloakprotocol.h"
|
||||||
#include "openvpnprotocol.h"
|
#include "openvpnprotocol.h"
|
||||||
#include "shadowsocksvpnprotocol.h"
|
#include "shadowsocksvpnprotocol.h"
|
||||||
@@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
|||||||
#if defined(Q_OS_WINDOWS)
|
#if defined(Q_OS_WINDOWS)
|
||||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ private:
|
|||||||
mutable SecureQSettings m_settings;
|
mutable SecureQSettings m_settings;
|
||||||
|
|
||||||
QString m_gatewayEndpoint;
|
QString m_gatewayEndpoint;
|
||||||
bool m_isDevGatewayEnv = false;
|
bool m_isDevGatewayEnv;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGS_H
|
#endif // SETTINGS_H
|
||||||
|
|||||||
@@ -258,18 +258,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>غير قادر علي فتح الملف</translation>
|
<translation>غير قادر علي فتح الملف</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>ملف تكوين غير صحيح</translation>
|
<translation>ملف تكوين غير صحيح</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>تم فحص%1 من %2.</translation>
|
<translation>تم فحص%1 من %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation>في التكوين المستورد، تم العثور على سطور يحتمل أن تكون خطرة:</translation>
|
<translation>في التكوين المستورد، تم العثور على سطور يحتمل أن تكون خطرة:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -401,28 +401,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>تم الاتصال</translation>
|
<translation>تم الاتصال</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>تم إنهاء الاتصال</translation>
|
<translation>تم إنهاء الاتصال</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>إشعار من AmneziaVPN</translation>
|
<translation>إشعار من AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>تم العثور علي شبكة غير مؤمنة: </translation>
|
<translation>تم العثور علي شبكة غير مؤمنة: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2426,12 +2426,12 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>عنوان خادم IP [:منفذ]</translation>
|
<translation>عنوان خادم IP [:منفذ]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>واصل</translation>
|
<translation>واصل</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>ستظل جميع البيانات التي تدخلها سرية للغاية ولن تتم مشاركتها أو الكشف عنها ل Amnezia أو أي طرف ثالث</translation>
|
<translation>ستظل جميع البيانات التي تدخلها سرية للغاية ولن تتم مشاركتها أو الكشف عنها ل Amnezia أو أي طرف ثالث</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2441,42 +2441,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>كلمة مرور او مفتاح SSH خاص</translation>
|
<translation>كلمة مرور او مفتاح SSH خاص</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation>كيف تقوم بتشغيل خادم ال VPN الخاص بك</translation>
|
<translation>كيف تقوم بتشغيل خادم ال VPN الخاص بك</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation>اين تحصل علي بيانات الاتصال, تعليمات خطوة ب خطوة لشراء VPS</translation>
|
<translation>اين تحصل علي بيانات الاتصال, تعليمات خطوة ب خطوة لشراء VPS</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>لا يمكن لعنوان IP ان يكون فارغ</translation>
|
<translation>لا يمكن لعنوان IP ان يكون فارغ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>ادخل العنوان في شكل 255.255.255.255:88</translation>
|
<translation>ادخل العنوان في شكل 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>تسجيل دخول لا يمكن ان يكون فارغ</translation>
|
<translation>تسجيل دخول لا يمكن ان يكون فارغ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>كلمة مرور/مفتاح خاص لأ يمكن ان يكونو فارغين</translation>
|
<translation>كلمة مرور/مفتاح خاص لأ يمكن ان يكونو فارغين</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2791,47 +2791,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>البيانات المٌرسلة: %1</translation>
|
<translation>البيانات المٌرسلة: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>إعادة التسمية</translation>
|
<translation>إعادة التسمية</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>اسم العميل</translation>
|
<translation>اسم العميل</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>احفظ</translation>
|
<translation>احفظ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>سحب وإبطال</translation>
|
<translation>سحب وإبطال</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>سحب وإبطال للمستخدم - %1?</translation>
|
<translation>سحب وإبطال للمستخدم - %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>المستخدم لن يكون قادر علي الاتصال بعد الان.</translation>
|
<translation>المستخدم لن يكون قادر علي الاتصال بعد الان.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>واصل</translation>
|
<translation>واصل</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>إلغاء</translation>
|
<translation>إلغاء</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4105,12 +4100,12 @@ While it offers a blend of security, stability, and speed, it's essential t
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>عبارة المرور الخاصة بالمفتاح</translation>
|
<translation>عبارة المرور الخاصة بالمفتاح</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>احفظ</translation>
|
<translation>احفظ</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -263,18 +263,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>نمیتوان فایل را باز کرد.</translation>
|
<translation>نمیتوان فایل را باز کرد.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>فایل پیکربندی نامعتبر است.</translation>
|
<translation>فایل پیکربندی نامعتبر است.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>ارزیابی %1 از %2.</translation>
|
<translation>ارزیابی %1 از %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation>در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند:</translation>
|
<translation>در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -405,28 +405,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>ویپیان وصل شد</translation>
|
<translation>ویپیان وصل شد</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>ویپیان قطع شد</translation>
|
<translation>ویپیان قطع شد</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>اخطار AmneziaVPN</translation>
|
<translation>اخطار AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>شبکه ناامن شناسایی شد: </translation>
|
<translation>شبکه ناامن شناسایی شد: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2544,12 +2544,12 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<translation>آدرس آیپی سرور (:پورت)</translation>
|
<translation>آدرس آیپی سرور (:پورت)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>ادامه</translation>
|
<translation>ادامه</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>آدرس را با فرمت 255.255.255.255:88 وارد کنید</translation>
|
<translation>آدرس را با فرمت 255.255.255.255:88 وارد کنید</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2564,42 +2564,42 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>رمز عبور یا کلید خصوصی SSH</translation>
|
<translation>رمز عبور یا کلید خصوصی SSH</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>تمام دادههایی که شما وارد میکنید به شدت محرمانه است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمیشود</translation>
|
<translation>تمام دادههایی که شما وارد میکنید به شدت محرمانه است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمیشود</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation>چگونه سرور VPN خود را اجرا کنید</translation>
|
<translation>چگونه سرور VPN خود را اجرا کنید</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation>دادههای اتصال را از کجا دریافت کنید و دستورالعملهای مرحله به مرحله برای خرید یک VPS</translation>
|
<translation>دادههای اتصال را از کجا دریافت کنید و دستورالعملهای مرحله به مرحله برای خرید یک VPS</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>آدرس آیپی نمیتواند خالی باشد</translation>
|
<translation>آدرس آیپی نمیتواند خالی باشد</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>نامکاربری نمیتواند خالی باشد</translation>
|
<translation>نامکاربری نمیتواند خالی باشد</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>پسورد یا کلید خصوصی نمیتواند خالی باشد</translation>
|
<translation>پسورد یا کلید خصوصی نمیتواند خالی باشد</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2958,52 +2958,47 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation>دادههای ارسال شده: %1</translation>
|
<translation>دادههای ارسال شده: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="vanished">تاریخ ایجاد: </translation>
|
<translation type="vanished">تاریخ ایجاد: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>تغییر نام</translation>
|
<translation>تغییر نام</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>نام کلاینت</translation>
|
<translation>نام کلاینت</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>ذخیره</translation>
|
<translation>ذخیره</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>ابطال</translation>
|
<translation>ابطال</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>لغو پیکربندی برای یک کاربر - %1?</translation>
|
<translation>لغو پیکربندی برای یک کاربر - %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>کاربر دیگر نمیتواند به سرور وصل شود.</translation>
|
<translation>کاربر دیگر نمیتواند به سرور وصل شود.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>ادامه</translation>
|
<translation>ادامه</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>کنسل</translation>
|
<translation>کنسل</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4321,12 +4316,12 @@ For more detailed information, you can
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>عبارت کلید خصوصی</translation>
|
<translation>عبارت کلید خصوصی</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>ذخیره</translation>
|
<translation>ذخیره</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -259,18 +259,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>फाइल खोलने में असमर्थ</translation>
|
<translation>फाइल खोलने में असमर्थ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>अमान्य कॉन्फ़िगरेशन फ़ाइल</translation>
|
<translation>अमान्य कॉन्फ़िगरेशन फ़ाइल</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>%2 में से %1 स्कैन किया गया.</translation>
|
<translation>%2 में से %1 स्कैन किया गया.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -401,28 +401,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>कनेक्ट</translation>
|
<translation>कनेक्ट</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>कनेक्ट</translation>
|
<translation>कनेक्ट</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>AmneziaVPN अधिसूचना</translation>
|
<translation>AmneziaVPN अधिसूचना</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>असुरक्षित नेटवर्क का पता चला: </translation>
|
<translation>असुरक्षित नेटवर्क का पता चला: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2494,12 +2494,12 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>सर्वर आईपी पता [:पोर्ट]</translation>
|
<translation>सर्वर आईपी पता [:पोर्ट]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>जारी रखना</translation>
|
<translation>जारी रखना</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>आपके द्वारा दर्ज किया गया सभी डेटा पूरी तरह से गोपनीय रहेगा और एमनेज़िया या किसी तीसरे पक्ष को साझा या प्रकट नहीं किया जाएगा</translation>
|
<translation>आपके द्वारा दर्ज किया गया सभी डेटा पूरी तरह से गोपनीय रहेगा और एमनेज़िया या किसी तीसरे पक्ष को साझा या प्रकट नहीं किया जाएगा</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2509,42 +2509,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>255.255.255.255:22</translation>
|
<translation>255.255.255.255:22</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>SSH उपयोगकर्ता नाम</translation>
|
<translation>SSH उपयोगकर्ता नाम</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>पासवर्ड या SSH निजी कुंजी</translation>
|
<translation>पासवर्ड या SSH निजी कुंजी</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>आईपी पता खाली नहीं हो सकता</translation>
|
<translation>आईपी पता खाली नहीं हो सकता</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>पता 255.255.255.255:88 प्रारूप में दर्ज करें</translation>
|
<translation>पता 255.255.255.255:88 प्रारूप में दर्ज करें</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>लॉगिन खाली नहीं हो सकता</translation>
|
<translation>लॉगिन खाली नहीं हो सकता</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>पासवर्ड/निजी कुंजी खाली नहीं हो सकती</translation>
|
<translation>पासवर्ड/निजी कुंजी खाली नहीं हो सकती</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2876,52 +2876,47 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation>डेटा भेजा गया: %1</translation>
|
<translation>डेटा भेजा गया: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="vanished">निर्माण तिथि: </translation>
|
<translation type="vanished">निर्माण तिथि: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>नाम बदलें</translation>
|
<translation>नाम बदलें</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>ग्राहक नाम</translation>
|
<translation>ग्राहक नाम</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>सहेजें</translation>
|
<translation>सहेजें</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>निरस्त करें</translation>
|
<translation>निरस्त करें</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>किसी उपयोक्ता के लिए कॉन्फ़िगरेशन निरस्त करें - %1?</translation>
|
<translation>किसी उपयोक्ता के लिए कॉन्फ़िगरेशन निरस्त करें - %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>उपयोगकर्ता अब आपके सर्वर से कनेक्ट नहीं हो पाएगा.</translation>
|
<translation>उपयोगकर्ता अब आपके सर्वर से कनेक्ट नहीं हो पाएगा.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>जारी रखना</translation>
|
<translation>जारी रखना</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>रद्द करना</translation>
|
<translation>रद्द करना</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4213,12 +4208,12 @@ While it offers a blend of security, stability, and speed, it's essential t
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>निजी कुंजी पासफ़्रेज़</translation>
|
<translation>निजी कुंजी पासफ़्रेज़</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>सहेजें</translation>
|
<translation>सहेजें</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -259,18 +259,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>ဖိုင်ကိုဖွင့်၍မရပါ</translation>
|
<translation>ဖိုင်ကိုဖွင့်၍မရပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>Configuration ဖိုင် မမှန်ကန်ပါ</translation>
|
<translation>Configuration ဖိုင် မမှန်ကန်ပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>%2 ၏ %1 ကို စကင်န်ဖတ်ထားသည်.</translation>
|
<translation>%2 ၏ %1 ကို စကင်န်ဖတ်ထားသည်.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation>တင်သွင်းသည့် configuration တွင်၊ အန္တရာယ်ရှိနိုင်သည့်စာလိုင်းများကို တွေ့ရှိခဲ့သည်:</translation>
|
<translation>တင်သွင်းသည့် configuration တွင်၊ အန္တရာယ်ရှိနိုင်သည့်စာလိုင်းများကို တွေ့ရှိခဲ့သည်:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -401,28 +401,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>VPN ချိတ်ဆက်ထားပါပြီ</translation>
|
<translation>VPN ချိတ်ဆက်ထားပါပြီ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>VPN ဖြုတ်လိုက်ပါပြီ</translation>
|
<translation>VPN ဖြုတ်လိုက်ပါပြီ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>AmneziaVPN နိုတီ</translation>
|
<translation>AmneziaVPN နိုတီ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>လုံခြုံမှုမရှိသောကွန်ရက်မှန်း ထောက်လှန်းမိသည်: </translation>
|
<translation>လုံခြုံမှုမရှိသောကွန်ရက်မှန်း ထောက်လှန်းမိသည်: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2430,12 +2430,12 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>ဆာဗာ IP လိပ်စာ [:port]</translation>
|
<translation>ဆာဗာ IP လိပ်စာ [:port]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>ဆက်လက်လုပ်ဆောင်မည်</translation>
|
<translation>ဆက်လက်လုပ်ဆောင်မည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>လိပ်စာကို 255.255.255.255:88 ဖော်မတ်ဖြင့် ထည့်ပါ</translation>
|
<translation>လိပ်စာကို 255.255.255.255:88 ဖော်မတ်ဖြင့် ထည့်ပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2450,42 +2450,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>255.255.255.255:22</translation>
|
<translation>255.255.255.255:22</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>SSH အသုံးပြုသူအမည်</translation>
|
<translation>SSH အသုံးပြုသူအမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>စကားဝှက် သိုမဟုတ် SSH private key</translation>
|
<translation>စကားဝှက် သိုမဟုတ် SSH private key</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>သင်ထည့်သွင်းသည့်ဒေတာအားလုံးကို တင်းကြပ်လုံခြုံစွာလျှို့ဝှက်ထားမည်ဖြစ်ပြီး Amnezia သို့မဟုတ် မည်သည့်ပြင်ပအဖွဲ့အစည်းကိုမျှ မျှဝေမည် သို့မဟုတ် ထုတ်ဖော်မည်မဟုတ်ပါ</translation>
|
<translation>သင်ထည့်သွင်းသည့်ဒေတာအားလုံးကို တင်းကြပ်လုံခြုံစွာလျှို့ဝှက်ထားမည်ဖြစ်ပြီး Amnezia သို့မဟုတ် မည်သည့်ပြင်ပအဖွဲ့အစည်းကိုမျှ မျှဝေမည် သို့မဟုတ် ထုတ်ဖော်မည်မဟုတ်ပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation>သင်၏ဆာဗာကို လည်ပတ်ပုံလည်ပတ်နည်း</translation>
|
<translation>သင်၏ဆာဗာကို လည်ပတ်ပုံလည်ပတ်နည်း</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation>ချိတ်ဆက်မှုဒေတာကို ဘယ်မှာရနိုင်မလဲ၊ VPS ဝယ်ယူပုံဝယ်ယူနည်းအတွက် အဆင့်ဆင့် ညွှန်ကြားချက်များ</translation>
|
<translation>ချိတ်ဆက်မှုဒေတာကို ဘယ်မှာရနိုင်မလဲ၊ VPS ဝယ်ယူပုံဝယ်ယူနည်းအတွက် အဆင့်ဆင့် ညွှန်ကြားချက်များ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>IP လိပ်စာသည် ဗလာမဖြစ်ရပါ</translation>
|
<translation>IP လိပ်စာသည် ဗလာမဖြစ်ရပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>လော့ဂ်အင်အချက်အလက်သည် ဗလာမဖြစ်ရပါ</translation>
|
<translation>လော့ဂ်အင်အချက်အလက်သည် ဗလာမဖြစ်ရပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>စကားဝှက်/private key သည် ဗလာမဖြစ်ရပါ</translation>
|
<translation>စကားဝှက်/private key သည် ဗလာမဖြစ်ရပါ</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2825,47 +2825,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>ပေးပို့လိုက်သည့်ဒေတာ: %1</translation>
|
<translation>ပေးပို့လိုက်သည့်ဒေတာ: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>အမည်ပြောင်းမည်</translation>
|
<translation>အမည်ပြောင်းမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>ကလိုင်းရင့်အမည်</translation>
|
<translation>ကလိုင်းရင့်အမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>သိမ်းဆည်းမည်</translation>
|
<translation>သိမ်းဆည်းမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>ပြန်ရုပ်သိမ်းမည်</translation>
|
<translation>ပြန်ရုပ်သိမ်းမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>အသုံးပြုသူ %1 အတွက် config ကို ပြန်လည်ရုပ်သိမ်းမည်လား?</translation>
|
<translation>အသုံးပြုသူ %1 အတွက် config ကို ပြန်လည်ရုပ်သိမ်းမည်လား?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>ဤအသုံးပြုသူသည် သင့်ဆာဗာသို့ ချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ.</translation>
|
<translation>ဤအသုံးပြုသူသည် သင့်ဆာဗာသို့ ချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>ဆက်လက်လုပ်ဆောင်မည်</translation>
|
<translation>ဆက်လက်လုပ်ဆောင်မည်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>ပယ်ဖျက်မည်</translation>
|
<translation>ပယ်ဖျက်မည်</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4110,12 +4105,12 @@ For more detailed information, you can
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>ကိုယ်ပိုင် key စကားဝှက်</translation>
|
<translation>ကိုယ်ပိုင် key စကားဝှက်</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>သိမ်းဆည်းမည်</translation>
|
<translation>သိမ်းဆည်းမည်</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -263,18 +263,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>Невозможно открыть файл</translation>
|
<translation>Невозможно открыть файл</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>Неверный файл конфигурации</translation>
|
<translation>Неверный файл конфигурации</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>Отсканировано %1 из %2.</translation>
|
<translation>Отсканировано %1 из %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation>В импортированной конфигурации были обнаружены потенциально опасные строки:</translation>
|
<translation>В импортированной конфигурации были обнаружены потенциально опасные строки:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -405,28 +405,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>VPN подключен</translation>
|
<translation>VPN подключен</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>VPN выключен</translation>
|
<translation>VPN выключен</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>Уведомление AmneziaVPN</translation>
|
<translation>Уведомление AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>Обнаружена незащищенная сеть: </translation>
|
<translation>Обнаружена незащищенная сеть: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2628,7 +2628,7 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<translation type="vanished">Password / SSH private key</translation>
|
<translation type="vanished">Password / SSH private key</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продолжить</translation>
|
<translation>Продолжить</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2638,7 +2638,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<translation type="vanished">Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам</translation>
|
<translation type="vanished">Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>Введите адрес в формате 255.255.255.255:88</translation>
|
<translation>Введите адрес в формате 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2657,42 +2657,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<translation>255.255.255.255:22</translation>
|
<translation>255.255.255.255:22</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>Имя пользователя SSH</translation>
|
<translation>Имя пользователя SSH</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>Пароль или закрытый ключ SSH</translation>
|
<translation>Пароль или закрытый ключ SSH</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам</translation>
|
<translation>Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation>Как создать VPN на собственном сервере</translation>
|
<translation>Как создать VPN на собственном сервере</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation>Где взять данные для подключения, пошаговые инстуркции по покупке VPS</translation>
|
<translation>Где взять данные для подключения, пошаговые инстуркции по покупке VPS</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>Поле с IP-адресом не может быть пустым</translation>
|
<translation>Поле с IP-адресом не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>Поле с логином не может быть пустым</translation>
|
<translation>Поле с логином не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>Поле с паролем/закрытым ключом не может быть пустым</translation>
|
<translation>Поле с паролем/закрытым ключом не может быть пустым</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3087,52 +3087,47 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation>Отправлено данных: %1</translation>
|
<translation>Отправлено данных: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="vanished">Дата создания: </translation>
|
<translation type="vanished">Дата создания: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>Переименовать</translation>
|
<translation>Переименовать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>Имя клиента</translation>
|
<translation>Имя клиента</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Сохранить</translation>
|
<translation>Сохранить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>Отозвать</translation>
|
<translation>Отозвать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>Отозвать конфигурацию для пользователя - %1?</translation>
|
<translation>Отозвать конфигурацию для пользователя - %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>Пользователь больше не сможет подключаться к вашему серверу.</translation>
|
<translation>Пользователь больше не сможет подключаться к вашему серверу.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продолжить</translation>
|
<translation>Продолжить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Отменить</translation>
|
<translation>Отменить</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4552,12 +4547,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>Парольная фраза для закрытого ключа</translation>
|
<translation>Парольная фраза для закрытого ключа</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Сохранить</translation>
|
<translation>Сохранить</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -290,18 +290,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>Неможливо відкрити файл</translation>
|
<translation>Неможливо відкрити файл</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>Недійсний файл конфігурації</translation>
|
<translation>Недійсний файл конфігурації</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>Відскановано %1 з %2.</translation>
|
<translation>Відскановано %1 з %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation>У імпортованій конфігурації знайдено потенційно небезпечні рядки:</translation>
|
<translation>У імпортованій конфігурації знайдено потенційно небезпечні рядки:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -431,28 +431,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>AmneziaVPN</translation>
|
<translation>AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>VPN Підключено</translation>
|
<translation>VPN Підключено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>VPN Вимкнено</translation>
|
<translation>VPN Вимкнено</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>Сповіщення AmneziaVPN</translation>
|
<translation>Сповіщення AmneziaVPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>Знайдена не захищена мережа: </translation>
|
<translation>Знайдена не захищена мережа: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2714,7 +2714,7 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<translation type="vanished">Password / SSH private key</translation>
|
<translation type="vanished">Password / SSH private key</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продовжити</translation>
|
<translation>Продовжити</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2725,7 +2725,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
і не будуть передані чи розголошені Amnezia або будь-яким третім особам</translation>
|
і не будуть передані чи розголошені Amnezia або будь-яким третім особам</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>Введіть адресу в форматі 255.255.255.255:88</translation>
|
<translation>Введіть адресу в форматі 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2744,42 +2744,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<translation>255.255.255.255:22</translation>
|
<translation>255.255.255.255:22</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>SSH Username</translation>
|
<translation>SSH Username</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>Пароль або SSH ключ</translation>
|
<translation>Пароль або SSH ключ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>Усі дані, які ви вводите, залишатимуться суворо конфіденційними та не будуть передані чи розголошені Amnezia або будь-яким третім особам</translation>
|
<translation>Усі дані, які ви вводите, залишатимуться суворо конфіденційними та не будуть передані чи розголошені Amnezia або будь-яким третім особам</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation>Як запустити ваш VPN-сервер</translation>
|
<translation>Як запустити ваш VPN-сервер</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation>Де отримати дані для підключення: покрокові інструкції з придбання VPS</translation>
|
<translation>Де отримати дані для підключення: покрокові інструкції з придбання VPS</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>Поле IP address не може бути пустим</translation>
|
<translation>Поле IP address не може бути пустим</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>Поле Login не може бути пустим</translation>
|
<translation>Поле Login не може бути пустим</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>Поле Password/Private key не може бути пустим</translation>
|
<translation>Поле Password/Private key не може бути пустим</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3182,52 +3182,47 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation>Відправлено даних: %1</translation>
|
<translation>Відправлено даних: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="obsolete">Дата створення:</translation>
|
<translation type="obsolete">Дата створення:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>Перейменувати</translation>
|
<translation>Перейменувати</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>Назва клієнта</translation>
|
<translation>Назва клієнта</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Зберегти</translation>
|
<translation>Зберегти</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>Відкликати</translation>
|
<translation>Відкликати</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>Відкликати доступ для користувача - %1?</translation>
|
<translation>Відкликати доступ для користувача - %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>Користувач більше не зможе підключатись до вашого сервера </translation>
|
<translation>Користувач більше не зможе підключатись до вашого сервера </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>Продовжити</translation>
|
<translation>Продовжити</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Відмінити</translation>
|
<translation>Відмінити</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4629,12 +4624,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>Пароль для особистого ключа</translation>
|
<translation>Пароль для особистого ключа</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Зберегти</translation>
|
<translation>Зберегти</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -257,18 +257,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation>فائل کو کھولنے سے قاصر ہے</translation>
|
<translation>فائل کو کھولنے سے قاصر ہے</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation>غلط کنفیگریشن فائل</translation>
|
<translation>غلط کنفیگریشن فائل</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>سکین%1 کی%2.</translation>
|
<translation>سکین%1 کی%2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -399,30 +399,30 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation>The translation of "AmneziaVPN" in Urdu would be:
|
<translation>The translation of "AmneziaVPN" in Urdu would be:
|
||||||
|
|
||||||
امنیزیا وی پی ای</translation>
|
امنیزیا وی پی ای</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>وی پی این متصل ہوگیا</translation>
|
<translation>وی پی این متصل ہوگیا</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>وی پی این منقطع ہوگیا</translation>
|
<translation>وی پی این منقطع ہوگیا</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>امنیزیا وی پی این کی اطلاعات</translation>
|
<translation>امنیزیا وی پی این کی اطلاعات</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>غیر محفوظ نیٹ ورک کا پتہ لگایا گیا ہے: </translation>
|
<translation>غیر محفوظ نیٹ ورک کا پتہ لگایا گیا ہے: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2498,12 +2498,12 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation>سرور آئی پی پتہ [:پورٹ]</translation>
|
<translation>سرور آئی پی پتہ [:پورٹ]</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>براہ کرم جاری رکھیں</translation>
|
<translation>براہ کرم جاری رکھیں</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>آپ جو ڈیٹا داخل کریں گے وہ بالکل خفیہ رہے گا اور نہ تو امنیزیا یا کسی تیسری شخصیت کے ساتھ اشتراک کیا جائے گا</translation>
|
<translation>آپ جو ڈیٹا داخل کریں گے وہ بالکل خفیہ رہے گا اور نہ تو امنیزیا یا کسی تیسری شخصیت کے ساتھ اشتراک کیا جائے گا</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2513,42 +2513,42 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>ایس ایس ایچ صارف نام</translation>
|
<translation>ایس ایس ایچ صارف نام</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>پاس ورڈ یا SSH نجی کلید</translation>
|
<translation>پاس ورڈ یا SSH نجی کلید</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>آئی پی پتہ خالی نہیں ہو سکتا</translation>
|
<translation>آئی پی پتہ خالی نہیں ہو سکتا</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>ایڈریس درج کریں فارمیٹ 255.255.255.255:88</translation>
|
<translation>ایڈریس درج کریں فارمیٹ 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>لاگ ان نام خالی نہیں ہو سکتا</translation>
|
<translation>لاگ ان نام خالی نہیں ہو سکتا</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>پاس ورڈ یا نجی کلید خالی نہیں ہو سکتی</translation>
|
<translation>پاس ورڈ یا نجی کلید خالی نہیں ہو سکتی</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2880,52 +2880,47 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="vanished">تخلیق کی تاریخ: </translation>
|
<translation type="vanished">تخلیق کی تاریخ: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>نام تبدیل</translation>
|
<translation>نام تبدیل</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>کلائنٹ کا نام</translation>
|
<translation>کلائنٹ کا نام</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>محفوظ</translation>
|
<translation>محفوظ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>واپس لین</translation>
|
<translation>واپس لین</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>کیا آپ مستعمل کے لئے کنفیگ کو واپس لینا چاہتے ہیں - %1؟</translation>
|
<translation>کیا آپ مستعمل کے لئے کنفیگ کو واپس لینا چاہتے ہیں - %1؟</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>صارف آپ کے سرور سے متصل ہونے کا اختیار نہیں رہے گا.</translation>
|
<translation>صارف آپ کے سرور سے متصل ہونے کا اختیار نہیں رہے گا.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>جاری رکھیں</translation>
|
<translation>جاری رکھیں</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>منسوخ</translation>
|
<translation>منسوخ</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4159,12 +4154,12 @@ While it offers a blend of security, stability, and speed, it's essential t
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>نجی کلید پاس فریز</translation>
|
<translation>نجی کلید پاس فریز</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>محفوظ کریں</translation>
|
<translation>محفوظ کریں</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -262,18 +262,18 @@ Can't be disabled for current server</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="191"/>
|
<location filename="../ui/controllers/importController.cpp" line="187"/>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="196"/>
|
<location filename="../ui/controllers/importController.cpp" line="192"/>
|
||||||
<source>Invalid configuration file</source>
|
<source>Invalid configuration file</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="622"/>
|
<location filename="../ui/controllers/importController.cpp" line="617"/>
|
||||||
<source>Scanned %1 of %2.</source>
|
<source>Scanned %1 of %2.</source>
|
||||||
<translation>扫描 %1 of %2.</translation>
|
<translation>扫描 %1 of %2.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/controllers/importController.cpp" line="657"/>
|
<location filename="../ui/controllers/importController.cpp" line="652"/>
|
||||||
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
<source>In the imported configuration, potentially dangerous lines were found:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -427,28 +427,28 @@ Already installed containers were found on the server. All installed containers
|
|||||||
<context>
|
<context>
|
||||||
<name>NotificationHandler</name>
|
<name>NotificationHandler</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="66"/>
|
<location filename="../ui/notificationhandler.cpp" line="63"/>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="73"/>
|
<location filename="../ui/notificationhandler.cpp" line="70"/>
|
||||||
<source>AmneziaVPN</source>
|
<source>AmneziaVPN</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="67"/>
|
<location filename="../ui/notificationhandler.cpp" line="64"/>
|
||||||
<source>VPN Connected</source>
|
<source>VPN Connected</source>
|
||||||
<translation>已连接到VPN</translation>
|
<translation>已连接到VPN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="74"/>
|
<location filename="../ui/notificationhandler.cpp" line="71"/>
|
||||||
<source>VPN Disconnected</source>
|
<source>VPN Disconnected</source>
|
||||||
<translation>已从VPN断开</translation>
|
<translation>已从VPN断开</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="97"/>
|
<location filename="../ui/notificationhandler.cpp" line="94"/>
|
||||||
<source>AmneziaVPN notification</source>
|
<source>AmneziaVPN notification</source>
|
||||||
<translation>AmneziaVPN 提示</translation>
|
<translation>AmneziaVPN 提示</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/notificationhandler.cpp" line="98"/>
|
<location filename="../ui/notificationhandler.cpp" line="95"/>
|
||||||
<source>Unsecured network detected: </source>
|
<source>Unsecured network detected: </source>
|
||||||
<translation>发现不安全网络</translation>
|
<translation>发现不安全网络</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2673,12 +2673,12 @@ It's okay as long as it's from someone you trust.</source>
|
|||||||
<translation type="vanished">密码 或 私钥</translation>
|
<translation type="vanished">密码 或 私钥</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="113"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="116"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>继续</translation>
|
<translation>继续</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="141"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="144"/>
|
||||||
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
<source>All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||||
<translation>您输入的所有数据将严格保密,不会与 Amnezia 或任何第三方共享或披露</translation>
|
<translation>您输入的所有数据将严格保密,不会与 Amnezia 或任何第三方共享或披露</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -2694,42 +2694,42 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="75"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="78"/>
|
||||||
<source>SSH Username</source>
|
<source>SSH Username</source>
|
||||||
<translation>SSH 用户名</translation>
|
<translation>SSH 用户名</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="91"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="94"/>
|
||||||
<source>Password or SSH private key</source>
|
<source>Password or SSH private key</source>
|
||||||
<translation>密码或 SSH 私钥</translation>
|
<translation>密码或 SSH 私钥</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="150"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="153"/>
|
||||||
<source>How to run your VPN server</source>
|
<source>How to run your VPN server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="151"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="154"/>
|
||||||
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
<source>Where to get connection data, step-by-step instructions for buying a VPS</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="167"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
||||||
<source>Ip address cannot be empty</source>
|
<source>Ip address cannot be empty</source>
|
||||||
<translation>IP不能为空</translation>
|
<translation>IP不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="170"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="173"/>
|
||||||
<source>Enter the address in the format 255.255.255.255:88</source>
|
<source>Enter the address in the format 255.255.255.255:88</source>
|
||||||
<translation>按照这种格式输入 255.255.255.255:88</translation>
|
<translation>按照这种格式输入 255.255.255.255:88</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="174"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="177"/>
|
||||||
<source>Login cannot be empty</source>
|
<source>Login cannot be empty</source>
|
||||||
<translation>账号不能为空</translation>
|
<translation>账号不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="178"/>
|
<location filename="../ui/qml/Pages2/PageSetupWizardCredentials.qml" line="181"/>
|
||||||
<source>Password/private key cannot be empty</source>
|
<source>Password/private key cannot be empty</source>
|
||||||
<translation>密码或私钥不能为空</translation>
|
<translation>密码或私钥不能为空</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -3086,52 +3086,47 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
|||||||
<source>Data sent: %1</source>
|
<source>Data sent: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
|
||||||
<source>Allowed IPs: %1</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Creation date: </source>
|
<source>Creation date: </source>
|
||||||
<translation type="vanished">创建日期: </translation>
|
<translation type="vanished">创建日期: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="870"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="862"/>
|
||||||
<source>Rename</source>
|
<source>Rename</source>
|
||||||
<translation>重新命名</translation>
|
<translation>重新命名</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="916"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="908"/>
|
||||||
<source>Client name</source>
|
<source>Client name</source>
|
||||||
<translation>客户名称</translation>
|
<translation>客户名称</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="929"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="921"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>保存</translation>
|
<translation>保存</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="963"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="955"/>
|
||||||
<source>Revoke</source>
|
<source>Revoke</source>
|
||||||
<translation>撤销</translation>
|
<translation>撤销</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="967"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="959"/>
|
||||||
<source>Revoke the config for a user - %1?</source>
|
<source>Revoke the config for a user - %1?</source>
|
||||||
<translation>撤销用户的配置- %1?</translation>
|
<translation>撤销用户的配置- %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="968"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="960"/>
|
||||||
<source>The user will no longer be able to connect to your server.</source>
|
<source>The user will no longer be able to connect to your server.</source>
|
||||||
<translation>该用户将无法再连接到您的服务器.</translation>
|
<translation>该用户将无法再连接到您的服务器.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="969"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="961"/>
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<translation>继续</translation>
|
<translation>继续</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="970"/>
|
<location filename="../ui/qml/Pages2/PageShare.qml" line="962"/>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>取消</translation>
|
<translation>取消</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -4615,12 +4610,12 @@ While it offers a blend of security, stability, and speed, it's essential t
|
|||||||
<context>
|
<context>
|
||||||
<name>main2</name>
|
<name>main2</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="163"/>
|
<location filename="../ui/qml/main2.qml" line="162"/>
|
||||||
<source>Private key passphrase</source>
|
<source>Private key passphrase</source>
|
||||||
<translation>私钥密码</translation>
|
<translation>私钥密码</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ui/qml/main2.qml" line="186"/>
|
<location filename="../ui/qml/main2.qml" line="185"/>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>保存</translation>
|
<translation>保存</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -242,26 +242,24 @@ void ImportController::processNativeWireGuardConfig()
|
|||||||
auto containers = m_config.value(config_key::containers).toArray();
|
auto containers = m_config.value(config_key::containers).toArray();
|
||||||
if (!containers.isEmpty()) {
|
if (!containers.isEmpty()) {
|
||||||
auto container = containers.at(0).toObject();
|
auto container = containers.at(0).toObject();
|
||||||
auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject();
|
auto containerConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::WireGuard)).toObject();
|
||||||
auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object();
|
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||||
|
|
||||||
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
|
QString junkPacketCount = QString::number(QRandomGenerator::global()->bounded(2, 5));
|
||||||
QString junkPacketMinSize = QString::number(10);
|
QString junkPacketMinSize = QString::number(10);
|
||||||
QString junkPacketMaxSize = QString::number(50);
|
QString junkPacketMaxSize = QString::number(50);
|
||||||
clientProtocolConfig[config_key::junkPacketCount] = junkPacketCount;
|
protocolConfig[config_key::junkPacketCount] = junkPacketCount;
|
||||||
clientProtocolConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
|
protocolConfig[config_key::junkPacketMinSize] = junkPacketMinSize;
|
||||||
clientProtocolConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
|
protocolConfig[config_key::junkPacketMaxSize] = junkPacketMaxSize;
|
||||||
clientProtocolConfig[config_key::initPacketJunkSize] = "0";
|
protocolConfig[config_key::initPacketJunkSize] = "0";
|
||||||
clientProtocolConfig[config_key::responsePacketJunkSize] = "0";
|
protocolConfig[config_key::responsePacketJunkSize] = "0";
|
||||||
clientProtocolConfig[config_key::initPacketMagicHeader] = "1";
|
protocolConfig[config_key::initPacketMagicHeader] = "1";
|
||||||
clientProtocolConfig[config_key::responsePacketMagicHeader] = "2";
|
protocolConfig[config_key::responsePacketMagicHeader] = "2";
|
||||||
clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3";
|
protocolConfig[config_key::underloadPacketMagicHeader] = "3";
|
||||||
clientProtocolConfig[config_key::transportPacketMagicHeader] = "4";
|
protocolConfig[config_key::transportPacketMagicHeader] = "4";
|
||||||
|
|
||||||
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
|
containerConfig[config_key::last_config] = QString(QJsonDocument(protocolConfig).toJson());
|
||||||
|
container["wireguard"] = containerConfig;
|
||||||
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());
|
|
||||||
container["wireguard"] = serverProtocolConfig;
|
|
||||||
containers.replace(0, container);
|
containers.replace(0, container);
|
||||||
m_config[config_key::containers] = containers;
|
m_config[config_key::containers] = containers;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
|
|||||||
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
|
AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_MACX and !defined MACOS_NE
|
#if defined Q_OS_MACX
|
||||||
connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); });
|
connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); });
|
||||||
connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); });
|
connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); });
|
||||||
#endif
|
#endif
|
||||||
@@ -114,7 +114,7 @@ void PageController::showOnStartup()
|
|||||||
} else {
|
} else {
|
||||||
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||||
emit hideMainWindow();
|
emit hideMainWindow();
|
||||||
#elif defined Q_OS_MACX and !defined MACOS_NE
|
#elif defined Q_OS_MACX
|
||||||
setDockIconVisible(false);
|
setDockIconVisible(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,12 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "notificationhandler.h"
|
#include "notificationhandler.h"
|
||||||
|
|
||||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
#if defined(Q_OS_IOS)
|
||||||
# include "platforms/ios/iosnotificationhandler.h"
|
# include "platforms/ios/iosnotificationhandler.h"
|
||||||
// #elif defined(MACOS_NE)
|
|
||||||
// # include "platforms/ios/iosnotificationhandler.h"
|
|
||||||
#else
|
#else
|
||||||
# include "systemtray_notificationhandler.h"
|
# include "systemtray_notificationhandler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS)
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PageStart {
|
PageStart {
|
||||||
width: root.width
|
anchors.fill: parent
|
||||||
height: root.height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "notificationhandler.h"
|
#include "notificationhandler.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QSystemTrayIcon>'QMenu' file not found
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
class SystemTrayNotificationHandler : public NotificationHandler {
|
class SystemTrayNotificationHandler : public NotificationHandler {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "Build script for macOS Network Extension started ..."
|
|
||||||
|
|
||||||
set -o errexit -o nounset
|
|
||||||
|
|
||||||
while getopts n flag
|
|
||||||
do
|
|
||||||
case "${flag}" in
|
|
||||||
n) NOTARIZE_APP=1;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Hold on to current directory
|
|
||||||
PROJECT_DIR=$(pwd)
|
|
||||||
DEPLOY_DIR=$PROJECT_DIR/deploy
|
|
||||||
|
|
||||||
mkdir -p $DEPLOY_DIR/build
|
|
||||||
BUILD_DIR=$DEPLOY_DIR/build
|
|
||||||
|
|
||||||
echo "Project dir: ${PROJECT_DIR}"
|
|
||||||
echo "Build dir: ${BUILD_DIR}"
|
|
||||||
|
|
||||||
APP_NAME=AmneziaVPN
|
|
||||||
APP_FILENAME=$APP_NAME.app
|
|
||||||
APP_DOMAIN=org.amneziavpn.package
|
|
||||||
PLIST_NAME=$APP_NAME.plist
|
|
||||||
|
|
||||||
OUT_APP_DIR=$BUILD_DIR/client
|
|
||||||
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
|
|
||||||
|
|
||||||
PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos
|
|
||||||
DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos
|
|
||||||
|
|
||||||
INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data
|
|
||||||
INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME
|
|
||||||
DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg
|
|
||||||
|
|
||||||
# Setup provisioning profiles for main app and NE
|
|
||||||
echo "Setting up provisioning profiles..."
|
|
||||||
|
|
||||||
# Tạo thư mục Provisioning Profiles nếu chưa tồn tại
|
|
||||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
|
||||||
|
|
||||||
# Setup provisioning profile cho main app
|
|
||||||
echo "Setting up provisioning profile for main app (AmneziaVPN)"
|
|
||||||
cp $PROJECT_DIR/deploy/AnhTVMacOSMain.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/
|
|
||||||
macos_main_uuid=$(grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/AnhTVMacOSMain.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
|
||||||
mv ~/Library/MobileDevice/Provisioning\ Profiles/AnhTVMacOSMain.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$macos_main_uuid.mobileprovision
|
|
||||||
|
|
||||||
# Setup provisioning profile cho Network Extension (NE)
|
|
||||||
echo "Setting up provisioning profile for Network Extension"
|
|
||||||
cp $PROJECT_DIR/deploy/AnhTVMacOSNE.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/macos_ne.mobileprovision
|
|
||||||
macos_ne_uuid=$(grep UUID -A1 -a ~/Library/MobileDevice/Provisioning\ Profiles/macos_ne.mobileprovision | grep -io "[-A-F0-9]\{36\}")
|
|
||||||
mv ~/Library/MobileDevice/Provisioning\ Profiles/macos_ne.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$macos_ne_uuid.mobileprovision
|
|
||||||
|
|
||||||
# Giải mã và cài đặt chứng chỉ ký code
|
|
||||||
echo "Installing signing certificates..."
|
|
||||||
echo $MAC_TRUST_CERT_BASE64 | base64 --decode > mac_trust_cert.pem
|
|
||||||
echo $MAC_SIGNING_CERT_BASE64 | base64 --decode > mac_signing_cert.p12
|
|
||||||
|
|
||||||
# Cài đặt chứng chỉ vào keychain
|
|
||||||
security create-keychain -p password build.keychain
|
|
||||||
security default-keychain -s build.keychain
|
|
||||||
security unlock-keychain -p password build.keychain
|
|
||||||
security import mac_trust_cert.pem -k build.keychain -A
|
|
||||||
security import mac_signing_cert.p12 -k build.keychain -P $MAC_SIGNING_CERT_PASSWORD -A
|
|
||||||
|
|
||||||
# Thiết lập keychain cho quá trình ký
|
|
||||||
security set-key-partition-list -S apple-tool:,apple: -s -k password build.keychain
|
|
||||||
|
|
||||||
# Check if QIF_VERSION is properly set, otherwise set a default
|
|
||||||
if [ -z "${QIF_VERSION+x}" ]; then
|
|
||||||
echo "QIF_VERSION is not set, using default 4.6"
|
|
||||||
QIF_VERSION=4.6
|
|
||||||
fi
|
|
||||||
|
|
||||||
QIF_BIN_DIR="$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin"
|
|
||||||
|
|
||||||
# Checking environment
|
|
||||||
$QT_BIN_DIR/qt-cmake --version || { echo "Error: qt-cmake not found in $QT_BIN_DIR"; exit 1; }
|
|
||||||
cmake --version || { echo "Error: cmake not found"; exit 1; }
|
|
||||||
clang -v || { echo "Error: clang not found"; exit 1; }
|
|
||||||
|
|
||||||
# Build the Network Extension app
|
|
||||||
echo "Building Network Extension App..."
|
|
||||||
mkdir -p build-macos-ne
|
|
||||||
cd build-macos-ne
|
|
||||||
|
|
||||||
$QT_BIN_DIR/qt-cmake .. -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -DMACOS_NE=TRUE
|
|
||||||
cmake --build . --config release --target AmneziaVPN -- -allowProvisioningUpdates # Thay đổi target phù hợp cho Network Extension
|
|
||||||
|
|
||||||
# Build and run tests here
|
|
||||||
|
|
||||||
echo "____________________________________"
|
|
||||||
echo "............Deploy.................."
|
|
||||||
echo "____________________________________"
|
|
||||||
|
|
||||||
# Package Network Extension
|
|
||||||
echo "Packaging Network Extension ..."
|
|
||||||
|
|
||||||
# Copy necessary data
|
|
||||||
cp -Rv $PREBUILT_DEPLOY_DATA_DIR/* $BUNDLE_DIR/Contents/macOS
|
|
||||||
$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite -qmldir=$PROJECT_DIR
|
|
||||||
cp -av $BUILD_DIR/service/server/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
|
|
||||||
cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS
|
|
||||||
|
|
||||||
# Signing and notarizing the Network Extension
|
|
||||||
if [ "${MAC_CERT_PW+x}" ]; then
|
|
||||||
CERTIFICATE_P12=$DEPLOY_DIR/PrivacyTechAppleCertDeveloperId.p12
|
|
||||||
WWDRCA=$DEPLOY_DIR/WWDRCA.cer
|
|
||||||
KEYCHAIN=amnezia.build.macos.keychain
|
|
||||||
TEMP_PASS=tmp_pass
|
|
||||||
|
|
||||||
security create-keychain -p $TEMP_PASS $KEYCHAIN || true
|
|
||||||
security default-keychain -s $KEYCHAIN
|
|
||||||
security unlock-keychain -p $TEMP_PASS $KEYCHAIN
|
|
||||||
|
|
||||||
security import $WWDRCA -k $KEYCHAIN -T /usr/bin/codesign || true
|
|
||||||
security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MAC_CERT_PW -T /usr/bin/codesign || true
|
|
||||||
|
|
||||||
echo "Signing Network Extension..."
|
|
||||||
/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $BUNDLE_DIR
|
|
||||||
spctl -a -vvvv $BUNDLE_DIR || true
|
|
||||||
|
|
||||||
if [ "${NOTARIZE_APP+x}" ]; then
|
|
||||||
echo "Notarizing Network Extension bundle..."
|
|
||||||
/usr/bin/ditto -c -k --keepParent $BUNDLE_DIR $PROJECT_DIR/NE_Bundle_to_notarize.zip
|
|
||||||
xcrun notarytool submit $PROJECT_DIR/NE_Bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD
|
|
||||||
rm $PROJECT_DIR/NE_Bundle_to_notarize.zip
|
|
||||||
sleep 300
|
|
||||||
xcrun stapler staple $BUNDLE_DIR
|
|
||||||
spctl -a -vvvv $BUNDLE_DIR || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Package installer, sign, and notarize (if needed)
|
|
||||||
|
|
||||||
# The rest of your installer packaging process similar to build_macos.sh
|
|
||||||
@@ -4,7 +4,7 @@ if(WIN32)
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/config/windows.xml.in
|
${CMAKE_CURRENT_LIST_DIR}/config/windows.xml.in
|
||||||
${CMAKE_BINARY_DIR}/installer/config/windows.xml
|
${CMAKE_BINARY_DIR}/installer/config/windows.xml
|
||||||
)
|
)
|
||||||
elseif(APPLE AND NOT IOS AND NOT MACOS_NE)
|
elseif(APPLE AND NOT IOS)
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/config/macos.xml.in
|
${CMAKE_CURRENT_LIST_DIR}/config/macos.xml.in
|
||||||
${CMAKE_BINARY_DIR}/installer/config/macos.xml
|
${CMAKE_BINARY_DIR}/installer/config/macos.xml
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ project(${PROJECT})
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
if(NOT IOS AND NOT ANDROID)
|
||||||
add_subdirectory(server)
|
add_subdirectory(server)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||