Merge pull request #407 from amnezia-vpn/feature/ss-and-cloak-native-configs

added native config generation for ss and cloak
This commit is contained in:
pokamest 2023-11-29 05:28:33 -08:00 committed by GitHub
commit 1cc5c5384e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 10 deletions

View file

@ -8,7 +8,9 @@
#include <QImage> #include <QImage>
#include <QStandardPaths> #include <QStandardPaths>
#include "configurators/cloak_configurator.h"
#include "configurators/openvpn_configurator.h" #include "configurators/openvpn_configurator.h"
#include "configurators/shadowsocks_configurator.h"
#include "configurators/wireguard_configurator.h" #include "configurators/wireguard_configurator.h"
#include "core/errorstrings.h" #include "core/errorstrings.h"
#include "systemController.h" #include "systemController.h"
@ -187,6 +189,88 @@ void ExportController::generateWireGuardConfig()
m_config.append(line + "\n"); m_config.append(line + "\n");
} }
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_config.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
void ExportController::generateShadowSocksConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config = m_configurator->shadowSocksConfigurator->genShadowSocksConfig(credentials, container,
containerConfig, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::ShadowSocks, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
m_nativeConfigString =
QString("%1:%2@%3:%4")
.arg(configJson.value("method").toString(), configJson.value("password").toString(),
configJson.value("server").toString(), configJson.value("server_port").toString());
m_nativeConfigString = "ss://" + m_nativeConfigString.toUtf8().toBase64();
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(m_nativeConfigString.toUtf8(), qrcodegen::QrCode::Ecc::LOW);
m_qrCodes << svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
void ExportController::generateCloakConfig()
{
clearPreviousConfig();
int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex();
ServerCredentials credentials =
qvariant_cast<ServerCredentials>(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole));
DockerContainer container = static_cast<DockerContainer>(m_containersModel->getCurrentlyProcessedContainerIndex());
QModelIndex containerModelIndex = m_containersModel->index(container);
QJsonObject containerConfig =
qvariant_cast<QJsonObject>(m_containersModel->data(containerModelIndex, ContainersModel::Roles::ConfigRole));
containerConfig.insert(config_key::container, ContainerProps::containerToString(container));
ErrorCode errorCode = ErrorCode::NoError;
QString config =
m_configurator->cloakConfigurator->genCloakConfig(credentials, container, containerConfig, &errorCode);
if (errorCode) {
emit exportErrorOccurred(errorString(errorCode));
return;
}
config = m_configurator->processConfigWithExportSettings(serverIndex, container, Proto::Cloak, config);
QJsonObject configJson = QJsonDocument::fromJson(config.toUtf8()).object();
configJson.remove(config_key::transport_proto);
configJson.insert("ProxyMethod", "shadowsocks");
QStringList lines = QString(QJsonDocument(configJson).toJson()).replace("\r", "").split("\n");
for (const QString &line : lines) {
m_config.append(line + "\n");
}
emit exportConfigChanged(); emit exportConfigChanged();
} }
@ -195,6 +279,11 @@ QString ExportController::getConfig()
return m_config; return m_config;
} }
QString ExportController::getNativeConfigString()
{
return m_nativeConfigString;
}
QList<QString> ExportController::getQrCodes() QList<QString> ExportController::getQrCodes()
{ {
return m_qrCodes; return m_qrCodes;
@ -219,7 +308,7 @@ QList<QString> ExportController::generateQrCodeImageSeries(const QByteArray &dat
QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW); qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(ba, qrcodegen::QrCode::Ecc::LOW);
QString svg = QString::fromStdString(toSvgString(qr, 0)); QString svg = QString::fromStdString(toSvgString(qr, 1));
chunks.append(svgToBase64(svg)); chunks.append(svgToBase64(svg));
} }
@ -239,5 +328,6 @@ int ExportController::getQrCodesCount()
void ExportController::clearPreviousConfig() void ExportController::clearPreviousConfig()
{ {
m_config.clear(); m_config.clear();
m_nativeConfigString.clear();
m_qrCodes.clear(); m_qrCodes.clear();
} }

View file

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

View file

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

View file

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

View file

@ -20,7 +20,9 @@ PageType {
AmneziaConnection, AmneziaConnection,
AmneziaFullAccess, AmneziaFullAccess,
OpenVpn, OpenVpn,
WireGuard WireGuard,
ShadowSocks,
Cloak
} }
Connections { Connections {
@ -44,18 +46,32 @@ PageType {
break; break;
} }
case PageShare.ConfigType.OpenVpn: { case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig(); ExportController.generateOpenVpnConfig()
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config") shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
shareConnectionDrawer.configExtension = ".ovpn" shareConnectionDrawer.configExtension = ".ovpn"
shareConnectionDrawer.configFileName = "amnezia_for_openvpn" shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
break; break
} }
case PageShare.ConfigType.WireGuard: { case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig(); ExportController.generateWireGuardConfig()
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config") shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
shareConnectionDrawer.configExtension = ".conf" shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_wireguard" shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break; break
}
case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save ShadowSocks config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
break
}
case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig()
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break
} }
} }
@ -96,6 +112,16 @@ PageType {
property string name: qsTr("WireGuard native format") property string name: qsTr("WireGuard native format")
property var type: PageShare.ConfigType.WireGuard property var type: PageShare.ConfigType.WireGuard
} }
QtObject {
id: shadowSocksConnectionFormat
property string name: qsTr("ShadowSocks native format")
property var type: PageShare.ConfigType.ShadowSocks
}
QtObject {
id: cloakConnectionFormat
property string name: qsTr("Cloak native format")
property var type: PageShare.ConfigType.Cloak
}
FlickableType { FlickableType {
anchors.top: parent.top anchors.top: parent.top
@ -322,6 +348,13 @@ PageType {
root.connectionTypesModel.push(openVpnConnectionFormat) root.connectionTypesModel.push(openVpnConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-wireguard")) { } else if (index === ContainerProps.containerFromString("amnezia-wireguard")) {
root.connectionTypesModel.push(wireGuardConnectionFormat) root.connectionTypesModel.push(wireGuardConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-shadowsocks")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
} else if (index === ContainerProps.containerFromString("amnezia-openvpn-cloak")) {
root.connectionTypesModel.push(openVpnConnectionFormat)
root.connectionTypesModel.push(shadowSocksConnectionFormat)
root.connectionTypesModel.push(cloakConnectionFormat)
} }
} }
} }