Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/client-management

This commit is contained in:
vladimir.kuznetsov 2023-11-29 17:26:26 +03:00
commit 02efd9c217
21 changed files with 276 additions and 56 deletions

View file

@ -1,7 +1,12 @@
name: 'Deploy workflow' name: 'Deploy workflow'
on:
push:
branches:
- '**'
on: [push] env:
QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/
jobs: jobs:
Build-Linux-Ubuntu: Build-Linux-Ubuntu:
@ -25,7 +30,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -89,7 +94,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Setup mvsc' - name: 'Setup mvsc'
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
@ -119,15 +124,14 @@ jobs:
# ------------------------------------------------------ # ------------------------------------------------------
Build-IOS: Build-iOS:
name: 'Build-IOS' name: 'Build-iOS'
runs-on: macos-12 runs-on: macos-12
env: env:
QT_VERSION: 6.5.2 QT_VERSION: 6.5.2
steps: steps:
# Just select XCode
- name: 'Setup xcode' - name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1 uses: maxim-lobanov/setup-xcode@v1
with: with:
@ -143,6 +147,7 @@ jobs:
arch: 'clang_64' arch: 'clang_64'
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
set-env: 'true' set-env: 'true'
extra: '--base ${{ env.QT_MIRROR }}'
- name: 'Install iOS Qt' - name: 'Install iOS Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
@ -154,7 +159,7 @@ jobs:
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true' setup-python: 'true'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install go' - name: 'Install go'
uses: actions/setup-go@v3 uses: actions/setup-go@v3
@ -174,7 +179,7 @@ jobs:
- name: 'Setup ccache' - name: 'Setup ccache'
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1.2
- name: Install dependencies - name: 'Install dependencies'
run: pip install jsonschema jinja2 run: pip install jsonschema jinja2
- name: 'Build project' - name: 'Build project'
@ -232,7 +237,7 @@ jobs:
setup-python: 'true' setup-python: 'true'
tools: 'tools_ifw' tools: 'tools_ifw'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Get sources' - name: 'Get sources'
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -296,7 +301,7 @@ jobs:
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true' setup-python: 'true'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android Qt' - name: 'Install android Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
@ -309,7 +314,7 @@ jobs:
dir: ${{ runner.temp }} dir: ${{ runner.temp }}
setup-python: 'true' setup-python: 'true'
set-env: 'true' set-env: 'true'
extra: '--external 7z' extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake' - name: 'Grant execute permission for qt-cmake'
shell: bash shell: bash

64
.github/workflows/tag-upload.yml vendored Normal file
View file

@ -0,0 +1,64 @@
name: 'Upload a new version'
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
jobs:
upload:
runs-on: ubuntu-latest
name: upload
steps:
- name: Checkout CMakeLists.txt
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
sparse-checkout: |
CMakeLists.txt
sparse-checkout-cone-mode: false
- name: Verify git tag
run: |
GIT_TAG=${{ github.ref_name }}
CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/')
if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..."
else
echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..."
exit 1
fi
- name: Download artifacts from the "${{ github.ref_name }}" tag
uses: robinraju/release-downloader@v1.8
with:
tag: ${{ github.ref_name }}
fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*"
out-file-path: ${{ github.ref_name }}
- name: Upload beta version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'dev')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: beta/${{ github.ref_name }}
- name: Upload stable version
uses: jakejarvis/s3-sync-action@master
if: contains(github.event.base_ref, 'master')
with:
args: --include "AmneziaVPN*" --delete
env:
AWS_S3_BUCKET: updates
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com
SOURCE_DIR: ${{ github.ref_name }}
DEST_DIR: stable/${{ github.ref_name }}

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.1.0.0 project(${PROJECT} VERSION 4.1.0.1
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )

@ -1 +1 @@
Subproject commit ac32d33555bd62f0b0af314b1e5119d6d78a1a4e Subproject commit fcf3022a2724402f68cc11bcbed9b43ea9ffcc07

View file

@ -288,6 +288,8 @@ void AmneziaApplication::initModels()
&ContainersModel::setCurrentlyProcessedServerIndex); &ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(), connect(m_serversModel.get(), &ServersModel::defaultServerIndexChanged, m_containersModel.get(),
&ContainersModel::setCurrentlyProcessedServerIndex); &ContainersModel::setCurrentlyProcessedServerIndex);
connect(m_containersModel.get(), &ContainersModel::containersModelUpdated, m_serversModel.get(),
&ServersModel::updateContainersConfig);
m_languageModel.reset(new LanguageModel(m_settings, this)); m_languageModel.reset(new LanguageModel(m_settings, this));
m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get()); m_engine->rootContext()->setContextProperty("LanguageModel", m_languageModel.get());
@ -357,7 +359,7 @@ void AmneziaApplication::initControllers()
m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings)); m_settingsController.reset(new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get()); m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
if (m_settingsController->isAutoStartEnabled() && m_serversModel->getDefaultServerIndex() >= 0) { if (m_settingsController->isAutoConnectEnabled() && m_serversModel->getDefaultServerIndex() >= 0) {
QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); });
} }

View file

@ -36,7 +36,7 @@ enum ErrorCode
ServerPacketManagerError, ServerPacketManagerError,
// Ssh connection errors // Ssh connection errors
SshRequsetDeniedError, SshInterruptedError, SshInternalError, SshRequestDeniedError, SshInterruptedError, SshInternalError,
SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError, SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError,
// Ssh sftp errors // Ssh sftp errors
@ -47,7 +47,6 @@ enum ErrorCode
SshSftpNoMediaError, SshSftpNoMediaError,
// Local errors // Local errors
FailedToSaveConfigData,
OpenVpnConfigMissing, OpenVpnConfigMissing,
OpenVpnManagementServerError, OpenVpnManagementServerError,
ConfigMissing, ConfigMissing,
@ -67,7 +66,6 @@ enum ErrorCode
// 3rd party utils errors // 3rd party utils errors
OpenSslFailed, OpenSslFailed,
OpenVpnExecutableCrashed,
ShadowSocksExecutableCrashed, ShadowSocksExecutableCrashed,
CloakExecutableCrashed, CloakExecutableCrashed,

View file

@ -19,7 +19,7 @@ QString errorString(ErrorCode code){
case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo"); case(ServerUserNotInSudo): return QObject::tr("The user does not have permission to use sudo");
// Libssh errors // Libssh errors
case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied"); case(SshRequestDeniedError): return QObject::tr("Ssh request was denied");
case(SshInterruptedError): return QObject::tr("Ssh request was interrupted"); case(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
case(SshInternalError): return QObject::tr("Ssh internal error"); case(SshInternalError): return QObject::tr("Ssh internal error");
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered"); case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
@ -42,7 +42,6 @@ QString errorString(ErrorCode code){
case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive"); case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive");
// Local errors // Local errors
case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk");
case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing"); case (OpenVpnConfigMissing): return QObject::tr("OpenVPN config missing");
case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error"); case (OpenVpnManagementServerError): return QObject::tr("OpenVPN management server error");

View file

@ -167,11 +167,8 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return ErrorCode::ServerContainerMissingError; return ErrorCode::ServerContainerMissingError;
} }
runScript(credentials, runScript(credentials,
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container))); replaceVars(QString("sudo shred -u %1").arg(tmpFileName), genVarsForScript(credentials, container)));
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
return e; return e;
} }

View file

@ -333,7 +333,7 @@ namespace libssh {
switch (errorCode) { switch (errorCode) {
case(SSH_NO_ERROR): return ErrorCode::NoError; case(SSH_NO_ERROR): return ErrorCode::NoError;
case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError; case(SSH_REQUEST_DENIED): return ErrorCode::SshRequestDeniedError;
case(SSH_EINTR): return ErrorCode::SshInterruptedError; case(SSH_EINTR): return ErrorCode::SshInterruptedError;
case(SSH_FATAL): return ErrorCode::SshInternalError; case(SSH_FATAL): return ErrorCode::SshInternalError;
default: return ErrorCode::SshInternalError; default: return ErrorCode::SshInternalError;

View file

@ -1,19 +1,19 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); docker_pkg="docker.io"; dist="debian";\ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); docker_pkg="docker"; dist="fedora";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); docker_pkg="docker"; dist="centos";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
else echo "Packet manager not found"; exit 1; fi;\ else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Docker pkg: $docker_pkg";\ echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\ if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
if ! command -v sudo > /dev/null 2>&1; then $pm update -yq; $pm install -yq sudo; fi;\ if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
if ! command -v fuser > /dev/null 2>&1; then sudo $pm install -yq psmisc; fi;\ if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
if ! command -v lsof > /dev/null 2>&1; then sudo $pm install -yq lsof; fi;\ if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
if ! command -v docker > /dev/null 2>&1; then sudo $pm update -yq; sudo $pm install -yq $docker_pkg;\ if ! command -v docker > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\ if [ "$dist" = "fedora" ] || [ "$dist" = "centos" ] || [ "$dist" = "debian" ]; then sudo systemctl enable docker && sudo systemctl start docker; fi;\
fi;\ fi;\
if [ "$dist" = "debian" ]; then \ if [ "$dist" = "debian" ]; then \
docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\ docker_service=$(systemctl list-units --full --all | grep docker.service | grep -v inactive | grep -v dead | grep -v failed);\
if [ -z "$docker_service" ]; then sudo $pm update -yq; sudo $pm install -yq curl $docker_pkg; fi;\ if [ -z "$docker_service" ]; then sudo $pm $check_pkgs; sudo $pm $silent_inst curl $docker_pkg; fi;\
sleep 3 && sudo systemctl start docker && sleep 3;\ sleep 3 && sudo systemctl start docker && sleep 3;\
fi;\ fi;\
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker";exit 1;fi;\ if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install Docker"; exit 1; fi;\
docker --version docker --version

View file

@ -206,7 +206,8 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
m_config.append(line + "\n"); m_config.append(line + "\n");
} }
m_qrCodes = generateQrCodeImageSeries(m_config.toUtf8()); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials); errorCode = m_clientManagementModel->appendClient(clientId, clientName, container, credentials);
if (errorCode) { if (errorCode) {
@ -217,11 +218,91 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
emit exportConfigChanged(); emit exportConfigChanged();
} }
void ExportController::generateShadowSocksConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container,
containerConfig, &errorCode);
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
m_nativeConfigString =
QString("%1:%2@%3:%4")
.arg(configJson.value("method").toString(), configJson.value("password").toString(),
configJson.value("server").toString(), configJson.value("server_port").toString());
m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64();
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_nativeConfigString.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
void ExportController::generateCloakConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config =
m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Cloak, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
configJson.remove(config_key::transport_proto);
configJson.insert("ProxyMethod", "shadowsocks");
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
emit exportConfigChanged();
}
QString ExportController::getConfig() QString ExportController::getConfig()
{ {
return m_config; return m_config;
} }
QString ExportController::getNativeConfigString()
{
return m_nativeConfigString;
}
QList<QString> ExportController::getQrCodes() QList<QString> ExportController::getQrCodes()
{ {
return m_qrCodes; return m_qrCodes;
@ -270,7 +351,7 @@ QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &dat
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
QString svg = QString::fromStdString(toSvgString(qr, 0)); QString svg = QString::fromStdString(toSvgString(qr, 1));
chunks.append(svgToBase64(svg)); chunks.append(svgToBase64(svg));
} }
@ -290,5 +371,6 @@ int ExportController::getQrCodesCount()
void ExportController::clearPreviousConfig() void ExportController::clearPreviousConfig()
{ {
m_config.clear(); m_config.clear();
m_nativeConfigString.clear();
m_qrCodes.clear(); m_qrCodes.clear();
} }

View file

@ -24,6 +24,7 @@ public:
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged) Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY exportConfigChanged)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged) Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY exportConfigChanged)
Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged) Q_PROPERTY(QString config READ getConfig NOTIFY exportConfigChanged)
Q_PROPERTY(QString nativeConfigString READ getNativeConfigString NOTIFY exportConfigChanged)
public slots: public slots:
void generateFullAccessConfig(); void generateFullAccessConfig();
@ -33,8 +34,11 @@ public slots:
void generateConnectionConfig(const QString &clientName); void generateConnectionConfig(const QString &clientName);
void generateOpenVpnConfig(const QString &clientName); void generateOpenVpnConfig(const QString &clientName);
void generateWireGuardConfig(const QString &clientName); void generateWireGuardConfig(const QString &clientName);
void generateShadowSocksConfig();
void generateCloakConfig();
QString getConfig(); QString getConfig();
QString getNativeConfigString();
QList<QString> getQrCodes(); QList<QString> getQrCodes();
void exportConfig(const QString &fileName); void exportConfig(const QString &fileName);
@ -66,6 +70,7 @@ private:
std::shared_ptr<VpnConfigurator> m_configurator; std::shared_ptr<VpnConfigurator> m_configurator;
QString m_config; QString m_config;
QString m_nativeConfigString;
QList<QString> m_qrCodes; QList<QString> m_qrCodes;
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID

View file

@ -22,10 +22,6 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
DockerContainer container = ContainerProps::allContainers().at(index.row()); DockerContainer container = ContainerProps::allContainers().at(index.row());
switch (role) { switch (role) {
case NameRole:
// return ContainerProps::containerHumanNames().value(container);
case DescriptionRole:
// return ContainerProps::containerDescriptions().value(container);
case ConfigRole: { case ConfigRole: {
m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject()); m_settings->setContainerConfig(m_currentlyProcessedServerIndex, container, value.toJsonObject());
m_containers = m_settings->containers(m_currentlyProcessedServerIndex); m_containers = m_settings->containers(m_currentlyProcessedServerIndex);
@ -35,19 +31,15 @@ bool ContainersModel::setData(const QModelIndex &index, const QVariant &value, i
break; break;
} }
} }
case ServiceTypeRole:
// return ContainerProps::containerService(container);
case DockerContainerRole:
// return container;
case IsInstalledRole:
// return m_settings->containers(m_currentlyProcessedServerIndex).contains(container);
case IsDefaultRole: { //todo remove case IsDefaultRole: { //todo remove
m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container); m_settings->setDefaultContainer(m_currentlyProcessedServerIndex, container);
m_defaultContainerIndex = container; m_defaultContainerIndex = container;
emit defaultContainerChanged(); emit defaultContainerChanged();
} }
default: break;
} }
emit containersModelUpdated();
emit dataChanged(index, index); emit dataChanged(index, index);
return true; return true;
} }

View file

@ -73,6 +73,7 @@ protected:
signals: signals:
void defaultContainerChanged(); void defaultContainerChanged();
void containersModelUpdated();
private: private:
QMap<DockerContainer, QJsonObject> m_containers; QMap<DockerContainer, QJsonObject> m_containers;

View file

@ -198,6 +198,12 @@ bool ServersModel::isDefaultServerConfigContainsAmneziaDns()
return primaryDns == protocols::dns::amneziaDnsIp; return primaryDns == protocols::dns::amneziaDnsIp;
} }
void ServersModel::updateContainersConfig()
{
auto server = m_settings->server(m_currentlyProcessedServerIndex);
m_servers.replace(m_currentlyProcessedServerIndex, server);
}
QHash<int, QByteArray> ServersModel::roleNames() const QHash<int, QByteArray> ServersModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;

View file

@ -60,6 +60,8 @@ public slots:
bool isDefaultServerConfigContainsAmneziaDns(); bool isDefaultServerConfigContainsAmneziaDns();
void updateContainersConfig();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;

View file

@ -112,6 +112,30 @@ DrawerType {
} }
} }
BasicButtonType {
Layout.fillWidth: true
Layout.topMargin: 8
visible: nativeConfigString.text !== ""
defaultColor: "transparent"
hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: Qt.rgba(1, 1, 1, 0.12)
disabledColor: "#878B91"
textColor: "#D7D8DB"
borderWidth: 1
text: qsTr("Copy config string")
imageSource: "qrc:/images/controls/copy.svg"
onClicked: {
nativeConfigString.selectAll()
nativeConfigString.copy()
nativeConfigString.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
BasicButtonType { BasicButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
@ -170,6 +194,12 @@ DrawerType {
} }
TextField { TextField {
id: nativeConfigString
visible: false
text: ExportController.nativeConfigString
}
TextArea {
id: configText id: configText
Layout.fillWidth: true Layout.fillWidth: true
@ -213,7 +243,6 @@ DrawerType {
Image { Image {
anchors.fill: parent anchors.fill: parent
anchors.margins: 2
smooth: false smooth: false
source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : "" source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : ""

View file

@ -54,7 +54,7 @@ PageType {
regularExpression: InstallController.ipAddressPortRegExp() regularExpression: InstallController.ipAddressPortRegExp()
} }
onTextFieldTextChanged: { onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, ''); textField.text = textField.text.replace(/^\s+|\s+$/g, '');
} }
} }
@ -81,6 +81,10 @@ PageType {
clickedFunc: function() { clickedFunc: function() {
hidePassword = !hidePassword hidePassword = !hidePassword
} }
onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '');
}
} }
BasicButtonType { BasicButtonType {
@ -90,6 +94,7 @@ PageType {
text: qsTr("Continue") text: qsTr("Continue")
onClicked: function() { onClicked: function() {
forceActiveFocus()
if (!isCredentialsFilled()) { if (!isCredentialsFilled()) {
return return
} }
@ -112,8 +117,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 12 Layout.topMargin: 12
text: qsTr("All data you enter will remain strictly confidential text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
and will not be shared or disclosed to the Amnezia or any third parties")
} }
} }
} }

View file

@ -24,7 +24,7 @@ PageType {
} }
function onImportFinished() { function onImportFinished() {
if (ConnectionController.isConnected) { if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1); ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
} }

View file

@ -19,7 +19,9 @@ PageType {
enum ConfigType { enum ConfigType {
AmneziaConnection, AmneziaConnection,
OpenVpn, OpenVpn,
WireGuard WireGuard,
ShadowSocks,
Cloak
} }
signal revokeConfig(int index) signal revokeConfig(int index)
@ -51,14 +53,28 @@ PageType {
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configExtension = ".ovpn"
shareConnectionDrawer.configFileName = "amnezia_for_openvpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
break; break
} }
case PageShare.ConfigType.WireGuard: { case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig(clientNameTextField.textFieldText) ExportController.generateWireGuardConfig(clientNameTextField.textFieldText)
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_wireguard" shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break; break
}
case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
break
}
case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig()
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break
} }
} }
@ -98,6 +114,16 @@ PageType {
property string name: qsTr("WireGuard native format") property string name: qsTr("WireGuard native format")
property var type: PageShare.ConfigType.WireGuard property var type: PageShare.ConfigType.WireGuard
} }
QtObject {
id: shadowSocksConnectionFormat
property string name: qsTr("ShadowSocks native format")
property var type: PageShare.ConfigType.ShadowSocks
}
QtObject {
id: cloakConnectionFormat
property string name: qsTr("Cloak native format")
property var type: PageShare.ConfigType.Cloak
}
FlickableType { FlickableType {
anchors.top: parent.top anchors.top: parent.top
@ -375,6 +401,13 @@ PageType {
root.connectionTypesModel.push(openVpnConnectionFormat) root.connectionTypesModel.push(openVpnConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) { } else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
root.connectionTypesModel.push(wireGuardConnectionFormat) root.connectionTypesModel.push(wireGuardConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-openvpn-cloak")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
root.connectionTypesModel.push(cloakConnectionFormat)
} }
} }
} }

View file

@ -380,7 +380,8 @@ void VpnConnection::appendSplitTunnelingConfig()
// Allow traffic to Amezia DNS // Allow traffic to Amezia DNS
if (routeMode == Settings::VpnOnlyForwardSites){ if (routeMode == Settings::VpnOnlyForwardSites){
sitesJsonArray.append(amnezia::protocols::dns::amneziaDnsIp); sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
} }
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);