mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
* Add updated awg container * add missing files * Hide uninstalled AwgLegacy container * Fix resources file * Add role for allowed for installation containers * Add native config sharing for new Awg container * Fix not opening awg settings * Remove AwgLegacy from wizard manual installation page * Fix AmneziaWG settings * chore: update link to submodule * refactor: remove j1-j3 and itime * chore: return s3 s4 fields to ui * fix: awg2 native config compatability * chore: update packet size validation * feat: add awg2 support in self-hosted containers * fix: delete parameters from server config * feat: add H-parameters validation as a strings * chore: update link to submodule * chore: add containers type for awg 1.5 and awg 2 * chore: fixed s3/s4 visibility for awg 1 --------- Co-authored-by: aiamnezia <ai@amnezia.org>
397 lines
15 KiB
C++
397 lines
15 KiB
C++
#include "exportController.h"
|
|
|
|
#include <QBuffer>
|
|
#include <QDataStream>
|
|
#include <QDesktopServices>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QImage>
|
|
#include <QStandardPaths>
|
|
|
|
#include "core/controllers/vpnConfigurationController.h"
|
|
#include "core/qrCodeUtils.h"
|
|
#include "core/serialization/serialization.h"
|
|
#include "core/serialization/transfer.h"
|
|
#include "systemController.h"
|
|
#include <QDebug>
|
|
|
|
ExportController::ExportController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
|
|
const QSharedPointer<ClientManagementModel> &clientManagementModel,
|
|
const std::shared_ptr<Settings> &settings, QObject *parent)
|
|
: QObject(parent),
|
|
m_serversModel(serversModel),
|
|
m_containersModel(containersModel),
|
|
m_clientManagementModel(clientManagementModel),
|
|
m_settings(settings)
|
|
{
|
|
}
|
|
|
|
void ExportController::generateFullAccessConfig()
|
|
{
|
|
clearPreviousConfig();
|
|
|
|
int serverIndex = m_serversModel->getProcessedServerIndex();
|
|
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
|
|
|
QJsonArray containers = serverConfig.value(config_key::containers).toArray();
|
|
for (auto i = 0; i < containers.size(); i++) {
|
|
auto containerConfig = containers.at(i).toObject();
|
|
auto containerType = ContainerProps::containerFromString(containerConfig.value(config_key::container).toString());
|
|
|
|
for (auto protocol : ContainerProps::protocolsForContainer(containerType)) {
|
|
auto protocolConfig = containerConfig.value(ProtocolProps::protoToString(protocol)).toObject();
|
|
|
|
protocolConfig.remove(config_key::last_config);
|
|
containerConfig[ProtocolProps::protoToString(protocol)] = protocolConfig;
|
|
}
|
|
|
|
containers.replace(i, containerConfig);
|
|
}
|
|
serverConfig[config_key::containers] = containers;
|
|
|
|
QByteArray compressedConfig = QJsonDocument(serverConfig).toJson();
|
|
compressedConfig = qCompress(compressedConfig, 8);
|
|
m_config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
|
|
|
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(compressedConfig);
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateConnectionConfig(const QString &clientName)
|
|
{
|
|
clearPreviousConfig();
|
|
|
|
int serverIndex = m_serversModel->getProcessedServerIndex();
|
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
|
|
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
|
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
|
|
|
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
|
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
|
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
|
|
|
|
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig, clientName, serverController);
|
|
if (errorCode != ErrorCode::NoError) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
|
|
if (!errorCode) {
|
|
serverConfig.remove(config_key::userName);
|
|
serverConfig.remove(config_key::password);
|
|
serverConfig.remove(config_key::port);
|
|
serverConfig.insert(config_key::containers, QJsonArray { containerConfig });
|
|
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(container));
|
|
|
|
auto dns = m_serversModel->getDnsPair(serverIndex);
|
|
serverConfig.insert(config_key::dns1, dns.first);
|
|
serverConfig.insert(config_key::dns2, dns.second);
|
|
}
|
|
|
|
QByteArray compressedConfig = QJsonDocument(serverConfig).toJson();
|
|
compressedConfig = qCompress(compressedConfig, 8);
|
|
m_config = QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
|
|
|
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(compressedConfig);
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
ErrorCode ExportController::generateNativeConfig(const DockerContainer container, const QString &clientName, const Proto &protocol,
|
|
QJsonObject &jsonNativeConfig)
|
|
{
|
|
clearPreviousConfig();
|
|
|
|
int serverIndex = m_serversModel->getProcessedServerIndex();
|
|
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
|
auto dns = m_serversModel->getDnsPair(serverIndex);
|
|
bool isApiConfig = qvariant_cast<bool>(m_serversModel->data(serverIndex, ServersModel::IsServerFromTelegramApiRole));
|
|
|
|
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
|
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
|
|
|
|
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
|
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
|
|
|
QString protocolConfigString;
|
|
ErrorCode errorCode = vpnConfigurationController.createProtocolConfigString(isApiConfig, dns, credentials, container, containerConfig,
|
|
protocol, protocolConfigString);
|
|
if (errorCode != ErrorCode::NoError) {
|
|
return errorCode;
|
|
}
|
|
|
|
jsonNativeConfig = QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
|
|
|
|
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
|
errorCode = m_clientManagementModel->appendClient(jsonNativeConfig, clientName, container, credentials, serverController);
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
void ExportController::generateOpenVpnConfig(const QString &clientName)
|
|
{
|
|
QJsonObject nativeConfig;
|
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
|
|
ErrorCode errorCode = ErrorCode::NoError;
|
|
|
|
if (container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) {
|
|
errorCode = generateNativeConfig(container, clientName, Proto::OpenVpn, nativeConfig);
|
|
} else {
|
|
errorCode = generateNativeConfig(container, clientName, ContainerProps::defaultProtocol(container), nativeConfig);
|
|
}
|
|
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line + "\n");
|
|
}
|
|
|
|
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateWireGuardConfig(const QString &clientName)
|
|
{
|
|
QJsonObject nativeConfig;
|
|
ErrorCode errorCode = generateNativeConfig(DockerContainer::WireGuard, clientName, Proto::WireGuard, nativeConfig);
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line + "\n");
|
|
}
|
|
|
|
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
|
|
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
|
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateAwgConfig(const QString &clientName)
|
|
{
|
|
QJsonObject nativeConfig;
|
|
ErrorCode errorCode = generateNativeConfig(DockerContainer::Awg2, clientName, Proto::Awg, nativeConfig);
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line + "\n");
|
|
}
|
|
|
|
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
|
|
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
|
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateShadowSocksConfig()
|
|
{
|
|
QJsonObject nativeConfig;
|
|
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getProcessedContainerIndex());
|
|
ErrorCode errorCode = ErrorCode::NoError;
|
|
|
|
if (container == DockerContainer::Cloak) {
|
|
errorCode = generateNativeConfig(container, "", Proto::ShadowSocks, nativeConfig);
|
|
} else {
|
|
errorCode = generateNativeConfig(container, "", ContainerProps::defaultProtocol(container), nativeConfig);
|
|
}
|
|
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line + "\n");
|
|
}
|
|
|
|
m_nativeConfigString = QString("%1:%2@%3:%4")
|
|
.arg(nativeConfig.value("method").toString(), nativeConfig.value("password").toString(),
|
|
nativeConfig.value("server").toString(), nativeConfig.value("server_port").toString());
|
|
|
|
m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64();
|
|
|
|
auto qr = qrCodeUtils::generateQrCode(m_nativeConfigString.toUtf8());
|
|
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
|
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateCloakConfig()
|
|
{
|
|
QJsonObject nativeConfig;
|
|
ErrorCode errorCode = generateNativeConfig(DockerContainer::Cloak, "", Proto::Cloak, nativeConfig);
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
nativeConfig.remove(config_key::transport_proto);
|
|
nativeConfig.insert("ProxyMethod", "shadowsocks");
|
|
|
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line + "\n");
|
|
}
|
|
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
void ExportController::generateXrayConfig(const QString &clientName)
|
|
{
|
|
//Xray data
|
|
QJsonObject nativeConfig;
|
|
ErrorCode errorCode = generateNativeConfig(DockerContainer::Xray, clientName, Proto::Xray, nativeConfig);
|
|
if (errorCode) {
|
|
emit exportErrorOccurred(errorCode);
|
|
return;
|
|
}
|
|
|
|
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n");
|
|
for (const QString &line : std::as_const(lines)) {
|
|
m_config.append(line+ "\n");
|
|
}
|
|
//Xray data
|
|
|
|
// Parse the Xray data to extract VLESS parameters and generate string
|
|
QString configString = QString(QJsonDocument(nativeConfig).toJson(QJsonDocument::Compact));
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(configString.toUtf8());
|
|
if (doc.isNull() || !doc.isObject()) {
|
|
qDebug() << "ERROR: Failed to parse config JSON";
|
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
|
return;
|
|
}
|
|
|
|
QJsonObject xrayConfig = doc.object();
|
|
QJsonArray outbounds = xrayConfig.value("outbounds").toArray();
|
|
|
|
if (outbounds.isEmpty()) {
|
|
qDebug() << "ERROR: Outbounds array is empty";
|
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
|
return;
|
|
}
|
|
|
|
QJsonObject outbound = outbounds[0].toObject();
|
|
QJsonObject settings = outbound.value("settings").toObject();
|
|
QJsonObject streamSettings = outbound.value("streamSettings").toObject();
|
|
|
|
QJsonArray vnext = settings.value("vnext").toArray();
|
|
if (vnext.isEmpty()) {
|
|
qDebug() << "ERROR: vnext array is empty";
|
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
|
return;
|
|
}
|
|
|
|
QJsonObject server = vnext[0].toObject();
|
|
QJsonArray users = server.value("users").toArray();
|
|
if (users.isEmpty()) {
|
|
qDebug() << "ERROR: users array is empty";
|
|
emit exportErrorOccurred(ErrorCode::InternalError);
|
|
return;
|
|
}
|
|
|
|
QJsonObject user = users[0].toObject();
|
|
|
|
amnezia::serialization::VlessServerObject vlessServer;
|
|
vlessServer.address = server.value("address").toString();
|
|
vlessServer.port = server.value("port").toInt();
|
|
vlessServer.id = user.value("id").toString();
|
|
vlessServer.flow = user.value("flow").toString("xtls-rprx-vision");
|
|
vlessServer.encryption = user.value("encryption").toString("none");
|
|
|
|
vlessServer.network = streamSettings.value("network").toString("tcp");
|
|
vlessServer.security = streamSettings.value("security").toString("reality");
|
|
|
|
|
|
if (vlessServer.security == "reality") {
|
|
QJsonObject realitySettings = streamSettings.value("realitySettings").toObject();
|
|
vlessServer.serverName = realitySettings.value("serverName").toString();
|
|
vlessServer.publicKey = realitySettings.value("publicKey").toString();
|
|
vlessServer.shortId = realitySettings.value("shortId").toString();
|
|
vlessServer.fingerprint = realitySettings.value("fingerprint").toString("chrome");
|
|
vlessServer.spiderX = realitySettings.value("spiderX").toString("");
|
|
}
|
|
|
|
m_nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
|
|
|
|
|
emit exportConfigChanged();
|
|
}
|
|
|
|
QString ExportController::getConfig()
|
|
{
|
|
return m_config;
|
|
}
|
|
|
|
QString ExportController::getNativeConfigString()
|
|
{
|
|
return m_nativeConfigString;
|
|
}
|
|
|
|
QList<QString> ExportController::getQrCodes()
|
|
{
|
|
return m_qrCodes;
|
|
}
|
|
|
|
void ExportController::exportConfig(const QString &fileName)
|
|
{
|
|
SystemController::saveFile(fileName, m_config);
|
|
}
|
|
|
|
void ExportController::updateClientManagementModel(const DockerContainer container, ServerCredentials credentials)
|
|
{
|
|
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
|
ErrorCode errorCode = m_clientManagementModel->updateModel(container, credentials, serverController);
|
|
if (errorCode != ErrorCode::NoError) {
|
|
emit exportErrorOccurred(errorCode);
|
|
}
|
|
}
|
|
|
|
void ExportController::revokeConfig(const int row, const DockerContainer container, ServerCredentials credentials)
|
|
{
|
|
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
|
ErrorCode errorCode =
|
|
m_clientManagementModel->revokeClient(row, container, credentials, m_serversModel->getProcessedServerIndex(), serverController);
|
|
if (errorCode != ErrorCode::NoError) {
|
|
emit exportErrorOccurred(errorCode);
|
|
}
|
|
emit revokeConfigCompleted();
|
|
}
|
|
|
|
void ExportController::renameClient(const int row, const QString &clientName, const DockerContainer container, ServerCredentials credentials)
|
|
{
|
|
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
|
ErrorCode errorCode = m_clientManagementModel->renameClient(row, clientName, container, credentials, serverController);
|
|
if (errorCode != ErrorCode::NoError) {
|
|
emit exportErrorOccurred(errorCode);
|
|
}
|
|
}
|
|
|
|
int ExportController::getQrCodesCount()
|
|
{
|
|
return m_qrCodes.size();
|
|
}
|
|
|
|
void ExportController::clearPreviousConfig()
|
|
{
|
|
m_config.clear();
|
|
m_nativeConfigString.clear();
|
|
m_qrCodes.clear();
|
|
|
|
emit exportConfigChanged();
|
|
}
|