added button 'Reset settings and remove all data from the application'

This commit is contained in:
vladimir.kuznetsov 2023-07-14 22:59:49 +09:00
parent 3aaa7b62ef
commit 75489c00c2
18 changed files with 585 additions and 432 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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>

View file

@ -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();
}

View file

@ -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;

View file

@ -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
{ {

View file

@ -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();

View file

@ -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()) {

View file

@ -27,6 +27,7 @@ namespace PageLoader
PageSettingsApplication, PageSettingsApplication,
PageSettingsBackup, PageSettingsBackup,
PageSettingsAbout, PageSettingsAbout,
PageSettingsLogging,
PageSetupWizardStart, PageSetupWizardStart,
PageSetupWizardCredentials, PageSetupWizardCredentials,

View file

@ -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();
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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();

View file

@ -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

View file

@ -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
}
} }
} }
} }

View file

@ -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

View 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"
}
}
}
}
}
}