Source code changes
This commit is contained in:
parent
332588a705
commit
95ee1bfe58
34 changed files with 936 additions and 231 deletions
|
|
@ -167,7 +167,7 @@ bool AmneziaApplication::parseCommands()
|
||||||
|
|
||||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||||
m_parser.addOption(c_cleanup);
|
m_parser.addOption(c_cleanup);
|
||||||
|
|
||||||
m_parser.process(*this);
|
m_parser.process(*this);
|
||||||
|
|
||||||
if (m_parser.isSet(c_cleanup)) {
|
if (m_parser.isSet(c_cleanup)) {
|
||||||
|
|
@ -179,9 +179,8 @@ bool AmneziaApplication::parseCommands()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void AmneziaApplication::startLocalServer()
|
void AmneziaApplication::startLocalServer() {
|
||||||
{
|
|
||||||
const QString serverName("AmneziaVPNInstance");
|
const QString serverName("AmneziaVPNInstance");
|
||||||
QLocalServer::removeServer(serverName);
|
QLocalServer::removeServer(serverName);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||||
#else
|
#else
|
||||||
#define AMNEZIA_BASE_CLASS QApplication
|
#define AMNEZIA_BASE_CLASS QApplication
|
||||||
|
|
@ -37,7 +37,7 @@ public:
|
||||||
void loadFonts();
|
void loadFonts();
|
||||||
bool parseCommands();
|
bool parseCommands();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
void startLocalServer();
|
void startLocalServer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
@ -120,7 +120,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -129,7 +129,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||||
|
|
||||||
// no redirect-gateway
|
// no redirect-gateway
|
||||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||||
// Prevent ipv6 leak
|
// Prevent ipv6 leak
|
||||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
||||||
|
|
||||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
||||||
|
|
@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEAD CODE.
|
||||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_IOS
|
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QProcess *p = new QProcess();
|
QProcess *p = new QProcess();
|
||||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
|
|
@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
||||||
pathEnvVar.clear();
|
pathEnvVar.clear();
|
||||||
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) && !defined(MACOS_NE)
|
||||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
#elif defined(Q_OS_IOS)
|
#elif defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case DockerContainer::WireGuard: return true;
|
case DockerContainer::WireGuard: return true;
|
||||||
case DockerContainer::OpenVpn: return true;
|
case DockerContainer::OpenVpn: return true;
|
||||||
|
|
|
||||||
509
client/core/controllers/apiController.cpp
Normal file
509
client/core/controllers/apiController.cpp
Normal file
|
|
@ -0,0 +1,509 @@
|
||||||
|
#include "apiController.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
#include "QBlockCipher.h"
|
||||||
|
#include "QRsa.h"
|
||||||
|
|
||||||
|
#include "amnezia_application.h"
|
||||||
|
#include "configurators/wireguard_configurator.h"
|
||||||
|
#include "core/enums/apiEnums.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace configKey
|
||||||
|
{
|
||||||
|
constexpr char cloak[] = "cloak";
|
||||||
|
constexpr char awg[] = "awg";
|
||||||
|
|
||||||
|
constexpr char apiEdnpoint[] = "api_endpoint";
|
||||||
|
constexpr char accessToken[] = "api_key";
|
||||||
|
constexpr char certificate[] = "certificate";
|
||||||
|
constexpr char publicKey[] = "public_key";
|
||||||
|
constexpr char protocol[] = "protocol";
|
||||||
|
|
||||||
|
constexpr char uuid[] = "installation_uuid";
|
||||||
|
constexpr char osVersion[] = "os_version";
|
||||||
|
constexpr char appVersion[] = "app_version";
|
||||||
|
|
||||||
|
constexpr char userCountryCode[] = "user_country_code";
|
||||||
|
constexpr char serverCountryCode[] = "server_country_code";
|
||||||
|
constexpr char serviceType[] = "service_type";
|
||||||
|
constexpr char serviceInfo[] = "service_info";
|
||||||
|
|
||||||
|
constexpr char aesKey[] = "aes_key";
|
||||||
|
constexpr char aesIv[] = "aes_iv";
|
||||||
|
constexpr char aesSalt[] = "aes_salt";
|
||||||
|
|
||||||
|
constexpr char apiPayload[] = "api_payload";
|
||||||
|
constexpr char keyPayload[] = "key_payload";
|
||||||
|
|
||||||
|
constexpr char apiConfig[] = "api_config";
|
||||||
|
constexpr char authData[] = "auth_data";
|
||||||
|
}
|
||||||
|
|
||||||
|
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
|
||||||
|
|
||||||
|
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if (!sslErrors.empty()) {
|
||||||
|
qDebug().noquote() << sslErrors;
|
||||||
|
return ErrorCode::ApiConfigSslError;
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
} else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
return ErrorCode::ApiConfigTimeoutError;
|
||||||
|
} else {
|
||||||
|
QString err = reply->errorString();
|
||||||
|
qDebug() << QString::fromUtf8(reply->readAll());
|
||||||
|
qDebug() << reply->error();
|
||||||
|
qDebug() << err;
|
||||||
|
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
return ErrorCode::ApiConfigDownloadError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldBypassProxy(QNetworkReply *reply, const QByteArray &responseBody, bool checkEncryption, const QByteArray &key = "",
|
||||||
|
const QByteArray &iv = "", const QByteArray &salt = "")
|
||||||
|
{
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
qDebug() << "Timeout occurred";
|
||||||
|
return true;
|
||||||
|
} else if (responseBody.contains("html")) {
|
||||||
|
qDebug() << "The response contains an html tag";
|
||||||
|
return true;
|
||||||
|
} else if (checkEncryption) {
|
||||||
|
try {
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));
|
||||||
|
} catch (...) {
|
||||||
|
qDebug() << "Failed to decrypt the data";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiController::ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent)
|
||||||
|
: QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiController::fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData,
|
||||||
|
const QByteArray &apiResponseBody, QJsonObject &serverConfig)
|
||||||
|
{
|
||||||
|
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
|
||||||
|
|
||||||
|
data.replace("vpn://", "");
|
||||||
|
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba_uncompressed = qUncompress(ba);
|
||||||
|
if (!ba_uncompressed.isEmpty()) {
|
||||||
|
ba = ba_uncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString configStr = ba;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
configStr.replace("<key>", "<key>\n");
|
||||||
|
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
|
||||||
|
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||||
|
auto containers = newServerConfig.value(config_key::containers).toArray();
|
||||||
|
if (containers.isEmpty()) {
|
||||||
|
return; // todo process error
|
||||||
|
}
|
||||||
|
auto container = containers.at(0).toObject();
|
||||||
|
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
|
||||||
|
auto containerConfig = container.value(containerName).toObject();
|
||||||
|
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
|
||||||
|
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
|
||||||
|
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
|
||||||
|
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
|
||||||
|
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
|
||||||
|
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
|
||||||
|
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
|
||||||
|
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
|
||||||
|
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
|
||||||
|
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
|
||||||
|
container[containerName] = containerConfig;
|
||||||
|
containers.replace(0, container);
|
||||||
|
newServerConfig[config_key::containers] = containers;
|
||||||
|
configStr = QString(QJsonDocument(newServerConfig).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||||
|
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
|
||||||
|
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
|
||||||
|
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
|
||||||
|
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
|
||||||
|
|
||||||
|
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||||
|
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
|
||||||
|
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
|
||||||
|
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
|
||||||
|
serverConfig[config_key::defaultContainer] = defaultContainer;
|
||||||
|
|
||||||
|
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
|
||||||
|
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
|
||||||
|
auto apiConfig = QJsonObject::fromVariantMap(map);
|
||||||
|
|
||||||
|
if (newServerConfig.value(config_key::configVersion).toInt() == ApiConfigSources::AmneziaGateway) {
|
||||||
|
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConfig[configKey::apiConfig] = apiConfig;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ApiController::getProxyUrls()
|
||||||
|
{
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
QNetworkReply *reply;
|
||||||
|
|
||||||
|
QStringList proxyStorageUrl;
|
||||||
|
if (m_isDevEnvironment) {
|
||||||
|
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
|
||||||
|
} else {
|
||||||
|
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||||
|
|
||||||
|
for (const auto &proxyStorageUrl : proxyStorageUrl) {
|
||||||
|
request.setUrl(proxyStorageUrl);
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto encryptedResponseBody = reply->readAll();
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
EVP_PKEY *privateKey = nullptr;
|
||||||
|
QByteArray responseBody;
|
||||||
|
try {
|
||||||
|
if (!m_isDevEnvironment) {
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha512);
|
||||||
|
hash.addData(key);
|
||||||
|
QByteArray hashResult = hash.result().toHex();
|
||||||
|
|
||||||
|
QByteArray key = QByteArray::fromHex(hashResult.left(64));
|
||||||
|
QByteArray iv = QByteArray::fromHex(hashResult.mid(64, 32));
|
||||||
|
|
||||||
|
QByteArray ba = QByteArray::fromBase64(encryptedResponseBody);
|
||||||
|
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
responseBody = blockCipher.decryptAesBlockCipher(ba, key, iv);
|
||||||
|
} else {
|
||||||
|
responseBody = encryptedResponseBody;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
Utils::logException();
|
||||||
|
qCritical() << "error loading private key from environment variables or decrypting payload";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto endpointsArray = QJsonDocument::fromJson(responseBody).array();
|
||||||
|
|
||||||
|
QStringList endpoints;
|
||||||
|
for (const auto &endpoint : endpointsArray) {
|
||||||
|
endpoints.push_back(endpoint.toString());
|
||||||
|
}
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiController::ApiPayloadData ApiController::generateApiPayloadData(const QString &protocol)
|
||||||
|
{
|
||||||
|
ApiController::ApiPayloadData apiPayload;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
apiPayload.certRequest = OpenVpnConfigurator::createCertRequest();
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
auto connData = WireguardConfigurator::genClientKeys();
|
||||||
|
apiPayload.wireGuardClientPubKey = connData.clientPubKey;
|
||||||
|
apiPayload.wireGuardClientPrivKey = connData.clientPrivKey;
|
||||||
|
}
|
||||||
|
return apiPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData)
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
if (protocol == configKey::cloak) {
|
||||||
|
obj[configKey::certificate] = apiPayloadData.certRequest.request;
|
||||||
|
} else if (protocol == configKey::awg) {
|
||||||
|
obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[configKey::osVersion] = QSysInfo::productType();
|
||||||
|
obj[configKey::appVersion] = QString(APP_VERSION);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiController::updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (serverConfig.value(config_key::configVersion).toInt()) {
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||||
|
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||||
|
request.setUrl(endpoint);
|
||||||
|
|
||||||
|
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||||
|
|
||||||
|
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||||
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
|
||||||
|
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
|
||||||
|
|
||||||
|
QNetworkReply *reply = amnApp->manager()->post(request, requestBody);
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [this, reply, protocol, apiPayloadData, serverIndex, serverConfig]() mutable {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
auto apiResponseBody = reply->readAll();
|
||||||
|
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
|
||||||
|
emit finished(serverConfig, serverIndex);
|
||||||
|
} else {
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|
||||||
|
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigTimeoutError);
|
||||||
|
} else {
|
||||||
|
QString err = reply->errorString();
|
||||||
|
qDebug() << QString::fromUtf8(reply->readAll());
|
||||||
|
qDebug() << reply->error();
|
||||||
|
qDebug() << err;
|
||||||
|
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigDownloadError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::errorOccurred,
|
||||||
|
[this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString() << error; });
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList<QSslError> &errors) {
|
||||||
|
qDebug().noquote() << errors;
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigSslError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ApiController::getServicesList(QByteArray &responseBody)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
QNetworkReply *reply;
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
responseBody = reply->readAll();
|
||||||
|
|
||||||
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, responseBody, false)) {
|
||||||
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
|
request.setUrl(QString("%1v1/services").arg(proxyUrl));
|
||||||
|
reply->deleteLater(); // delete the previous reply
|
||||||
|
reply = amnApp->manager()->get(request);
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
responseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, responseBody, false)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if (errorCode == ErrorCode::NoError) {
|
||||||
|
if (!responseBody.contains("services")) {
|
||||||
|
return ErrorCode::ApiServicesMissingError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ApiController::getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,
|
||||||
|
QJsonObject &serverConfig)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
IosController::Instance()->requestInetAccess();
|
||||||
|
QThread::msleep(10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setTransferTimeout(requestTimeoutMsecs);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
|
||||||
|
apiPayload[configKey::userCountryCode] = userCountryCode;
|
||||||
|
if (!serverCountryCode.isEmpty()) {
|
||||||
|
apiPayload[configKey::serverCountryCode] = serverCountryCode;
|
||||||
|
}
|
||||||
|
apiPayload[configKey::serviceType] = serviceType;
|
||||||
|
apiPayload[configKey::uuid] = installationUuid;
|
||||||
|
if (!authData.isEmpty()) {
|
||||||
|
apiPayload[configKey::authData] = authData;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
|
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||||
|
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||||
|
QByteArray salt = blockCipher.generatePrivateSalt(8);
|
||||||
|
|
||||||
|
QJsonObject keyPayload;
|
||||||
|
keyPayload[configKey::aesKey] = QString(key.toBase64());
|
||||||
|
keyPayload[configKey::aesIv] = QString(iv.toBase64());
|
||||||
|
keyPayload[configKey::aesSalt] = QString(salt.toBase64());
|
||||||
|
|
||||||
|
QByteArray encryptedKeyPayload;
|
||||||
|
QByteArray encryptedApiPayload;
|
||||||
|
try {
|
||||||
|
QSimpleCrypto::QRsa rsa;
|
||||||
|
|
||||||
|
EVP_PKEY *publicKey = nullptr;
|
||||||
|
try {
|
||||||
|
QByteArray rsaKey = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||||
|
QSimpleCrypto::QRsa rsa;
|
||||||
|
publicKey = rsa.getPublicKeyFromByteArray(rsaKey);
|
||||||
|
} catch (...) {
|
||||||
|
Utils::logException();
|
||||||
|
qCritical() << "error loading public key from environment variables";
|
||||||
|
return ErrorCode::ApiMissingAgwPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedKeyPayload = rsa.encrypt(QJsonDocument(keyPayload).toJson(), publicKey, RSA_PKCS1_PADDING);
|
||||||
|
EVP_PKEY_free(publicKey);
|
||||||
|
|
||||||
|
encryptedApiPayload = blockCipher.encryptAesBlockCipher(QJsonDocument(apiPayload).toJson(), key, iv, "", salt);
|
||||||
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
|
Utils::logException();
|
||||||
|
qCritical() << "error when encrypting the request body";
|
||||||
|
return ErrorCode::ApiConfigDecryptionError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject requestBody;
|
||||||
|
requestBody[configKey::keyPayload] = QString(encryptedKeyPayload.toBase64());
|
||||||
|
requestBody[configKey::apiPayload] = QString(encryptedApiPayload.toBase64());
|
||||||
|
|
||||||
|
QNetworkReply *reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
|
||||||
|
QList<QSslError> sslErrors;
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
auto encryptedResponseBody = reply->readAll();
|
||||||
|
|
||||||
|
if (sslErrors.isEmpty() && shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
|
m_proxyUrls = getProxyUrls();
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::shuffle(m_proxyUrls.begin(), m_proxyUrls.end(), generator);
|
||||||
|
for (const QString &proxyUrl : m_proxyUrls) {
|
||||||
|
qDebug() << "Go to the next endpoint";
|
||||||
|
request.setUrl(QString("%1v1/config").arg(proxyUrl));
|
||||||
|
reply->deleteLater(); // delete the previous reply
|
||||||
|
reply = amnApp->manager()->post(request, QJsonDocument(requestBody).toJson());
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||||
|
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
encryptedResponseBody = reply->readAll();
|
||||||
|
if (!sslErrors.isEmpty() || !shouldBypassProxy(reply, encryptedResponseBody, true, key, iv, salt)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto errorCode = checkErrors(sslErrors, reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
if (errorCode) {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto responseBody = blockCipher.decryptAesBlockCipher(encryptedResponseBody, key, iv, "", salt);
|
||||||
|
fillServerConfig(protocol, apiPayloadData, responseBody, serverConfig);
|
||||||
|
} catch (...) { // todo change error handling in QSimpleCrypto?
|
||||||
|
Utils::logException();
|
||||||
|
qCritical() << "error when decrypting the request body";
|
||||||
|
return ErrorCode::ApiConfigDecryptionError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
50
client/core/controllers/apiController.h
Normal file
50
client/core/controllers/apiController.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef APICONTROLLER_H
|
||||||
|
#define APICONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "configurators/openvpn_configurator.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class ApiController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ApiController(const QString &gatewayEndpoint, bool isDevEnvironment, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
|
||||||
|
|
||||||
|
ErrorCode getServicesList(QByteArray &responseBody);
|
||||||
|
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
|
||||||
|
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, QJsonObject &serverConfig);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(ErrorCode errorCode);
|
||||||
|
void finished(const QJsonObject &config, const int serverIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ApiPayloadData
|
||||||
|
{
|
||||||
|
OpenVpnConfigurator::ConnectionData certRequest;
|
||||||
|
|
||||||
|
QString wireGuardClientPrivKey;
|
||||||
|
QString wireGuardClientPubKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiPayloadData generateApiPayloadData(const QString &protocol);
|
||||||
|
QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData);
|
||||||
|
void fillServerConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
|
||||||
|
QJsonObject &serverConfig);
|
||||||
|
QStringList getProxyUrls();
|
||||||
|
|
||||||
|
QString m_gatewayEndpoint;
|
||||||
|
QStringList m_proxyUrls;
|
||||||
|
bool m_isDevEnvironment = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APICONTROLLER_H
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
@ -378,7 +378,7 @@ QString NetworkUtilities::getGatewayAndIface()
|
||||||
close(sock);
|
close(sock);
|
||||||
return gateway_address;
|
return gateway_address;
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QString gateway;
|
QString gateway;
|
||||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||||
int afinet_type[] = {AF_INET, AF_INET6};
|
int afinet_type[] = {AF_INET, AF_INET6};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* 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
|
* 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/. */
|
* 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/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||||
|
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
@ -23,3 +23,8 @@ 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);
|
||||||
|
|
||||||
|
// init function definition in C
|
||||||
|
void hev_socks5_tunnel_quit(void);
|
||||||
|
// Updated function definition in C
|
||||||
|
int hev_socks5_tunnel_main(const char* configFile, int fd);
|
||||||
|
|
|
||||||
3
client/macos/networkextension/wireguard-go-version.h.in
Normal file
3
client/macos/networkextension/wireguard-go-version.h.in
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#ifndef WIREGUARD_GO_VERSION
|
||||||
|
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||||
|
#endif // WIREGUARD_GO_VERSION
|
||||||
|
|
@ -11,11 +11,11 @@
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#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)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
bool isAnotherInstanceRunning()
|
bool isAnotherInstanceRunning()
|
||||||
{
|
{
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
|
|
@ -45,7 +45,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
AmneziaApplication app(argc, argv);
|
AmneziaApplication app(argc, argv);
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (isAnotherInstanceRunning()) {
|
if (isAnotherInstanceRunning()) {
|
||||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
||||||
startHandler = completionHandler
|
startHandler = completionHandler
|
||||||
ovpnAdapter?.connect(using: packetFlow)
|
ovpnAdapter?.connect(using: packetFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
guard let completionHandler = completionHandler else { return }
|
guard let completionHandler = completionHandler else { return }
|
||||||
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,19 @@ extension PacketTunnelProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastHandshakeString = settingsDictionary["last_handshake_time_sec"]
|
||||||
|
let lastHandshake: Int64
|
||||||
|
|
||||||
|
if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) {
|
||||||
|
lastHandshake = handshakeValue
|
||||||
|
} else {
|
||||||
|
lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec`
|
||||||
|
}
|
||||||
|
|
||||||
let response: [String: Any] = [
|
let response: [String: Any] = [
|
||||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0",
|
||||||
|
"last_handshake_time_sec": lastHandshake
|
||||||
]
|
]
|
||||||
|
|
||||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#if !MACOS_NE
|
||||||
#include "QRCodeReaderBase.h"
|
#include "QRCodeReaderBase.h"
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
@ -108,3 +109,19 @@ 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,5 +1,6 @@
|
||||||
|
#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,3 +1,13 @@
|
||||||
|
#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) {
|
||||||
|
|
@ -90,3 +100,4 @@ struct ProtectionPair {
|
||||||
textField.removeFromSuperview()
|
textField.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public:
|
||||||
void disconnectVpn();
|
void disconnectVpn();
|
||||||
|
|
||||||
void vpnStatusDidChange(void *pNotification);
|
void vpnStatusDidChange(void *pNotification);
|
||||||
|
|
||||||
void vpnConfigurationDidChange(void *pNotification);
|
void vpnConfigurationDidChange(void *pNotification);
|
||||||
|
|
||||||
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ 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) {
|
||||||
|
|
@ -36,6 +37,7 @@ static UIViewController* getViewController() {
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
|
@ -249,6 +251,19 @@ void IosController::checkStatus()
|
||||||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||||
|
|
||||||
|
uint64_t last_handshake_time_sec = 0;
|
||||||
|
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
|
||||||
|
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
|
||||||
|
} else {
|
||||||
|
qDebug() << "Key last_handshake_time_sec is missing or null";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_handshake_time_sec < 0) {
|
||||||
|
disconnectVpn();
|
||||||
|
qDebug() << "Invalid handshake time, disconnecting VPN.";
|
||||||
|
}
|
||||||
|
|
||||||
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
||||||
m_rxBytes = rxBytes;
|
m_rxBytes = rxBytes;
|
||||||
m_txBytes = txBytes;
|
m_txBytes = txBytes;
|
||||||
|
|
@ -789,14 +804,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();
|
||||||
|
|
@ -808,7 +823,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();
|
||||||
|
|
@ -817,6 +832,7 @@ 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];
|
||||||
|
|
@ -826,9 +842,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);
|
||||||
|
|
@ -837,7 +853,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();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#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;
|
||||||
|
|
@ -17,9 +21,10 @@ 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,8 @@
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DocumentPickerDelegate
|
#if !MACOS_NE
|
||||||
|
@implementation DocumentPickerDelegate
|
||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||||
for (NSURL *url in urls) {
|
for (NSURL *url in urls) {
|
||||||
|
|
@ -42,4 +43,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#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
|
||||||
|
|
@ -87,3 +89,86 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ namespace amnezia
|
||||||
|
|
||||||
constexpr char defaultPort[] = "51820";
|
constexpr char defaultPort[] = "51820";
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
|
|
@ -197,7 +197,7 @@ namespace amnezia
|
||||||
namespace awg
|
namespace awg
|
||||||
{
|
{
|
||||||
constexpr char defaultPort[] = "55424";
|
constexpr char defaultPort[] = "55424";
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
constexpr char defaultMtu[] = "1280";
|
constexpr char defaultMtu[] = "1280";
|
||||||
#else
|
#else
|
||||||
constexpr char defaultMtu[] = "1376";
|
constexpr char defaultMtu[] = "1376";
|
||||||
|
|
|
||||||
|
|
@ -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) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (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) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (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);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "connectionController.h"
|
#include "connectionController.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
@ -32,8 +32,9 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
||||||
|
|
||||||
void ConnectionController::openConnection()
|
void ConnectionController::openConnection()
|
||||||
{
|
{
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
|
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||||
|
{
|
||||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -570,7 +570,7 @@ void ImportController::startDecodingQr()
|
||||||
m_totalQrCodeChunksCount = 0;
|
m_totalQrCodeChunksCount = 0;
|
||||||
m_receivedQrCodeChunksCount = 0;
|
m_receivedQrCodeChunksCount = 0;
|
||||||
|
|
||||||
#if defined Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
m_isQrCodeProcessed = true;
|
m_isQrCodeProcessed = true;
|
||||||
#endif
|
#endif
|
||||||
#if defined Q_OS_ANDROID
|
#if defined Q_OS_ANDROID
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#include "utils/converter.h"
|
#include "utils/converter.h"
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#else
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
#if defined Q_OS_MAC
|
#if defined Q_OS_MAC && !defined(MACOS_NE)
|
||||||
#include "ui/macos_util.h"
|
#include "ui/macos_util.h"
|
||||||
#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
|
#elif defined Q_OS_MACX and !defined MACOS_NE
|
||||||
setDockIconVisible(false);
|
setDockIconVisible(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ bool SettingsController::isLoggingEnabled()
|
||||||
void SettingsController::toggleLogging(bool enable)
|
void SettingsController::toggleLogging(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setSaveLogs(enable);
|
m_settings->setSaveLogs(enable);
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS)
|
||||||
AmneziaVPN::toggleLogging(enable);
|
AmneziaVPN::toggleLogging(enable);
|
||||||
#endif
|
#endif
|
||||||
if (enable == true) {
|
if (enable == true) {
|
||||||
|
|
@ -131,8 +131,12 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||||
|
|
||||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QFile file(fileName);
|
||||||
SystemController::readFile(fileName, data);
|
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QByteArray data = file.readAll();
|
||||||
|
|
||||||
restoreAppConfigFromData(data);
|
restoreAppConfigFromData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +173,7 @@ void SettingsController::clearSettings()
|
||||||
|
|
||||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
AmneziaVPN::clearSettings();
|
AmneziaVPN::clearSettings();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include "core/controllers/serverController.h"
|
#include "core/controllers/serverController.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include <AmneziaVPN-Swift.h>
|
#include <AmneziaVPN-Swift.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -760,7 +760,7 @@ void ServersModel::removeApiConfig(const int serverIndex)
|
||||||
{
|
{
|
||||||
auto serverConfig = getServerConfig(serverIndex);
|
auto serverConfig = getServerConfig(serverIndex);
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
QString vpncName = QString("%1 (%2) %3")
|
QString vpncName = QString("%1 (%2) %3")
|
||||||
.arg(serverConfig[config_key::description].toString())
|
.arg(serverConfig[config_key::description].toString())
|
||||||
.arg(serverConfig[config_key::hostName].toString())
|
.arg(serverConfig[config_key::hostName].toString())
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "notificationhandler.h"
|
#include "notificationhandler.h"
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
# include "platforms/ios/iosnotificationhandler.h"
|
# 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) || defined(MACOS_NE)
|
||||||
return new IOSNotificationHandler(parent);
|
return new IOSNotificationHandler(parent);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,29 +25,32 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
defaultActiveFocusItem: focusItem
|
||||||
id: listView
|
|
||||||
|
|
||||||
anchors.fill: parent
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
|
||||||
property bool isFocusable: true
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {}
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
model: variants
|
spacing: 0
|
||||||
|
|
||||||
clip: true
|
Item {
|
||||||
|
id: focusItem
|
||||||
|
KeyNavigation.tab: textKey.textField
|
||||||
|
}
|
||||||
|
|
||||||
reuseItems: true
|
|
||||||
|
|
||||||
header: ColumnLayout {
|
|
||||||
width: listView.width
|
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
id: moreButton
|
|
||||||
|
|
||||||
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
@ -57,7 +60,7 @@ PageType {
|
||||||
|
|
||||||
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
|
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
|
||||||
actionButtonFunction: function() {
|
actionButtonFunction: function() {
|
||||||
moreActionsDrawer.openTriggered()
|
moreActionsDrawer.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
|
|
@ -68,7 +71,7 @@ PageType {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: root.height * 0.5
|
expandedHeight: root.height * 0.5
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
expandedContent: ColumnLayout {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
@ -128,8 +131,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
objectName: "insertKeyLabel"
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: 32
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
@ -153,6 +154,8 @@ PageType {
|
||||||
textField.text = ""
|
textField.text = ""
|
||||||
textField.paste()
|
textField.paste()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyNavigation.tab: continueButton
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
|
@ -163,12 +166,13 @@ PageType {
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
visible: textKey.textField.text !== ""
|
visible: textKey.textFieldText !== ""
|
||||||
|
|
||||||
text: qsTr("Continue")
|
text: qsTr("Continue")
|
||||||
|
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (ImportController.extractConfigFromData(textKey.textField.text)) {
|
if (ImportController.extractConfigFromData(textKey.textFieldText)) {
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,157 +188,143 @@ PageType {
|
||||||
color: AmneziaStyle.color.charcoalGray
|
color: AmneziaStyle.color.charcoalGray
|
||||||
text: qsTr("Other connection options")
|
text: qsTr("Other connection options")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
|
||||||
width: listView.width
|
|
||||||
|
|
||||||
CardWithIconsType {
|
CardWithIconsType {
|
||||||
|
id: apiInstalling
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
visible: isVisible
|
headerText: qsTr("VPN by Amnezia")
|
||||||
|
bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia")
|
||||||
headerText: title
|
|
||||||
bodyText: description
|
|
||||||
|
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
leftImageSource: imageSource
|
leftImageSource: "qrc:/images/controls/amnezia.svg"
|
||||||
|
|
||||||
onClicked: { handler() }
|
onClicked: function() {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
var result = InstallController.fillAvailableServices()
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
if (result) {
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardApiServicesList)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
footer: ColumnLayout {
|
CardWithIconsType {
|
||||||
width: listView.width
|
id: manualInstalling
|
||||||
|
|
||||||
BasicButtonType {
|
Layout.fillWidth: true
|
||||||
id: siteLink2
|
Layout.rightMargin: 16
|
||||||
Layout.topMargin: 24
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
implicitHeight: 32
|
|
||||||
|
|
||||||
visible: Qt.platform.os !== "ios"
|
headerText: qsTr("Self-hosted VPN")
|
||||||
|
bodyText: qsTr("Configure Amnezia VPN on your own server")
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
leftImageSource: "qrc:/images/controls/server.svg"
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
disabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.goldenApricot
|
|
||||||
|
|
||||||
text: qsTr("Site Amnezia")
|
onClicked: {
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
CardWithIconsType {
|
||||||
|
id: backupRestore
|
||||||
|
|
||||||
clickedFunc: function() {
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
visible: PageController.isStartPageVisible()
|
||||||
|
|
||||||
|
headerText: qsTr("Restore from backup")
|
||||||
|
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
leftImageSource: "qrc:/images/controls/archive-restore.svg"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var filePath = SystemController.getFileName(qsTr("Open backup file"),
|
||||||
|
qsTr("Backup files (*.backup)"))
|
||||||
|
if (filePath !== "") {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
SettingsController.restoreAppConfig(filePath)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardWithIconsType {
|
||||||
|
id: openFile
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("File with connection settings")
|
||||||
|
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
leftImageSource: "qrc:/images/controls/folder-search-2.svg"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
|
||||||
|
"Config files (*.vpn *.ovpn *.conf *.json)"
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
||||||
|
if (fileName !== "") {
|
||||||
|
if (ImportController.extractConfigFromFile(fileName)) {
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardWithIconsType {
|
||||||
|
id: scanQr
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
visible: (Qt.platform.os === "android" || Qt.platform.os === "ios") && SettingsController.isCameraPresent()
|
||||||
|
|
||||||
|
headerText: qsTr("QR code")
|
||||||
|
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
leftImageSource: "qrc:/images/controls/scan-line.svg"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
ImportController.startDecodingQr()
|
||||||
|
if (Qt.platform.os === "ios") {
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardQrReader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardWithIconsType {
|
||||||
|
id: siteLink
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
visible: PageController.isStartPageVisible()
|
||||||
|
|
||||||
|
headerText: qsTr("I have nothing")
|
||||||
|
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
leftImageSource: "qrc:/images/controls/help-circle.svg"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property list<QtObject> variants: [
|
|
||||||
amneziaVpn,
|
|
||||||
selfHostVpn,
|
|
||||||
backupRestore,
|
|
||||||
fileOpen,
|
|
||||||
qrScan,
|
|
||||||
siteLink
|
|
||||||
]
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: amneziaVpn
|
|
||||||
|
|
||||||
property string title: qsTr("VPN by Amnezia")
|
|
||||||
property string description: qsTr("Connect to classic paid and free VPN services from Amnezia")
|
|
||||||
property string imageSource: "qrc:/images/controls/amnezia.svg"
|
|
||||||
property bool isVisible: true
|
|
||||||
property var handler: function() {
|
|
||||||
PageController.showBusyIndicator(true)
|
|
||||||
var result = ApiConfigsController.fillAvailableServices()
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
if (result) {
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardApiServicesList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: selfHostVpn
|
|
||||||
|
|
||||||
property string title: qsTr("Self-hosted VPN")
|
|
||||||
property string description: qsTr("Configure Amnezia VPN on your own server")
|
|
||||||
property string imageSource: "qrc:/images/controls/server.svg"
|
|
||||||
property bool isVisible: true
|
|
||||||
property var handler: function() {
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: backupRestore
|
|
||||||
|
|
||||||
property string title: qsTr("Restore from backup")
|
|
||||||
property string description: qsTr("")
|
|
||||||
property string imageSource: "qrc:/images/controls/archive-restore.svg"
|
|
||||||
property bool isVisible: PageController.isStartPageVisible()
|
|
||||||
property var handler: function() {
|
|
||||||
var filePath = SystemController.getFileName(qsTr("Open backup file"),
|
|
||||||
qsTr("Backup files (*.backup)"))
|
|
||||||
if (filePath !== "") {
|
|
||||||
PageController.showBusyIndicator(true)
|
|
||||||
SettingsController.restoreAppConfig(filePath)
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: fileOpen
|
|
||||||
|
|
||||||
property string title: qsTr("File with connection settings")
|
|
||||||
property string description: qsTr("")
|
|
||||||
property string imageSource: "qrc:/images/controls/folder-search-2.svg"
|
|
||||||
property bool isVisible: true
|
|
||||||
property var handler: function() {
|
|
||||||
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
|
|
||||||
"Config files (*.vpn *.ovpn *.conf *.json)"
|
|
||||||
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
|
||||||
if (fileName !== "") {
|
|
||||||
if (ImportController.extractConfigFromFile(fileName)) {
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: qrScan
|
|
||||||
|
|
||||||
property string title: qsTr("QR code")
|
|
||||||
property string description: qsTr("")
|
|
||||||
property string imageSource: "qrc:/images/controls/scan-line.svg"
|
|
||||||
property bool isVisible: SettingsController.isCameraPresent()
|
|
||||||
property var handler: function() {
|
|
||||||
ImportController.startDecodingQr()
|
|
||||||
if (Qt.platform.os === "ios") {
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardQrReader)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: siteLink
|
|
||||||
|
|
||||||
property string title: qsTr("I have nothing")
|
|
||||||
property string description: qsTr("")
|
|
||||||
property string imageSource: "qrc:/images/controls/help-circle.svg"
|
|
||||||
property bool isVisible: PageController.isStartPageVisible() && Qt.platform.os !== "ios"
|
|
||||||
property var handler: function() {
|
|
||||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ bool Utils::processIsRunning(const QString &fileName, const bool fullFlag)
|
||||||
CloseHandle(hSnapshot);
|
CloseHandle(hSnapshot);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
|
#elif defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(MACOS_NE)
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
#include "platforms/android/android_controller.h"
|
#include "platforms/android/android_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent
|
||||||
: QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this))
|
: QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this))
|
||||||
{
|
{
|
||||||
m_checkTimer.setInterval(1000);
|
m_checkTimer.setInterval(1000);
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged);
|
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged);
|
||||||
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
|
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
if (state == Vpn::ConnectionState::Connected) {
|
if (state == Vpn::ConnectionState::Connected) {
|
||||||
m_checkTimer.start();
|
m_checkTimer.start();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -215,10 +215,11 @@ ErrorCode VpnConnection::lastError() const
|
||||||
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &vpnConfiguration)
|
const QJsonObject &vpnConfiguration)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Trying to connect to VPN, server index is %1, container is %2")
|
qDebug() << QString("ConnectToVpn, Server index is %1, container is %2, route mode is")
|
||||||
.arg(serverIndex)
|
.arg(serverIndex)
|
||||||
.arg(ContainerProps::containerToString(container));
|
.arg(ContainerProps::containerToString(container))
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
<< m_settings->routeMode();
|
||||||
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
if (!m_IpcClient) {
|
if (!m_IpcClient) {
|
||||||
m_IpcClient = new IpcClient(this);
|
m_IpcClient = new IpcClient(this);
|
||||||
}
|
}
|
||||||
|
|
@ -249,7 +250,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||||
|
|
||||||
appendSplitTunnelingConfig();
|
appendSplitTunnelingConfig();
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
|
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
|
||||||
if (!m_vpnProtocol) {
|
if (!m_vpnProtocol) {
|
||||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||||
|
|
@ -261,7 +262,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||||
createAndroidConnections();
|
createAndroidConnections();
|
||||||
|
|
||||||
m_vpnProtocol.reset(androidVpnProtocol);
|
m_vpnProtocol.reset(androidVpnProtocol);
|
||||||
#elif defined Q_OS_IOS
|
#elif defined Q_OS_IOS || defined(MACOS_NE)
|
||||||
Proto proto = ContainerProps::defaultProtocol(container);
|
Proto proto = ContainerProps::defaultProtocol(container);
|
||||||
IosController::Instance()->connectVpn(proto, m_vpnConfiguration);
|
IosController::Instance()->connectVpn(proto, m_vpnConfiguration);
|
||||||
connect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
connect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
||||||
|
|
@ -340,26 +341,26 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::RouteMode sitesRouteMode = Settings::RouteMode::VpnAllSites;
|
Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites;
|
||||||
QJsonArray sitesJsonArray;
|
QJsonArray sitesJsonArray;
|
||||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
sitesRouteMode = m_settings->routeMode();
|
routeMode = m_settings->routeMode();
|
||||||
|
|
||||||
if (allowSiteBasedSplitTunneling) {
|
if (allowSiteBasedSplitTunneling) {
|
||||||
auto sites = m_settings->getVpnIps(sitesRouteMode);
|
auto sites = m_settings->getVpnIps(routeMode);
|
||||||
for (const auto &site : sites) {
|
for (const auto &site : sites) {
|
||||||
sitesJsonArray.append(site);
|
sitesJsonArray.append(site);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow traffic to Amnezia DNS
|
// Allow traffic to Amnezia DNS
|
||||||
if (sitesRouteMode == Settings::VpnOnlyForwardSites) {
|
if (routeMode == Settings::VpnOnlyForwardSites) {
|
||||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
|
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
|
||||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
|
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_vpnConfiguration.insert(config_key::splitTunnelType, sitesRouteMode);
|
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
|
||||||
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
|
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
|
||||||
|
|
||||||
Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
|
Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
|
||||||
|
|
@ -375,13 +376,6 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||||
|
|
||||||
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);
|
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);
|
||||||
m_vpnConfiguration.insert(config_key::splitTunnelApps, appsJsonArray);
|
m_vpnConfiguration.insert(config_key::splitTunnelApps, appsJsonArray);
|
||||||
|
|
||||||
qDebug() << QString("Site split tunneling is %1, route mode is %2")
|
|
||||||
.arg(m_settings->isSitesSplitTunnelingEnabled() ? "enabled" : "disabled")
|
|
||||||
.arg(sitesRouteMode);
|
|
||||||
qDebug() << QString("App split tunneling is %1, route mode is %2")
|
|
||||||
.arg(m_settings->isAppsSplitTunnelingEnabled() ? "enabled" : "disabled")
|
|
||||||
.arg(appsRouteMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
|
@ -443,7 +437,7 @@ void VpnConnection::disconnectFromVpn()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_IOS
|
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||||
IosController::Instance()->disconnectVpn();
|
IosController::Instance()->disconnectVpn();
|
||||||
disconnect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
disconnect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue