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" };
|
||||
m_parser.addOption(c_cleanup);
|
||||
|
||||
|
||||
m_parser.process(*this);
|
||||
|
||||
if (m_parser.isSet(c_cleanup)) {
|
||||
|
@ -179,9 +179,8 @@ bool AmneziaApplication::parseCommands()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
void AmneziaApplication::startLocalServer()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
void AmneziaApplication::startLocalServer() {
|
||||
const QString serverName("AmneziaVPNInstance");
|
||||
QLocalServer::removeServer(serverName);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#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>
|
||||
#else
|
||||
#include <QApplication>
|
||||
|
@ -19,7 +19,7 @@
|
|||
|
||||
#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
|
||||
#else
|
||||
#define AMNEZIA_BASE_CLASS QApplication
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
void loadFonts();
|
||||
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();
|
||||
#endif
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <QString>
|
||||
#include <QTemporaryDir>
|
||||
#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>
|
||||
#else
|
||||
#include <QApplication>
|
||||
|
@ -120,7 +120,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
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
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
#endif
|
||||
|
@ -129,7 +129,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
|||
|
||||
// no redirect-gateway
|
||||
} 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");
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <QTemporaryFile>
|
||||
#include <QThread>
|
||||
#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>
|
||||
#else
|
||||
#include <QApplication>
|
||||
|
@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
|||
|
||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||
{
|
||||
#ifndef Q_OS_IOS
|
||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
|
@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
|||
#endif
|
||||
}
|
||||
|
||||
// DEAD CODE.
|
||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||
{
|
||||
#ifndef Q_OS_IOS
|
||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QProcess *p = new QProcess();
|
||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
|
||||
|
@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
|||
pathEnvVar.clear();
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||
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");
|
||||
#endif
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||
#ifdef Q_OS_WINDOWS
|
||||
return true;
|
||||
|
||||
#elif defined(Q_OS_IOS)
|
||||
#elif defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: 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 <unistd.h>
|
||||
#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/sysctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -378,7 +378,7 @@ QString NetworkUtilities::getGatewayAndIface()
|
|||
close(sock);
|
||||
return gateway_address;
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QString gateway;
|
||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
int afinet_type[] = {AF_INET, AF_INET6};
|
||||
|
|
|
@ -1,10 +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 "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
@ -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]);
|
||||
|
||||
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"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
bool isAnotherInstanceRunning()
|
||||
{
|
||||
QLocalSocket socket;
|
||||
|
@ -45,7 +45,7 @@ int main(int argc, char *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()) {
|
||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||
return app.exec();
|
||||
|
|
|
@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
|||
startHandler = completionHandler
|
||||
ovpnAdapter?.connect(using: packetFlow)
|
||||
}
|
||||
|
||||
|
||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
||||
|
|
|
@ -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] = [
|
||||
"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: []))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if !MACOS_NE
|
||||
#include "QRCodeReaderBase.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
@ -108,3 +109,19 @@ void QRCodeReader::startReading() {
|
|||
void 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>
|
||||
|
||||
#endif
|
||||
@interface QIOSApplicationDelegate
|
||||
@end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
|
||||
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
||||
|
||||
#if !MACOS_NE
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
||||
|
@ -57,5 +57,5 @@
|
|||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#endif
|
||||
@end
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
#if MACOS_NE
|
||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||
|
||||
}
|
||||
|
||||
class ScreenProtection {
|
||||
|
||||
|
||||
}
|
||||
#else
|
||||
import UIKit
|
||||
|
||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||
|
@ -90,3 +100,4 @@ struct ProtectionPair {
|
|||
textField.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
void disconnectVpn();
|
||||
|
||||
void vpnStatusDidChange(void *pNotification);
|
||||
|
||||
void vpnConfigurationDidChange(void *pNotification);
|
||||
|
||||
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::SplitTunnelSites = "SplitTunnelSites";
|
||||
|
||||
#if !MACOS_NE
|
||||
static UIViewController* getViewController() {
|
||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||
for (UIWindow *window in windows) {
|
||||
|
@ -36,6 +37,7 @@ static UIViewController* getViewController() {
|
|||
}
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||
switch (status) {
|
||||
|
@ -249,6 +251,19 @@ void IosController::checkStatus()
|
|||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||
|
||||
uint64_t last_handshake_time_sec = 0;
|
||||
if (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);
|
||||
m_rxBytes = rxBytes;
|
||||
m_txBytes = txBytes;
|
||||
|
@ -789,14 +804,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||
[sharingItems addObject:logFileUrl];
|
||||
}
|
||||
|
||||
#if !MACOS_NE
|
||||
UIViewController *qtController = getViewController();
|
||||
if (!qtController) return;
|
||||
|
||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||
|
||||
#endif
|
||||
__block bool isAccepted = false;
|
||||
|
||||
#if !MACOS_NE
|
||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||
isAccepted = completed;
|
||||
emit finished();
|
||||
|
@ -808,7 +823,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||
popController.sourceView = qtController.view;
|
||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||
}
|
||||
|
||||
#endif
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
@ -817,6 +832,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
|||
}
|
||||
|
||||
QString IosController::openFile() {
|
||||
#if !MACOS_NE
|
||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||
|
||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||
|
@ -826,9 +842,9 @@ QString IosController::openFile() {
|
|||
if (!qtController) return;
|
||||
|
||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||
|
||||
#endif
|
||||
__block QString filePath;
|
||||
|
||||
#if !MACOS_NE
|
||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||
if (path) {
|
||||
filePath = QString::fromUtf8(path.UTF8String);
|
||||
|
@ -837,7 +853,7 @@ QString IosController::openFile() {
|
|||
}
|
||||
emit finished();
|
||||
};
|
||||
|
||||
#endif
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#import <NetworkExtension/NetworkExtension.h>
|
||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if !MACOS_NE
|
||||
#include <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#include <Security/Security.h>
|
||||
|
||||
class IosController;
|
||||
|
@ -17,9 +21,10 @@ class IosController;
|
|||
@end
|
||||
|
||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||
|
||||
#if !MACOS_NE
|
||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||
|
||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
@end
|
||||
|
||||
@implementation DocumentPickerDelegate
|
||||
#if !MACOS_NE
|
||||
@implementation DocumentPickerDelegate
|
||||
|
||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||
for (NSURL *url in urls) {
|
||||
|
@ -42,4 +43,5 @@
|
|||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if !MACOS_NE
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@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";
|
||||
|
||||
#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";
|
||||
#else
|
||||
constexpr char defaultMtu[] = "1376";
|
||||
|
@ -197,7 +197,7 @@ namespace amnezia
|
|||
namespace awg
|
||||
{
|
||||
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";
|
||||
#else
|
||||
constexpr char defaultMtu[] = "1376";
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "core/errorstrings.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 "openvpnprotocol.h"
|
||||
#include "shadowsocksvpnprotocol.h"
|
||||
|
@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
|||
#if defined(Q_OS_WINDOWS)
|
||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||
#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::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#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>
|
||||
#else
|
||||
#include <QApplication>
|
||||
|
@ -32,8 +32,9 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||
|
||||
void ConnectionController::openConnection()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||
{
|
||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
|
@ -570,7 +570,7 @@ void ImportController::startDecodingQr()
|
|||
m_totalQrCodeChunksCount = 0;
|
||||
m_receivedQrCodeChunksCount = 0;
|
||||
|
||||
#if defined Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
m_isQrCodeProcessed = true;
|
||||
#endif
|
||||
#if defined Q_OS_ANDROID
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "utils/converter.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>
|
||||
#else
|
||||
#include <QApplication>
|
||||
|
@ -11,7 +11,7 @@
|
|||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
#if defined Q_OS_MAC
|
||||
#if defined Q_OS_MAC && !defined(MACOS_NE)
|
||||
#include "ui/macos_util.h"
|
||||
#endif
|
||||
|
||||
|
@ -114,7 +114,7 @@ void PageController::showOnStartup()
|
|||
} else {
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
emit hideMainWindow();
|
||||
#elif defined Q_OS_MACX
|
||||
#elif defined Q_OS_MACX and !defined MACOS_NE
|
||||
setDockIconVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <AmneziaVPN-Swift.h>
|
||||
#endif
|
||||
|
||||
|
@ -76,7 +76,7 @@ bool SettingsController::isLoggingEnabled()
|
|||
void SettingsController::toggleLogging(bool enable)
|
||||
{
|
||||
m_settings->setSaveLogs(enable);
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS)
|
||||
AmneziaVPN::toggleLogging(enable);
|
||||
#endif
|
||||
if (enable == true) {
|
||||
|
@ -131,8 +131,12 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
|||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
{
|
||||
QByteArray data;
|
||||
SystemController::readFile(fileName, data);
|
||||
QFile file(fileName);
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
restoreAppConfigFromData(data);
|
||||
}
|
||||
|
||||
|
@ -169,7 +173,7 @@ void SettingsController::clearSettings()
|
|||
|
||||
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();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "core/controllers/serverController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <AmneziaVPN-Swift.h>
|
||||
#endif
|
||||
|
||||
|
@ -760,7 +760,7 @@ void ServersModel::removeApiConfig(const int serverIndex)
|
|||
{
|
||||
auto serverConfig = getServerConfig(serverIndex);
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
QString vpncName = QString("%1 (%2) %3")
|
||||
.arg(serverConfig[config_key::description].toString())
|
||||
.arg(serverConfig[config_key::hostName].toString())
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
#include <QDebug>
|
||||
#include "notificationhandler.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
# include "platforms/ios/iosnotificationhandler.h"
|
||||
#else
|
||||
# include "systemtray_notificationhandler.h"
|
||||
#endif
|
||||
|
||||
|
||||
// static
|
||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||
#if defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
return new IOSNotificationHandler(parent);
|
||||
#else
|
||||
|
||||
|
|
|
@ -25,29 +25,32 @@ PageType {
|
|||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
defaultActiveFocusItem: focusItem
|
||||
|
||||
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 {
|
||||
id: moreButton
|
||||
|
||||
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
|
@ -57,7 +60,7 @@ PageType {
|
|||
|
||||
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
|
||||
actionButtonFunction: function() {
|
||||
moreActionsDrawer.openTriggered()
|
||||
moreActionsDrawer.open()
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
|
@ -68,7 +71,7 @@ PageType {
|
|||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.5
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
expandedContent: ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
@ -128,8 +131,6 @@ PageType {
|
|||
}
|
||||
|
||||
ParagraphTextType {
|
||||
objectName: "insertKeyLabel"
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.rightMargin: 16
|
||||
|
@ -153,6 +154,8 @@ PageType {
|
|||
textField.text = ""
|
||||
textField.paste()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: continueButton
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
|
@ -163,12 +166,13 @@ PageType {
|
|||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
visible: textKey.textField.text !== ""
|
||||
visible: textKey.textFieldText !== ""
|
||||
|
||||
text: qsTr("Continue")
|
||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
||||
|
||||
clickedFunc: function() {
|
||||
if (ImportController.extractConfigFromData(textKey.textField.text)) {
|
||||
if (ImportController.extractConfigFromData(textKey.textFieldText)) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
}
|
||||
}
|
||||
|
@ -184,157 +188,143 @@ PageType {
|
|||
color: AmneziaStyle.color.charcoalGray
|
||||
text: qsTr("Other connection options")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
CardWithIconsType {
|
||||
id: apiInstalling
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
visible: isVisible
|
||||
|
||||
headerText: title
|
||||
bodyText: description
|
||||
headerText: qsTr("VPN by Amnezia")
|
||||
bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia")
|
||||
|
||||
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 {
|
||||
width: listView.width
|
||||
CardWithIconsType {
|
||||
id: manualInstalling
|
||||
|
||||
BasicButtonType {
|
||||
id: siteLink2
|
||||
Layout.topMargin: 24
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 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
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/server.svg"
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
#else
|
||||
QProcess process;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#endif
|
||||
|
||||
|
@ -33,7 +33,7 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent
|
|||
: QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this))
|
||||
{
|
||||
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::bytesChanged, this, &VpnConnection::onBytesChanged);
|
||||
|
||||
|
@ -101,7 +101,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
if (state == Vpn::ConnectionState::Connected) {
|
||||
m_checkTimer.start();
|
||||
} else {
|
||||
|
@ -215,10 +215,11 @@ ErrorCode VpnConnection::lastError() const
|
|||
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
||||
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(ContainerProps::containerToString(container));
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
.arg(ContainerProps::containerToString(container))
|
||||
<< m_settings->routeMode();
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
if (!m_IpcClient) {
|
||||
m_IpcClient = new IpcClient(this);
|
||||
}
|
||||
|
@ -249,7 +250,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
|||
|
||||
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));
|
||||
if (!m_vpnProtocol) {
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
|
@ -261,7 +262,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
|||
createAndroidConnections();
|
||||
|
||||
m_vpnProtocol.reset(androidVpnProtocol);
|
||||
#elif defined Q_OS_IOS
|
||||
#elif defined Q_OS_IOS || defined(MACOS_NE)
|
||||
Proto proto = ContainerProps::defaultProtocol(container);
|
||||
IosController::Instance()->connectVpn(proto, m_vpnConfiguration);
|
||||
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;
|
||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
sitesRouteMode = m_settings->routeMode();
|
||||
routeMode = m_settings->routeMode();
|
||||
|
||||
if (allowSiteBasedSplitTunneling) {
|
||||
auto sites = m_settings->getVpnIps(sitesRouteMode);
|
||||
auto sites = m_settings->getVpnIps(routeMode);
|
||||
for (const auto &site : sites) {
|
||||
sitesJsonArray.append(site);
|
||||
}
|
||||
|
||||
// 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::dns2).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelType, sitesRouteMode);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
|
||||
|
||||
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::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
|
||||
|
@ -443,7 +437,7 @@ void VpnConnection::disconnectFromVpn()
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
IosController::Instance()->disconnectVpn();
|
||||
disconnect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue