added button 'Reset settings and remove all data from the application'
This commit is contained in:
parent
3aaa7b62ef
commit
75489c00c2
18 changed files with 585 additions and 432 deletions
|
@ -1,23 +1,23 @@
|
||||||
#include "servercontroller.h"
|
#include "servercontroller.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QTimer>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -25,15 +25,14 @@
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "scripts_registry.h"
|
||||||
#include "server_defs.h"
|
#include "server_defs.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "scripts_registry.h"
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#include <configurators/vpn_configurator.h>
|
#include <configurators/vpn_configurator.h>
|
||||||
|
|
||||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) :
|
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) : m_settings(settings)
|
||||||
m_settings(settings)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +41,10 @@ ServerController::~ServerController()
|
||||||
m_sshClient.disconnectFromHost();
|
m_sshClient.disconnectFromHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
|
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr) {
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
|
||||||
|
{
|
||||||
|
|
||||||
auto error = m_sshClient.connectToHost(credentials);
|
auto error = m_sshClient.connectToHost(credentials);
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
|
@ -82,7 +81,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
qDebug().noquote() << "EXEC" << lineToExec;
|
qDebug().noquote() << "EXEC" << lineToExec;
|
||||||
Logger::appendSshLog("Run command:" + lineToExec);
|
Logger::appendSshLog("Run command:" + lineToExec);
|
||||||
|
|
||||||
|
|
||||||
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
|
@ -93,36 +91,36 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials,
|
ErrorCode
|
||||||
DockerContainer container, QString script,
|
ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr)
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
|
||||||
{
|
{
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||||
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
|
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
|
||||||
e = runScript(credentials,
|
e = runScript(credentials, replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||||
replaceVars(runner, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
|
QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
|
||||||
runScript(credentials,
|
runScript(credentials, replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
||||||
replaceVars(remover, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
const ServerCredentials &credentials, const QString &file, const QString &path,
|
const QString &file, const QString &path,
|
||||||
libssh::SftpOverwriteMode overwriteMode)
|
libssh::SftpOverwriteMode overwriteMode)
|
||||||
{
|
{
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
|
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
|
||||||
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
|
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStd = [&](const QString &data, libssh::Client &) {
|
auto cbReadStd = [&](const QString &data, libssh::Client &) {
|
||||||
|
@ -131,61 +129,63 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
};
|
};
|
||||||
|
|
||||||
// mkdir
|
// mkdir
|
||||||
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"")
|
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
|
||||||
.arg(path);
|
|
||||||
|
|
||||||
e = runScript(credentials,
|
|
||||||
replaceVars(mkdir, genVarsForScript(credentials, container)));
|
|
||||||
if (e) return e;
|
|
||||||
|
|
||||||
|
e = runScript(credentials, replaceVars(mkdir, genVarsForScript(credentials, container)));
|
||||||
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) {
|
if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
genVarsForScript(credentials, container)),
|
||||||
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
if (e) return e;
|
if (e)
|
||||||
}
|
return e;
|
||||||
else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
|
} else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
genVarsForScript(credentials, container)),
|
||||||
|
cbReadStd, cbReadStd);
|
||||||
|
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
e = runScript(credentials,
|
e = runScript(
|
||||||
replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
credentials,
|
||||||
genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
|
replaceVars(
|
||||||
|
QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path),
|
||||||
if (e) return e;
|
genVarsForScript(credentials, container)),
|
||||||
}
|
cbReadStd, cbReadStd);
|
||||||
else return ErrorCode::NotImplementedError;
|
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
return e;
|
||||||
|
} else
|
||||||
|
return ErrorCode::NotImplementedError;
|
||||||
|
|
||||||
if (stdOut.contains("Error: No such container:")) {
|
if (stdOut.contains("Error: No such container:")) {
|
||||||
return ErrorCode::ServerContainerMissingError;
|
return ErrorCode::ServerContainerMissingError;
|
||||||
}
|
}
|
||||||
|
|
||||||
runScript(credentials,
|
runScript(credentials,
|
||||||
replaceVars(QString("sudo shred %1").arg(tmpFileName),
|
replaceVars(QString("sudo shred %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||||
genVarsForScript(credentials, container)));
|
|
||||||
|
|
||||||
runScript(credentials,
|
runScript(credentials, replaceVars(QString("sudo rm %1").arg(tmpFileName), genVarsForScript(credentials, container)));
|
||||||
replaceVars(QString("sudo rm %1").arg(tmpFileName),
|
|
||||||
genVarsForScript(credentials, container)));
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
QByteArray ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode)
|
const QString &path, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (errorCode) *errorCode = ErrorCode::NoError;
|
if (errorCode)
|
||||||
|
*errorCode = ErrorCode::NoError;
|
||||||
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").
|
|
||||||
arg(ContainerProps::containerToString(container)).arg(path);
|
|
||||||
|
|
||||||
|
QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"")
|
||||||
|
.arg(ContainerProps::containerToString(container))
|
||||||
|
.arg(path);
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
@ -197,14 +197,13 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||||
|
|
||||||
qDebug().noquote() << "Copy file from container stdout : \n" << stdOut;
|
qDebug().noquote() << "Copy file from container stdout : \n" << stdOut;
|
||||||
|
|
||||||
|
qDebug().noquote() << "Copy file from container END : \n";
|
||||||
qDebug().noquote() << "Copy file from container END : \n" ;
|
|
||||||
|
|
||||||
return QByteArray::fromHex(stdOut.toUtf8());
|
return QByteArray::fromHex(stdOut.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
|
||||||
libssh::SftpOverwriteMode overwriteMode)
|
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode)
|
||||||
{
|
{
|
||||||
auto error = m_sshClient.connectToHost(credentials);
|
auto error = m_sshClient.connectToHost(credentials);
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
|
@ -218,7 +217,8 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||||
|
|
||||||
qDebug() << "remotePath" << remotePath;
|
qDebug() << "remotePath" << remotePath;
|
||||||
|
|
||||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(),
|
||||||
|
"non_desc");
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -227,41 +227,45 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||||
|
|
||||||
ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials)
|
ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
return runScript(credentials,
|
return runScript(credentials, amnezia::scriptData(SharedScriptType::remove_all_containers));
|
||||||
amnezia::scriptData(SharedScriptType::remove_all_containers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
|
ErrorCode ServerController::removeContainer(const ServerCredentials &credentials, DockerContainer container)
|
||||||
{
|
{
|
||||||
return runScript(credentials,
|
return runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
|
replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
|
||||||
genVarsForScript(credentials, container)));
|
genVarsForScript(credentials, container)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||||
QJsonObject &config, bool isUpdate)
|
QJsonObject &config, bool isUpdate)
|
||||||
{
|
{
|
||||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
||||||
//qDebug().noquote() << QJsonDocument(config).toJson();
|
// qDebug().noquote() << QJsonDocument(config).toJson();
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
|
|
||||||
e = isUserInSudo(credentials, container);
|
e = isUserInSudo(credentials, container);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
if (!isUpdate) {
|
if (!isUpdate) {
|
||||||
e = isServerPortBusy(credentials, container, config);
|
e = isServerPortBusy(credentials, container, config);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = isServerDpkgBusy(credentials, container);
|
e = isServerDpkgBusy(credentials, container);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
e = installDockerWorker(credentials, container);
|
e = installDockerWorker(credentials, container);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished";
|
qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished";
|
||||||
|
|
||||||
e = prepareHostWorker(credentials, container, config);
|
e = prepareHostWorker(credentials, container, config);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
qDebug().noquote() << "ServerController::setupContainer prepareHostWorker finished";
|
qDebug().noquote() << "ServerController::setupContainer prepareHostWorker finished";
|
||||||
|
|
||||||
removeContainer(credentials, container);
|
removeContainer(credentials, container);
|
||||||
|
@ -269,15 +273,18 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||||
|
|
||||||
qDebug().noquote() << "buildContainerWorker start";
|
qDebug().noquote() << "buildContainerWorker start";
|
||||||
e = buildContainerWorker(credentials, container, config);
|
e = buildContainerWorker(credentials, container, config);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
qDebug().noquote() << "ServerController::setupContainer buildContainerWorker finished";
|
qDebug().noquote() << "ServerController::setupContainer buildContainerWorker finished";
|
||||||
|
|
||||||
e = runContainerWorker(credentials, container, config);
|
e = runContainerWorker(credentials, container, config);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
qDebug().noquote() << "ServerController::setupContainer runContainerWorker finished";
|
qDebug().noquote() << "ServerController::setupContainer runContainerWorker finished";
|
||||||
|
|
||||||
e = configureContainerWorker(credentials, container, config);
|
e = configureContainerWorker(credentials, container, config);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
qDebug().noquote() << "ServerController::setupContainer configureContainerWorker finished";
|
qDebug().noquote() << "ServerController::setupContainer configureContainerWorker finished";
|
||||||
|
|
||||||
setupServerFirewall(credentials);
|
setupServerFirewall(credentials);
|
||||||
|
@ -287,46 +294,25 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
ErrorCode ServerController::updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &oldConfig, QJsonObject &newConfig)
|
const QJsonObject &oldConfig, QJsonObject &newConfig)
|
||||||
{
|
{
|
||||||
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
|
||||||
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
|
qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is"
|
||||||
|
<< reinstallRequired;
|
||||||
|
|
||||||
if (reinstallRequired) {
|
if (reinstallRequired) {
|
||||||
return setupContainer(credentials, container, newConfig, true);
|
return setupContainer(credentials, container, newConfig, true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ErrorCode e = configureContainerWorker(credentials, container, newConfig);
|
ErrorCode e = configureContainerWorker(credentials, container, newConfig);
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
return startupContainerWorker(credentials, container, newConfig);
|
return startupContainerWorker(credentials, container, newConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject ServerController::createContainerInitialConfig(DockerContainer container, int port, TransportProto tp)
|
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
|
||||||
{
|
const QJsonObject &newConfig)
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
|
||||||
|
|
||||||
QJsonObject config {
|
|
||||||
{ config_key::container, ContainerProps::containerToString(container) }
|
|
||||||
};
|
|
||||||
|
|
||||||
QJsonObject protoConfig;
|
|
||||||
protoConfig.insert(config_key::port, QString::number(port));
|
|
||||||
protoConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(tp, mainProto));
|
|
||||||
|
|
||||||
|
|
||||||
if (container == DockerContainer::Sftp) {
|
|
||||||
protoConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
|
||||||
protoConfig.insert(config_key::password, Utils::getRandomString(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
config.insert(ProtocolProps::protoToString(mainProto), protoConfig);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerController::isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
|
|
||||||
{
|
{
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
|
|
||||||
|
@ -334,25 +320,25 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
|
||||||
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||||
|
|
||||||
if (container == DockerContainer::OpenVpn) {
|
if (container == DockerContainer::OpenVpn) {
|
||||||
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) !=
|
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto)
|
||||||
newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
|
!= newProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) !=
|
if (oldProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)
|
||||||
newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
|
!= newProtoConfig.value(config_key::port).toString(protocols::openvpn::defaultPort))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::Cloak) {
|
if (container == DockerContainer::Cloak) {
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort) !=
|
if (oldProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort)
|
||||||
newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
|
!= newProtoConfig.value(config_key::port).toString(protocols::cloak::defaultPort))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container == DockerContainer::ShadowSocks) {
|
if (container == DockerContainer::ShadowSocks) {
|
||||||
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) !=
|
if (oldProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)
|
||||||
newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
|
!= newProtoConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -374,75 +360,88 @@ ErrorCode ServerController::installDockerWorker(const ServerCredentials &credent
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorCode error = runScript(credentials,
|
ErrorCode error =
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::install_docker),
|
runScript(credentials,
|
||||||
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
replaceVars(amnezia::scriptData(SharedScriptType::install_docker), genVarsForScript(credentials)),
|
||||||
|
cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (stdOut.contains("command not found")) return ErrorCode::ServerDockerFailedError;
|
if (stdOut.contains("command not found"))
|
||||||
|
return ErrorCode::ServerDockerFailedError;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
ErrorCode ServerController::prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &config)
|
||||||
{
|
{
|
||||||
// create folder on host
|
// create folder on host
|
||||||
return runScript(credentials,
|
return runScript(
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::prepare_host),
|
credentials,
|
||||||
genVarsForScript(credentials, container)));
|
replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), genVarsForScript(credentials, container)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
ErrorCode ServerController::buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &config)
|
||||||
{
|
{
|
||||||
ErrorCode e = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),
|
ErrorCode e = uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, container).toUtf8(),
|
||||||
amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
|
amnezia::server::getDockerfileFolder(container) + "/Dockerfile");
|
||||||
|
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||||
// stdOut += data + "\n";
|
// stdOut += data + "\n";
|
||||||
// };
|
// };
|
||||||
|
|
||||||
e = runScript(credentials,
|
e = runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
|
replaceVars(amnezia::scriptData(SharedScriptType::build_container),
|
||||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
genVarsForScript(credentials, container, config)),
|
||||||
if (e) return e;
|
cbReadStdOut);
|
||||||
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
QJsonObject &config)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||||
// stdOut += data + "\n";
|
// stdOut += data + "\n";
|
||||||
// };
|
// };
|
||||||
|
|
||||||
ErrorCode e = runScript(credentials,
|
ErrorCode e = runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
genVarsForScript(credentials, container, config)),
|
||||||
|
cbReadStdOut);
|
||||||
|
|
||||||
qDebug() << "cbReadStdOut: " << stdOut;
|
qDebug() << "cbReadStdOut: " << stdOut;
|
||||||
|
|
||||||
|
if (stdOut.contains("docker: Error response from daemon"))
|
||||||
|
return ErrorCode::ServerDockerFailedError;
|
||||||
|
|
||||||
if (stdOut.contains("docker: Error response from daemon")) return ErrorCode::ServerDockerFailedError;
|
if (stdOut.contains("address already in use"))
|
||||||
|
return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||||
if (stdOut.contains("address already in use")) return ErrorCode::ServerPortAlreadyAllocatedError;
|
if (stdOut.contains("is already in use by container"))
|
||||||
if (stdOut.contains("is already in use by container")) return ErrorCode::ServerPortAlreadyAllocatedError;
|
return ErrorCode::ServerPortAlreadyAllocatedError;
|
||||||
if (stdOut.contains("invalid publish")) return ErrorCode::ServerDockerFailedError;
|
if (stdOut.contains("invalid publish"))
|
||||||
|
return ErrorCode::ServerDockerFailedError;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
QJsonObject &config)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
@ -454,19 +453,18 @@ ErrorCode ServerController::configureContainerWorker(const ServerCredentials &cr
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ErrorCode e = runContainerScript(credentials, container,
|
ErrorCode e = runContainerScript(credentials, container,
|
||||||
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, container),
|
||||||
genVarsForScript(credentials, container, config)),
|
genVarsForScript(credentials, container, config)),
|
||||||
cbReadStdOut, cbReadStdErr);
|
cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
|
|
||||||
m_configurator->updateContainerConfigAfterInstallation(container, config, stdOut);
|
m_configurator->updateContainerConfigAfterInstallation(container, config, stdOut);
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
ErrorCode ServerController::startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &config)
|
||||||
{
|
{
|
||||||
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
QString script = amnezia::scriptData(ProtocolScriptType::container_startup, container);
|
||||||
|
|
||||||
|
@ -475,16 +473,19 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials,
|
ErrorCode e = uploadTextFileToContainer(container, credentials,
|
||||||
replaceVars(script, genVarsForScript(credentials, container, config)),
|
replaceVars(script, genVarsForScript(credentials, container, config)),
|
||||||
"/opt/amnezia/start.sh");
|
"/opt/amnezia/start.sh");
|
||||||
if (e) return e;
|
if (e)
|
||||||
|
return e;
|
||||||
|
|
||||||
return runScript(credentials,
|
return runScript(credentials,
|
||||||
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && /opt/amnezia/start.sh\"",
|
replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && "
|
||||||
genVarsForScript(credentials, container, config)));
|
"/opt/amnezia/start.sh\"",
|
||||||
|
genVarsForScript(credentials, container, config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials,
|
||||||
|
DockerContainer container, const QJsonObject &config)
|
||||||
{
|
{
|
||||||
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
|
const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
|
||||||
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject();
|
||||||
|
@ -495,85 +496,102 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||||
|
|
||||||
Vars vars;
|
Vars vars;
|
||||||
|
|
||||||
vars.append({{"$REMOTE_HOST", credentials.hostName}});
|
vars.append({ { "$REMOTE_HOST", credentials.hostName } });
|
||||||
|
|
||||||
// OpenVPN vars
|
// OpenVPN vars
|
||||||
vars.append({{"$OPENVPN_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) }});
|
vars.append(
|
||||||
vars.append({{"$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) }});
|
{ { "$OPENVPN_SUBNET_IP",
|
||||||
vars.append({{"$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) }});
|
openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) } });
|
||||||
|
vars.append({ { "$OPENVPN_SUBNET_CIDR",
|
||||||
|
openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) } });
|
||||||
|
vars.append({ { "$OPENVPN_SUBNET_MASK",
|
||||||
|
openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) } });
|
||||||
|
|
||||||
vars.append({{"$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) }});
|
vars.append({ { "$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) } });
|
||||||
vars.append({{"$OPENVPN_TRANSPORT_PROTO", openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) }});
|
vars.append(
|
||||||
|
{ { "$OPENVPN_TRANSPORT_PROTO",
|
||||||
|
openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) } });
|
||||||
|
|
||||||
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||||
vars.append({{"$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" }});
|
vars.append({ { "$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" } });
|
||||||
|
|
||||||
vars.append({{"$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) }});
|
vars.append({ { "$OPENVPN_CIPHER",
|
||||||
vars.append({{"$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) }});
|
openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) } });
|
||||||
|
vars.append({ { "$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) } });
|
||||||
|
|
||||||
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||||
vars.append({{"$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" }});
|
vars.append({ { "$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" } });
|
||||||
if (!isTlsAuth) {
|
if (!isTlsAuth) {
|
||||||
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
|
// erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig
|
||||||
vars.append({{"$OPENVPN_TA_KEY", "" }});
|
vars.append({ { "$OPENVPN_TA_KEY", "" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.append({{"$OPENVPN_ADDITIONAL_CLIENT_CONFIG", openvpnConfig.value(config_key::additional_client_config).
|
vars.append({ { "$OPENVPN_ADDITIONAL_CLIENT_CONFIG",
|
||||||
toString(protocols::openvpn::defaultAdditionalClientConfig) }});
|
openvpnConfig.value(config_key::additional_client_config)
|
||||||
vars.append({{"$OPENVPN_ADDITIONAL_SERVER_CONFIG", openvpnConfig.value(config_key::additional_server_config).
|
.toString(protocols::openvpn::defaultAdditionalClientConfig) } });
|
||||||
toString(protocols::openvpn::defaultAdditionalServerConfig) }});
|
vars.append({ { "$OPENVPN_ADDITIONAL_SERVER_CONFIG",
|
||||||
|
openvpnConfig.value(config_key::additional_server_config)
|
||||||
|
.toString(protocols::openvpn::defaultAdditionalServerConfig) } });
|
||||||
|
|
||||||
// ShadowSocks vars
|
// ShadowSocks vars
|
||||||
vars.append({{"$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) }});
|
vars.append({ { "$SHADOWSOCKS_SERVER_PORT",
|
||||||
vars.append({{"$SHADOWSOCKS_LOCAL_PORT", ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) }});
|
ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) } });
|
||||||
vars.append({{"$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) }});
|
vars.append({ { "$SHADOWSOCKS_LOCAL_PORT",
|
||||||
|
ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) } });
|
||||||
|
vars.append({ { "$SHADOWSOCKS_CIPHER",
|
||||||
|
ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) } });
|
||||||
|
|
||||||
vars.append({{"$CONTAINER_NAME", ContainerProps::containerToString(container)}});
|
vars.append({ { "$CONTAINER_NAME", ContainerProps::containerToString(container) } });
|
||||||
vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container)}});
|
vars.append({ { "$DOCKERFILE_FOLDER", "/opt/amnezia/" + ContainerProps::containerToString(container) } });
|
||||||
|
|
||||||
// Cloak vars
|
// Cloak vars
|
||||||
vars.append({{"$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) }});
|
vars.append({ { "$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) } });
|
||||||
vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) }});
|
vars.append({ { "$FAKE_WEB_SITE_ADDRESS",
|
||||||
|
cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) } });
|
||||||
|
|
||||||
// Wireguard vars
|
// Wireguard vars
|
||||||
vars.append({{"$WIREGUARD_SUBNET_IP", wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) }});
|
vars.append(
|
||||||
vars.append({{"$WIREGUARD_SUBNET_CIDR", wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) }});
|
{ { "$WIREGUARD_SUBNET_IP",
|
||||||
vars.append({{"$WIREGUARD_SUBNET_MASK", wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) }});
|
wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) } });
|
||||||
|
vars.append({ { "$WIREGUARD_SUBNET_CIDR",
|
||||||
|
wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) } });
|
||||||
|
vars.append({ { "$WIREGUARD_SUBNET_MASK",
|
||||||
|
wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) } });
|
||||||
|
|
||||||
vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }});
|
vars.append({ { "$WIREGUARD_SERVER_PORT",
|
||||||
|
wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) } });
|
||||||
|
|
||||||
// IPsec vars
|
// IPsec vars
|
||||||
vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}});
|
vars.append({ { "$IPSEC_VPN_L2TP_NET", "192.168.42.0/24" } });
|
||||||
vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}});
|
vars.append({ { "$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250" } });
|
||||||
vars.append({{"$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1"}});
|
vars.append({ { "$IPSEC_VPN_L2TP_LOCAL", "192.168.42.1" } });
|
||||||
|
|
||||||
vars.append({{"$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24"}});
|
vars.append({ { "$IPSEC_VPN_XAUTH_NET", "192.168.43.0/24" } });
|
||||||
vars.append({{"$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250"}});
|
vars.append({ { "$IPSEC_VPN_XAUTH_POOL", "192.168.43.10-192.168.43.250" } });
|
||||||
|
|
||||||
vars.append({{"$IPSEC_VPN_SHA2_TRUNCBUG", "yes"}});
|
vars.append({ { "$IPSEC_VPN_SHA2_TRUNCBUG", "yes" } });
|
||||||
|
|
||||||
vars.append({{"$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes"}});
|
vars.append({ { "$IPSEC_VPN_VPN_ANDROID_MTU_FIX", "yes" } });
|
||||||
vars.append({{"$IPSEC_VPN_DISABLE_IKEV2", "no"}});
|
vars.append({ { "$IPSEC_VPN_DISABLE_IKEV2", "no" } });
|
||||||
vars.append({{"$IPSEC_VPN_DISABLE_L2TP", "no"}});
|
vars.append({ { "$IPSEC_VPN_DISABLE_L2TP", "no" } });
|
||||||
vars.append({{"$IPSEC_VPN_DISABLE_XAUTH", "no"}});
|
vars.append({ { "$IPSEC_VPN_DISABLE_XAUTH", "no" } });
|
||||||
|
|
||||||
vars.append({{"$IPSEC_VPN_C2C_TRAFFIC", "no"}});
|
vars.append({ { "$IPSEC_VPN_C2C_TRAFFIC", "no" } });
|
||||||
|
|
||||||
vars.append({{"$PRIMARY_SERVER_DNS", m_settings->primaryDns()}});
|
|
||||||
vars.append({{"$SECONDARY_SERVER_DNS", m_settings->secondaryDns()}});
|
|
||||||
|
|
||||||
|
vars.append({ { "$PRIMARY_SERVER_DNS", m_settings->primaryDns() } });
|
||||||
|
vars.append({ { "$SECONDARY_SERVER_DNS", m_settings->secondaryDns() } });
|
||||||
|
|
||||||
// Sftp vars
|
// Sftp vars
|
||||||
vars.append({{"$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) }});
|
vars.append(
|
||||||
vars.append({{"$SFTP_USER", sftpConfig.value(config_key::userName).toString() }});
|
{ { "$SFTP_PORT",
|
||||||
vars.append({{"$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() }});
|
sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) } });
|
||||||
|
vars.append({ { "$SFTP_USER", sftpConfig.value(config_key::userName).toString() } });
|
||||||
|
vars.append({ { "$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() } });
|
||||||
|
|
||||||
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
QString serverIp = Utils::getIPAddress(credentials.hostName);
|
||||||
if (!serverIp.isEmpty()) {
|
if (!serverIp.isEmpty()) {
|
||||||
vars.append({{"$SERVER_IP_ADDRESS", serverIp}});
|
vars.append({ { "$SERVER_IP_ADDRESS", serverIp } });
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
|
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,10 +610,11 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorCode e = runScript(credentials,
|
ErrorCode e =
|
||||||
amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
runScript(credentials, amnezia::scriptData(SharedScriptType::check_connection), cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (errorCode) *errorCode = e;
|
if (errorCode)
|
||||||
|
*errorCode = e;
|
||||||
|
|
||||||
return stdOut;
|
return stdOut;
|
||||||
}
|
}
|
||||||
|
@ -607,23 +626,24 @@ void ServerController::setCancelInstallation(const bool cancel)
|
||||||
|
|
||||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
return runScript(credentials,
|
return runScript(
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall),
|
credentials,
|
||||||
genVarsForScript(credentials)));
|
replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), genVarsForScript(credentials)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
||||||
{
|
{
|
||||||
QString s = script;
|
QString s = script;
|
||||||
for (const QPair<QString, QString> &var : vars) {
|
for (const QPair<QString, QString> &var : vars) {
|
||||||
//qDebug() << "Replacing" << var.first << var.second;
|
// qDebug() << "Replacing" << var.first << var.second;
|
||||||
s.replace(var.first, var.second);
|
s.replace(var.first, var.second);
|
||||||
}
|
}
|
||||||
//qDebug().noquote() << script;
|
// qDebug().noquote() << script;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
|
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &config)
|
||||||
{
|
{
|
||||||
if (container == DockerContainer::Dns) {
|
if (container == DockerContainer::Dns) {
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
|
@ -646,8 +666,10 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||||
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
|
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
|
||||||
|
|
||||||
QString defaultPort("%1");
|
QString defaultPort("%1");
|
||||||
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
|
QString port =
|
||||||
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
|
containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
|
||||||
|
QString defaultTransportProto =
|
||||||
|
ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
|
||||||
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
|
||||||
|
|
||||||
QString script = QString("sudo lsof -i -P -n | grep -E ':%1 ").arg(port);
|
QString script = QString("sudo lsof -i -P -n | grep -E ':%1 ").arg(port);
|
||||||
|
@ -660,8 +682,8 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
|
||||||
script = script.append(" | grep LISTEN");
|
script = script.append(" | grep LISTEN");
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode errorCode = runScript(credentials,
|
ErrorCode errorCode = runScript(credentials, replaceVars(script, genVarsForScript(credentials, container)),
|
||||||
replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
|
cbReadStdOut, cbReadStdErr);
|
||||||
if (errorCode != ErrorCode::NoError) {
|
if (errorCode != ErrorCode::NoError) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
@ -689,9 +711,11 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
|
||||||
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
ErrorCode error =
|
||||||
|
runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
||||||
|
|
||||||
if (!stdOut.contains("sudo")) return ErrorCode::ServerUserNotInSudo;
|
if (!stdOut.contains("sudo"))
|
||||||
|
return ErrorCode::ServerUserNotInSudo;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -718,7 +742,8 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||||
stdOut.clear();
|
stdOut.clear();
|
||||||
runScript(credentials,
|
runScript(credentials,
|
||||||
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
|
replaceVars(amnezia::scriptData(SharedScriptType::check_server_is_busy),
|
||||||
genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
|
genVarsForScript(credentials)),
|
||||||
|
cbReadStdOut, cbReadStdErr);
|
||||||
if (!stdOut.isEmpty() || stdOut.contains("Unable to acquire the dpkg frontend lock")) {
|
if (!stdOut.isEmpty() || stdOut.contains("Unable to acquire the dpkg frontend lock")) {
|
||||||
emit serverIsBusy(true);
|
emit serverIsBusy(true);
|
||||||
QThread::msleep(1000);
|
QThread::msleep(1000);
|
||||||
|
@ -738,7 +763,8 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential
|
||||||
return future.result();
|
return future.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers)
|
ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||||
|
QMap<DockerContainer, QJsonObject> &installedContainers)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
|
@ -770,13 +796,10 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
||||||
QString transportProto = containerAndPortMatch.captured(3);
|
QString transportProto = containerAndPortMatch.captured(3);
|
||||||
DockerContainer container = ContainerProps::containerFromString(name);
|
DockerContainer container = ContainerProps::containerFromString(name);
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
QJsonObject config {
|
QJsonObject config { { config_key::container, name },
|
||||||
{ config_key::container, name },
|
{ ProtocolProps::protoToString(mainProto),
|
||||||
{ ProtocolProps::protoToString(mainProto), QJsonObject {
|
QJsonObject { { config_key::port, port },
|
||||||
{ config_key::port, port },
|
{ config_key::transport_proto, transportProto } } } };
|
||||||
{ config_key::transport_proto, transportProto }}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
installedContainers.insert(container, config);
|
installedContainers.insert(container, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -784,7 +807,8 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback)
|
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||||
|
const std::function<QString()> &callback)
|
||||||
{
|
{
|
||||||
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
|
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "defs.h"
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
#include "defs.h"
|
||||||
#include "sshclient.h"
|
#include "sshclient.h"
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
|
@ -24,52 +24,61 @@ public:
|
||||||
|
|
||||||
ErrorCode removeAllContainers(const ServerCredentials &credentials);
|
ErrorCode removeAllContainers(const ServerCredentials &credentials);
|
||||||
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
|
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
|
||||||
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container,
|
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config,
|
||||||
QJsonObject &config, bool isUpdate = false);
|
bool isUpdate = false);
|
||||||
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container,
|
||||||
const QJsonObject &oldConfig, QJsonObject &newConfig);
|
const QJsonObject &oldConfig, QJsonObject &newConfig);
|
||||||
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers);
|
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials,
|
||||||
|
QMap<DockerContainer, QJsonObject> &installedContainers);
|
||||||
|
|
||||||
// create initial config - generate passwords, etc
|
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp);
|
const QJsonObject &config = QJsonObject());
|
||||||
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
|
||||||
|
|
||||||
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
ErrorCode uploadTextFileToContainer(
|
||||||
const QString &file, const QString &path,
|
DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path,
|
||||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||||
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||||
const QString &path, ErrorCode *errorCode = nullptr);
|
const QString &path, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
QString replaceVars(const QString &script, const Vars &vars);
|
QString replaceVars(const QString &script, const Vars &vars);
|
||||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None,
|
||||||
|
const QJsonObject &config = QJsonObject());
|
||||||
|
|
||||||
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||||
|
|
||||||
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
ErrorCode
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||||
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||||
|
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||||
|
|
||||||
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
||||||
|
|
||||||
void setCancelInstallation(const bool cancel);
|
void setCancelInstallation(const bool cancel);
|
||||||
|
|
||||||
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback);
|
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey,
|
||||||
|
const std::function<QString()> &callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
|
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
|
||||||
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
|
const QJsonObject &config = QJsonObject());
|
||||||
|
ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
const QJsonObject &config = QJsonObject());
|
||||||
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
||||||
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config);
|
ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container,
|
||||||
|
QJsonObject &config);
|
||||||
|
|
||||||
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
|
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container,
|
||||||
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
|
const QJsonObject &config);
|
||||||
|
bool isReinstallContainerRequired(DockerContainer container, const QJsonObject &oldConfig,
|
||||||
|
const QJsonObject &newConfig);
|
||||||
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
|
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
|
||||||
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
|
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
|
||||||
|
|
||||||
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
|
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
||||||
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||||
|
|
||||||
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
|
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
|
||||||
|
|
||||||
|
|
|
@ -274,5 +274,6 @@
|
||||||
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
|
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
|
||||||
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
|
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
|
||||||
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
|
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
#include "secure_qsettings.h"
|
#include "secure_qsettings.h"
|
||||||
#include "platforms/ios/MobileUtils.h"
|
#include "platforms/ios/MobileUtils.h"
|
||||||
|
|
||||||
|
#include "QAead.h"
|
||||||
|
#include "QBlockCipher.h"
|
||||||
|
#include "utilities.h"
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QRandomGenerator>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include "utilities.h"
|
|
||||||
#include <QRandomGenerator>
|
|
||||||
#include "QAead.h"
|
|
||||||
#include "QBlockCipher.h"
|
|
||||||
|
|
||||||
using namespace QKeychain;
|
using namespace QKeychain;
|
||||||
|
|
||||||
SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent)
|
SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent)
|
||||||
: QObject{parent},
|
: QObject { parent }, m_settings(organization, application, parent), encryptedKeys({ "Servers/serversList" })
|
||||||
m_settings(organization, application, parent),
|
|
||||||
encryptedKeys({"Servers/serversList"})
|
|
||||||
{
|
{
|
||||||
bool encrypted = m_settings.value("Conf/encrypted").toBool();
|
bool encrypted = m_settings.value("Conf/encrypted").toBool();
|
||||||
|
|
||||||
// convert settings to encrypted for if updated to >= 2.1.0
|
// convert settings to encrypted for if updated to >= 2.1.0
|
||||||
if (encryptionRequired() && ! encrypted) {
|
if (encryptionRequired() && !encrypted) {
|
||||||
for (const QString &key : m_settings.allKeys()) {
|
for (const QString &key : m_settings.allKeys()) {
|
||||||
if (encryptedKeys.contains(key)) {
|
if (encryptedKeys.contains(key)) {
|
||||||
const QVariant &val = value(key);
|
const QVariant &val = value(key);
|
||||||
|
@ -44,15 +42,15 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
|
||||||
return m_cache.value(key);
|
return m_cache.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_settings.contains(key)) return defaultValue;
|
if (!m_settings.contains(key))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
QVariant retVal;
|
QVariant retVal;
|
||||||
|
|
||||||
// check if value is not encrypted, v. < 2.0.x
|
// check if value is not encrypted, v. < 2.0.x
|
||||||
retVal = m_settings.value(key);
|
retVal = m_settings.value(key);
|
||||||
if (retVal.isValid()) {
|
if (retVal.isValid()) {
|
||||||
if (retVal.userType() == QVariant::ByteArray &&
|
if (retVal.userType() == QVariant::ByteArray && retVal.toByteArray().mid(0, magicString.size()) == magicString) {
|
||||||
retVal.toByteArray().mid(0, magicString.size()) == magicString) {
|
|
||||||
|
|
||||||
if (getEncKey().isEmpty() || getEncIv().isEmpty()) {
|
if (getEncKey().isEmpty() || getEncIv().isEmpty()) {
|
||||||
qCritical() << "SecureQSettings::setValue Decryption requested, but key is empty";
|
qCritical() << "SecureQSettings::setValue Decryption requested, but key is empty";
|
||||||
|
@ -71,8 +69,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
|
||||||
retVal = QVariant();
|
retVal = QVariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
qWarning() << "SecureQSettings::value invalid QVariant value";
|
qWarning() << "SecureQSettings::value invalid QVariant value";
|
||||||
retVal = QVariant();
|
retVal = QVariant();
|
||||||
}
|
}
|
||||||
|
@ -95,14 +92,12 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value)
|
||||||
|
|
||||||
QByteArray encryptedValue = encryptText(decryptedValue);
|
QByteArray encryptedValue = encryptText(decryptedValue);
|
||||||
m_settings.setValue(key, magicString + encryptedValue);
|
m_settings.setValue(key, magicString + encryptedValue);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
qCritical() << "SecureQSettings::setValue Encryption required, but key is empty";
|
qCritical() << "SecureQSettings::setValue Encryption required, but key is empty";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
m_settings.setValue(key, value);
|
m_settings.setValue(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +134,8 @@ QByteArray SecureQSettings::backupAppConfig() const
|
||||||
bool SecureQSettings::restoreAppConfig(const QByteArray &json)
|
bool SecureQSettings::restoreAppConfig(const QByteArray &json)
|
||||||
{
|
{
|
||||||
QJsonObject cfg = QJsonDocument::fromJson(json).object();
|
QJsonObject cfg = QJsonDocument::fromJson(json).object();
|
||||||
if (cfg.isEmpty()) return false;
|
if (cfg.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
for (const QString &key : cfg.keys()) {
|
for (const QString &key : cfg.keys()) {
|
||||||
setValue(key, cfg.value(key).toVariant());
|
setValue(key, cfg.value(key).toVariant());
|
||||||
|
@ -149,14 +145,13 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray SecureQSettings::encryptText(const QByteArray &value) const
|
||||||
QByteArray SecureQSettings::encryptText(const QByteArray& value) const
|
|
||||||
{
|
{
|
||||||
QSimpleCrypto::QBlockCipher cipher;
|
QSimpleCrypto::QBlockCipher cipher;
|
||||||
return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
|
return cipher.encryptAesBlockCipher(value, getEncKey(), getEncIv());
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SecureQSettings::decryptText(const QByteArray& ba) const
|
QByteArray SecureQSettings::decryptText(const QByteArray &ba) const
|
||||||
{
|
{
|
||||||
QSimpleCrypto::QBlockCipher cipher;
|
QSimpleCrypto::QBlockCipher cipher;
|
||||||
return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
|
return cipher.decryptAesBlockCipher(ba, getEncKey(), getEncIv());
|
||||||
|
@ -228,13 +223,11 @@ QByteArray SecureQSettings::getSecTag(const QString &tag)
|
||||||
job->setAutoDelete(false);
|
job->setAutoDelete(false);
|
||||||
job->setKey(tag);
|
job->setKey(tag);
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop](){
|
job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop]() { loop.quit(); });
|
||||||
loop.quit();
|
|
||||||
});
|
|
||||||
job->start();
|
job->start();
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if ( job->error() ) {
|
if (job->error()) {
|
||||||
qCritical() << "SecureQSettings::getSecTag Error:" << job->errorString();
|
qCritical() << "SecureQSettings::getSecTag Error:" << job->errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,9 +242,7 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
|
||||||
job->setBinaryData(data);
|
job->setBinaryData(data);
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
QTimer::singleShot(1000, &loop, SLOT(quit()));
|
QTimer::singleShot(1000, &loop, SLOT(quit()));
|
||||||
job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop](){
|
job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop]() { loop.quit(); });
|
||||||
loop.quit();
|
|
||||||
});
|
|
||||||
job->start();
|
job->start();
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
|
@ -260,4 +251,10 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SecureQSettings::clearSettings()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
m_settings.clear();
|
||||||
|
m_cache.clear();
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
#ifndef SECUREQSETTINGS_H
|
#ifndef SECUREQSETTINGS_H
|
||||||
#define SECUREQSETTINGS_H
|
#define SECUREQSETTINGS_H
|
||||||
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
#include "keychain.h"
|
#include "keychain.h"
|
||||||
|
|
||||||
|
constexpr const char *settingsKeyTag = "settingsKeyTag";
|
||||||
constexpr const char* settingsKeyTag = "settingsKeyTag";
|
constexpr const char *settingsIvTag = "settingsIvTag";
|
||||||
constexpr const char* settingsIvTag = "settingsIvTag";
|
constexpr const char *keyChainName = "AmneziaVPN-Keychain";
|
||||||
constexpr const char* keyChainName = "AmneziaVPN-Keychain";
|
|
||||||
|
|
||||||
|
|
||||||
class SecureQSettings : public QObject
|
class SecureQSettings : public QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit SecureQSettings(const QString &organization, const QString &application = QString(), QObject *parent = nullptr);
|
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||||
void setValue(const QString &key, const QVariant &value);
|
void setValue(const QString &key, const QVariant &value);
|
||||||
|
@ -28,7 +27,7 @@ public:
|
||||||
bool restoreAppConfig(const QByteArray &json);
|
bool restoreAppConfig(const QByteArray &json);
|
||||||
|
|
||||||
QByteArray encryptText(const QByteArray &value) const;
|
QByteArray encryptText(const QByteArray &value) const;
|
||||||
QByteArray decryptText(const QByteArray& ba) const;
|
QByteArray decryptText(const QByteArray &ba) const;
|
||||||
|
|
||||||
bool encryptionRequired() const;
|
bool encryptionRequired() const;
|
||||||
|
|
||||||
|
@ -38,6 +37,8 @@ public:
|
||||||
static QByteArray getSecTag(const QString &tag);
|
static QByteArray getSecTag(const QString &tag);
|
||||||
static void setSecTag(const QString &tag, const QByteArray &data);
|
static void setSecTag(const QString &tag, const QByteArray &data);
|
||||||
|
|
||||||
|
void clearSettings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSettings m_settings;
|
QSettings m_settings;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "version.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
@ -8,10 +8,7 @@
|
||||||
const char Settings::cloudFlareNs1[] = "1.1.1.1";
|
const char Settings::cloudFlareNs1[] = "1.1.1.1";
|
||||||
const char Settings::cloudFlareNs2[] = "1.0.0.1";
|
const char Settings::cloudFlareNs2[] = "1.0.0.1";
|
||||||
|
|
||||||
|
Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
|
||||||
Settings::Settings(QObject* parent) :
|
|
||||||
QObject(parent),
|
|
||||||
m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
|
|
||||||
{
|
{
|
||||||
// Import old settings
|
// Import old settings
|
||||||
if (serversCount() == 0) {
|
if (serversCount() == 0) {
|
||||||
|
@ -20,7 +17,7 @@ Settings::Settings(QObject* parent) :
|
||||||
QString serverName = m_settings.value("Server/serverName").toString();
|
QString serverName = m_settings.value("Server/serverName").toString();
|
||||||
int port = m_settings.value("Server/serverPort").toInt();
|
int port = m_settings.value("Server/serverPort").toInt();
|
||||||
|
|
||||||
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()){
|
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
|
||||||
QJsonObject server;
|
QJsonObject server;
|
||||||
server.insert(config_key::userName, user);
|
server.insert(config_key::userName, user);
|
||||||
server.insert(config_key::password, password);
|
server.insert(config_key::password, password);
|
||||||
|
@ -46,7 +43,8 @@ int Settings::serversCount() const
|
||||||
QJsonObject Settings::server(int index) const
|
QJsonObject Settings::server(int index) const
|
||||||
{
|
{
|
||||||
const QJsonArray &servers = serversArray();
|
const QJsonArray &servers = serversArray();
|
||||||
if (index >= servers.size()) return QJsonObject();
|
if (index >= servers.size())
|
||||||
|
return QJsonObject();
|
||||||
|
|
||||||
return servers.at(index).toObject();
|
return servers.at(index).toObject();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +59,8 @@ void Settings::addServer(const QJsonObject &server)
|
||||||
void Settings::removeServer(int index)
|
void Settings::removeServer(int index)
|
||||||
{
|
{
|
||||||
QJsonArray servers = serversArray();
|
QJsonArray servers = serversArray();
|
||||||
if (index >= servers.size()) return;
|
if (index >= servers.size())
|
||||||
|
return;
|
||||||
|
|
||||||
servers.removeAt(index);
|
servers.removeAt(index);
|
||||||
setServersArray(servers);
|
setServersArray(servers);
|
||||||
|
@ -70,7 +69,8 @@ void Settings::removeServer(int index)
|
||||||
bool Settings::editServer(int index, const QJsonObject &server)
|
bool Settings::editServer(int index, const QJsonObject &server)
|
||||||
{
|
{
|
||||||
QJsonArray servers = serversArray();
|
QJsonArray servers = serversArray();
|
||||||
if (index >= servers.size()) return false;
|
if (index >= servers.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
servers.replace(index, server);
|
servers.replace(index, server);
|
||||||
setServersArray(servers);
|
setServersArray(servers);
|
||||||
|
@ -94,8 +94,8 @@ QString Settings::defaultContainerName(int serverIndex) const
|
||||||
QString name = server(serverIndex).value(config_key::defaultContainer).toString();
|
QString name = server(serverIndex).value(config_key::defaultContainer).toString();
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
return ContainerProps::containerToString(DockerContainer::None);
|
return ContainerProps::containerToString(DockerContainer::None);
|
||||||
}
|
} else
|
||||||
else return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<DockerContainer, QJsonObject> Settings::containers(int serverIndex) const
|
QMap<DockerContainer, QJsonObject> Settings::containers(int serverIndex) const
|
||||||
|
@ -104,7 +104,8 @@ QMap<DockerContainer, QJsonObject> Settings::containers(int serverIndex) const
|
||||||
|
|
||||||
QMap<DockerContainer, QJsonObject> containersMap;
|
QMap<DockerContainer, QJsonObject> containersMap;
|
||||||
for (const QJsonValue &val : containers) {
|
for (const QJsonValue &val : containers) {
|
||||||
containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), val.toObject());
|
containersMap.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()),
|
||||||
|
val.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
return containersMap;
|
return containersMap;
|
||||||
|
@ -114,17 +115,17 @@ void Settings::setContainers(int serverIndex, const QMap<DockerContainer, QJsonO
|
||||||
{
|
{
|
||||||
QJsonObject s = server(serverIndex);
|
QJsonObject s = server(serverIndex);
|
||||||
QJsonArray c;
|
QJsonArray c;
|
||||||
for (const QJsonObject &o: containers) {
|
for (const QJsonObject &o : containers) {
|
||||||
c.append(o);
|
c.append(o);
|
||||||
}
|
}
|
||||||
s.insert(config_key::containers, c);
|
s.insert(config_key::containers, c);
|
||||||
editServer(serverIndex, s);
|
editServer(serverIndex, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QJsonObject Settings::containerConfig(int serverIndex, DockerContainer container)
|
QJsonObject Settings::containerConfig(int serverIndex, DockerContainer container)
|
||||||
{
|
{
|
||||||
if (container == DockerContainer::None) return QJsonObject();
|
if (container == DockerContainer::None)
|
||||||
|
return QJsonObject();
|
||||||
return containers(serverIndex).value(container);
|
return containers(serverIndex).value(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ void Settings::clearLastConnectionConfig(int serverIndex, DockerContainer contai
|
||||||
{
|
{
|
||||||
// recursively remove
|
// recursively remove
|
||||||
if (proto == Proto::Any) {
|
if (proto == Proto::Any) {
|
||||||
for (Proto p: ContainerProps::protocolsForContainer(container)) {
|
for (Proto p : ContainerProps::protocolsForContainer(container)) {
|
||||||
clearLastConnectionConfig(serverIndex, container, p);
|
clearLastConnectionConfig(serverIndex, container, p);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -183,7 +184,8 @@ void Settings::clearLastConnectionConfig(int serverIndex, DockerContainer contai
|
||||||
|
|
||||||
bool Settings::haveAuthData(int serverIndex) const
|
bool Settings::haveAuthData(int serverIndex) const
|
||||||
{
|
{
|
||||||
if (serverIndex < 0) return false;
|
if (serverIndex < 0)
|
||||||
|
return false;
|
||||||
ServerCredentials cred = serverCredentials(serverIndex);
|
ServerCredentials cred = serverCredentials(serverIndex);
|
||||||
return (!cred.hostName.isEmpty() && !cred.userName.isEmpty() && !cred.secretData.isEmpty());
|
return (!cred.hostName.isEmpty() && !cred.userName.isEmpty() && !cred.secretData.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -196,7 +198,7 @@ QString Settings::nextAvailableServerName() const
|
||||||
do {
|
do {
|
||||||
i++;
|
i++;
|
||||||
nameExist = false;
|
nameExist = false;
|
||||||
for (const QJsonValue &server: serversArray()) {
|
for (const QJsonValue &server : serversArray()) {
|
||||||
if (server.toObject().value(config_key::description).toString() == tr("Server") + " " + QString::number(i)) {
|
if (server.toObject().value(config_key::description).toString() == tr("Server") + " " + QString::number(i)) {
|
||||||
nameExist = true;
|
nameExist = true;
|
||||||
break;
|
break;
|
||||||
|
@ -223,19 +225,17 @@ void Settings::setSaveLogs(bool enabled)
|
||||||
QString Settings::routeModeString(RouteMode mode) const
|
QString Settings::routeModeString(RouteMode mode) const
|
||||||
{
|
{
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case VpnAllSites:
|
case VpnAllSites: return "AllSites";
|
||||||
return "AllSites";
|
case VpnOnlyForwardSites: return "ForwardSites";
|
||||||
case VpnOnlyForwardSites:
|
case VpnAllExceptSites: return "ExceptSites";
|
||||||
return "ForwardSites";
|
|
||||||
case VpnAllExceptSites:
|
|
||||||
return "ExceptSites";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
|
void Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
|
||||||
{
|
{
|
||||||
QVariantMap sites = vpnSites(mode);
|
QVariantMap sites = vpnSites(mode);
|
||||||
if (sites.contains(site) && ip.isEmpty()) return;
|
if (sites.contains(site) && ip.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
sites.insert(site, ip);
|
sites.insert(site, ip);
|
||||||
setVpnSites(mode, sites);
|
setVpnSites(mode, sites);
|
||||||
|
@ -248,7 +248,8 @@ void Settings::addVpnSites(RouteMode mode, const QMap<QString, QString> &sites)
|
||||||
const QString &site = i.key();
|
const QString &site = i.key();
|
||||||
const QString &ip = i.value();
|
const QString &ip = i.value();
|
||||||
|
|
||||||
if (allSites.contains(site) && allSites.value(site) == ip) continue;
|
if (allSites.contains(site) && allSites.value(site) == ip)
|
||||||
|
continue;
|
||||||
|
|
||||||
allSites.insert(site, ip);
|
allSites.insert(site, ip);
|
||||||
}
|
}
|
||||||
|
@ -263,8 +264,7 @@ QStringList Settings::getVpnIps(RouteMode mode) const
|
||||||
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
|
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
|
||||||
if (Utils::checkIpSubnetFormat(i.key())) {
|
if (Utils::checkIpSubnetFormat(i.key())) {
|
||||||
ips.append(i.key());
|
ips.append(i.key());
|
||||||
}
|
} else if (Utils::checkIpSubnetFormat(i.value().toString())) {
|
||||||
else if (Utils::checkIpSubnetFormat(i.value().toString())) {
|
|
||||||
ips.append(i.value().toString());
|
ips.append(i.value().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,8 @@ QStringList Settings::getVpnIps(RouteMode mode) const
|
||||||
void Settings::removeVpnSite(RouteMode mode, const QString &site)
|
void Settings::removeVpnSite(RouteMode mode, const QString &site)
|
||||||
{
|
{
|
||||||
QVariantMap sites = vpnSites(mode);
|
QVariantMap sites = vpnSites(mode);
|
||||||
if (!sites.contains(site)) return;
|
if (!sites.contains(site))
|
||||||
|
return;
|
||||||
|
|
||||||
sites.remove(site);
|
sites.remove(site);
|
||||||
setVpnSites(mode, sites);
|
setVpnSites(mode, sites);
|
||||||
|
@ -285,7 +286,8 @@ void Settings::addVpnIps(RouteMode mode, const QStringList &ips)
|
||||||
{
|
{
|
||||||
QVariantMap sites = vpnSites(mode);
|
QVariantMap sites = vpnSites(mode);
|
||||||
for (const QString &ip : ips) {
|
for (const QString &ip : ips) {
|
||||||
if (ip.isEmpty()) continue;
|
if (ip.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
sites.insert(ip, "");
|
sites.insert(ip, "");
|
||||||
}
|
}
|
||||||
|
@ -297,7 +299,8 @@ void Settings::removeVpnSites(RouteMode mode, const QStringList &sites)
|
||||||
{
|
{
|
||||||
QVariantMap sitesMap = vpnSites(mode);
|
QVariantMap sitesMap = vpnSites(mode);
|
||||||
for (const QString &site : sites) {
|
for (const QString &site : sites) {
|
||||||
if (site.isEmpty()) continue;
|
if (site.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
sitesMap.remove(site);
|
sitesMap.remove(site);
|
||||||
}
|
}
|
||||||
|
@ -305,9 +308,20 @@ void Settings::removeVpnSites(RouteMode mode, const QStringList &sites)
|
||||||
setVpnSites(mode, sitesMap);
|
setVpnSites(mode, sitesMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Settings::primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString(); }
|
QString Settings::primaryDns() const
|
||||||
|
{
|
||||||
|
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
|
||||||
|
}
|
||||||
|
|
||||||
QString Settings::secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString(); }
|
QString Settings::secondaryDns() const
|
||||||
|
{
|
||||||
|
return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::clearSettings()
|
||||||
|
{
|
||||||
|
m_settings.clearSettings();
|
||||||
|
}
|
||||||
|
|
||||||
ServerCredentials Settings::defaultServerCredentials() const
|
ServerCredentials Settings::defaultServerCredentials() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -183,6 +183,8 @@ public:
|
||||||
m_settings.setValue("Conf/appLanguage", locale);
|
m_settings.setValue("Conf/appLanguage", locale);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void clearSettings();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void saveLogsChanged();
|
void saveLogsChanged();
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,19 @@ InstallController::InstallController(const QSharedPointer<ServersModel> &servers
|
||||||
void InstallController::install(DockerContainer container, int port, TransportProto transportProto)
|
void InstallController::install(DockerContainer container, int port, TransportProto transportProto)
|
||||||
{
|
{
|
||||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||||
|
QJsonObject containerConfig;
|
||||||
|
|
||||||
QJsonObject containerConfig { { config_key::port, QString::number(port) },
|
containerConfig.insert(config_key::port, QString::number(port));
|
||||||
{ config_key::transport_proto,
|
containerConfig.insert(config_key::transport_proto, ProtocolProps::transportProtoToString(transportProto, mainProto));
|
||||||
ProtocolProps::transportProtoToString(transportProto, mainProto) } };
|
|
||||||
QJsonObject config { { config_key::container, ContainerProps::containerToString(container) },
|
if (container == DockerContainer::Sftp) {
|
||||||
{ ProtocolProps::protoToString(mainProto), containerConfig } };
|
containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName);
|
||||||
|
containerConfig.insert(config_key::password, Utils::getRandomString(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject config;
|
||||||
|
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||||
|
config.insert(ProtocolProps::protoToString(mainProto), containerConfig);
|
||||||
|
|
||||||
if (m_shouldCreateServer) {
|
if (m_shouldCreateServer) {
|
||||||
if (isServerAlreadyExists()) {
|
if (isServerAlreadyExists()) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace PageLoader
|
||||||
PageSettingsApplication,
|
PageSettingsApplication,
|
||||||
PageSettingsBackup,
|
PageSettingsBackup,
|
||||||
PageSettingsAbout,
|
PageSettingsAbout,
|
||||||
|
PageSettingsLogging,
|
||||||
|
|
||||||
PageSetupWizardStart,
|
PageSetupWizardStart,
|
||||||
PageSetupWizardCredentials,
|
PageSetupWizardCredentials,
|
||||||
|
|
|
@ -46,14 +46,15 @@ void SettingsController::setSecondaryDns(const QString &dns)
|
||||||
emit secondaryDnsChanged();
|
emit secondaryDnsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SettingsController::isSaveLogsEnabled()
|
bool SettingsController::isLoggingEnable()
|
||||||
{
|
{
|
||||||
return m_settings->isSaveLogs();
|
return m_settings->isSaveLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsController::setSaveLogs(bool enable)
|
void SettingsController::toggleLogging(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setSaveLogs(enable);
|
m_settings->setSaveLogs(enable);
|
||||||
|
emit loggingStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsController::openLogsFolder()
|
void SettingsController::openLogsFolder()
|
||||||
|
@ -101,3 +102,8 @@ QString SettingsController::getAppVersion()
|
||||||
{
|
{
|
||||||
return m_appVersion;
|
return m_appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsController::clearSettings()
|
||||||
|
{
|
||||||
|
m_settings->clearSettings();
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ public:
|
||||||
|
|
||||||
Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged)
|
Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged)
|
||||||
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
||||||
|
Q_PROPERTY(bool isLoggingEnable READ isLoggingEnable WRITE toggleLogging NOTIFY loggingStateChanged)
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setAmneziaDns(bool enable);
|
void setAmneziaDns(bool enable);
|
||||||
|
@ -27,8 +28,8 @@ public slots:
|
||||||
QString getSecondaryDns();
|
QString getSecondaryDns();
|
||||||
void setSecondaryDns(const QString &dns);
|
void setSecondaryDns(const QString &dns);
|
||||||
|
|
||||||
bool isSaveLogsEnabled();
|
bool isLoggingEnable();
|
||||||
void setSaveLogs(bool enable);
|
void toggleLogging(bool enable);
|
||||||
|
|
||||||
void openLogsFolder();
|
void openLogsFolder();
|
||||||
void exportLogsFile();
|
void exportLogsFile();
|
||||||
|
@ -39,9 +40,12 @@ public slots:
|
||||||
|
|
||||||
QString getAppVersion();
|
QString getAppVersion();
|
||||||
|
|
||||||
|
void clearSettings();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void primaryDnsChanged();
|
void primaryDnsChanged();
|
||||||
void secondaryDnsChanged();
|
void secondaryDnsChanged();
|
||||||
|
void loggingStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
|
|
@ -54,3 +54,8 @@ int LanguageModel::getCurrentLanguageIndex()
|
||||||
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString LanguageModel::getCurrentLanuageName()
|
||||||
|
{
|
||||||
|
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void changeLanguage(const LanguageSettings::AvailableLanguageEnum language);
|
void changeLanguage(const LanguageSettings::AvailableLanguageEnum language);
|
||||||
int getCurrentLanguageIndex();
|
int getCurrentLanguageIndex();
|
||||||
|
QString getCurrentLanuageName();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateTranslations(const QLocale &locale);
|
void updateTranslations(const QLocale &locale);
|
||||||
|
|
|
@ -89,7 +89,7 @@ void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container)
|
||||||
void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp)
|
void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp)
|
||||||
{
|
{
|
||||||
ServerController serverController(m_settings);
|
ServerController serverController(m_settings);
|
||||||
QJsonObject config = serverController.createContainerInitialConfig(c, port, tp);
|
QJsonObject config; // = serverController.createContainerInitialConfig(c, port, tp);
|
||||||
|
|
||||||
emit uiLogic()->goToPage(Page::ServerConfiguringProgress);
|
emit uiLogic()->goToPage(Page::ServerConfiguringProgress);
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
|
|
|
@ -17,14 +17,16 @@ Item {
|
||||||
|
|
||||||
property string textColor: "#d7d8db"
|
property string textColor: "#d7d8db"
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||||
implicitHeight: content.implicitHeight
|
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: content
|
id: content
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.bottomMargin: 16
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: leftImageBackground
|
id: leftImageBackground
|
||||||
|
@ -56,8 +58,6 @@ Item {
|
||||||
color: root.textColor
|
color: root.textColor
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.bottomMargin: description.visible ? 0 : 16
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
@ -72,7 +72,6 @@ Item {
|
||||||
visible: root.descriptionText !== ""
|
visible: root.descriptionText !== ""
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.bottomMargin: 16
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
|
@ -48,6 +48,7 @@ PageType {
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
text: qsTr("Language")
|
text: qsTr("Language")
|
||||||
|
descriptionText: LanguageModel.getCurrentLanuageName()
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
@ -60,6 +61,22 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Logging")
|
||||||
|
descriptionText: SettingsController.isLoggingEnable ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
goToPage(PageEnum.PageSettingsLogging)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
@ -67,10 +84,27 @@ PageType {
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
|
questionDrawer.headerText = qsTr("Reset settings and remove all data from the application?")
|
||||||
|
questionDrawer.descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
|
||||||
|
questionDrawer.yesButtonText = qsTr("Continue")
|
||||||
|
questionDrawer.noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
questionDrawer.yesButtonFunction = function() {
|
||||||
|
questionDrawer.visible = false
|
||||||
|
SettingsController.clearSettings()
|
||||||
|
}
|
||||||
|
questionDrawer.noButtonFunction = function() {
|
||||||
|
questionDrawer.visible = false
|
||||||
|
}
|
||||||
|
questionDrawer.visible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
|
QuestionDrawer {
|
||||||
|
id: questionDrawer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,96 +44,6 @@ PageType {
|
||||||
headerText: qsTr("Backup")
|
headerText: qsTr("Backup")
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitcherType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 16
|
|
||||||
|
|
||||||
text: qsTr("Save logs")
|
|
||||||
|
|
||||||
checked: SettingsController.isSaveLogsEnabled()
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked !== SettingsController.isSaveLogsEnabled()) {
|
|
||||||
SettingsController.setSaveLogs(checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignBaseline
|
|
||||||
Layout.preferredWidth: root.width / 3
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
implicitWidth: 56
|
|
||||||
implicitHeight: 56
|
|
||||||
|
|
||||||
image: "qrc:/images/controls/folder-open.svg"
|
|
||||||
|
|
||||||
onClicked: SettingsController.openLogsFolder()
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Open folder with logs")
|
|
||||||
color: "#D7D8DB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignBaseline
|
|
||||||
Layout.preferredWidth: root.width / 3
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
implicitWidth: 56
|
|
||||||
implicitHeight: 56
|
|
||||||
|
|
||||||
image: "qrc:/images/controls/save.svg"
|
|
||||||
|
|
||||||
onClicked: SettingsController.exportLogsFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Save logs to file")
|
|
||||||
color: "#D7D8DB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignBaseline
|
|
||||||
Layout.preferredWidth: root.width / 3
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
implicitWidth: 56
|
|
||||||
implicitHeight: 56
|
|
||||||
|
|
||||||
image: "qrc:/images/controls/delete.svg"
|
|
||||||
|
|
||||||
onClicked: SettingsController.clearLogs()
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Clear logs")
|
|
||||||
color: "#D7D8DB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListItemTitleType {
|
ListItemTitleType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
|
|
138
client/ui/qml/Pages2/PageSettingsLogging.qml
Normal file
138
client/ui/qml/Pages2/PageSettingsLogging.qml
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Config"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: backButton.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
HeaderType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
headerText: qsTr("Logging")
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitcherType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
text: qsTr("Save logs")
|
||||||
|
|
||||||
|
checked: SettingsController.isLoggingEnable
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (checked !== SettingsController.isLoggingEnable) {
|
||||||
|
SettingsController.isLoggingEnable = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
Layout.preferredWidth: root.width / 3
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
implicitWidth: 56
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
image: "qrc:/images/controls/folder-open.svg"
|
||||||
|
|
||||||
|
onClicked: SettingsController.openLogsFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Open folder with logs")
|
||||||
|
color: "#D7D8DB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
Layout.preferredWidth: root.width / 3
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
implicitWidth: 56
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
image: "qrc:/images/controls/save.svg"
|
||||||
|
|
||||||
|
onClicked: SettingsController.exportLogsFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Save logs to file")
|
||||||
|
color: "#D7D8DB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
Layout.preferredWidth: root.width / 3
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
implicitWidth: 56
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
image: "qrc:/images/controls/delete.svg"
|
||||||
|
|
||||||
|
onClicked: SettingsController.clearLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Clear logs")
|
||||||
|
color: "#D7D8DB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue