diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6372781..7907308 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -287,6 +287,9 @@ set(PROJECT_SOURCES
include/ui/profile/edit_tuic.h
src/ui/profile/edit_tuic.cpp
include/ui/profile/edit_tuic.ui
+ include/ui/profile/edit_advanced.h
+ src/ui/profile/edit_advanced.cpp
+ include/ui/profile/edit_advanced.ui
)
if (NOT APPLE AND Qt6_VERSION VERSION_GREATER_EQUAL 6.9.0)
diff --git a/include/configs/common/TLS.h b/include/configs/common/TLS.h
index f9eaf53..9a6536a 100644
--- a/include/configs/common/TLS.h
+++ b/include/configs/common/TLS.h
@@ -82,10 +82,10 @@ namespace Configs
QString max_version;
QStringList cipher_suites;
QStringList curve_preferences;
- QString certificate;
+ QStringList certificate;
QString certificate_path;
QStringList certificate_public_key_sha256;
- QString client_certificate;
+ QStringList client_certificate;
QString client_certificate_path;
QStringList client_key;
QString client_key_path;
@@ -107,10 +107,10 @@ namespace Configs
_add(new configItem("max_version", &max_version, string));
_add(new configItem("cipher_suites", &cipher_suites, stringList));
_add(new configItem("curve_preferences", &curve_preferences, stringList));
- _add(new configItem("certificate", &certificate, string));
+ _add(new configItem("certificate", &certificate, stringList));
_add(new configItem("certificate_path", &certificate_path, string));
_add(new configItem("certificate_public_key_sha256", &certificate_public_key_sha256, stringList));
- _add(new configItem("client_certificate", &client_certificate, string));
+ _add(new configItem("client_certificate", &client_certificate, stringList));
_add(new configItem("client_certificate_path", &client_certificate_path, string));
_add(new configItem("client_key", &client_key, stringList));
_add(new configItem("client_key_path", &client_key_path, string));
diff --git a/include/ui/profile/dialog_edit_profile.h b/include/ui/profile/dialog_edit_profile.h
index 3819ff0..cf83347 100644
--- a/include/ui/profile/dialog_edit_profile.h
+++ b/include/ui/profile/dialog_edit_profile.h
@@ -42,7 +42,7 @@ private:
QString network_title_base;
struct {
- QString certificate;
+ QStringList certificate;
} CACHE;
void typeSelected(const QString &newType);
diff --git a/include/ui/profile/dialog_edit_profile.ui b/include/ui/profile/dialog_edit_profile.ui
index 0738be6..d8a7507 100644
--- a/include/ui/profile/dialog_edit_profile.ui
+++ b/include/ui/profile/dialog_edit_profile.ui
@@ -294,6 +294,13 @@
+ -
+
+
+ Advanced Settings
+
+
+
diff --git a/include/ui/profile/edit_advanced.h b/include/ui/profile/edit_advanced.h
new file mode 100644
index 0000000..585e763
--- /dev/null
+++ b/include/ui/profile/edit_advanced.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include "ui_edit_advanced.h"
+#include "include/dataStore/ProxyEntity.hpp"
+
+namespace Ui {
+class EditAdvanced;
+}
+
+class EditAdvanced : public QDialog
+{
+ Q_OBJECT
+
+public:
+ EditAdvanced(QWidget *parent, const std::shared_ptr &_ent);
+
+ ~EditAdvanced() override;
+
+public slots:
+ void accept() override;
+
+private slots:
+ void on_ech_config_clicked();
+
+ void on_cert_sha256_clicked();
+
+ void on_client_cert_clicked();
+
+ void on_client_key_clicked();
+
+private:
+ Ui::EditAdvanced *ui;
+ std::shared_ptr ent;
+
+ struct {
+ QStringList echConfig;
+ QStringList certSha256;
+ QStringList clientCert;
+ QStringList clientKey;
+ } CACHE;
+};
\ No newline at end of file
diff --git a/include/ui/profile/edit_advanced.ui b/include/ui/profile/edit_advanced.ui
new file mode 100644
index 0000000..81f7e2f
--- /dev/null
+++ b/include/ui/profile/edit_advanced.ui
@@ -0,0 +1,210 @@
+
+
+ EditAdvanced
+
+
+
+ 0
+ 0
+ 400
+ 534
+
+
+
+ Dialog
+
+
+ -
+
+
+ Dial Fields
+
+
+
-
+
+
+ Connect Timeout
+
+
+
+ -
+
+
+ -
+
+
+ TCP Fast Open
+
+
+
+ -
+
+
+ TCP MultiPath
+
+
+
+ -
+
+
+ Reuse Address
+
+
+
+ -
+
+
+ UDP Fragment
+
+
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
+
+
+
+ -
+
+
+ TLS
+
+
+
-
+
+
+ Enable ECH
+
+
+
+ -
+
+
+ ECH Config
+
+
+
+ -
+
+
+ Not Set
+
+
+
+ -
+
+
+ Certificate SHA256
+
+
+
+ -
+
+
+ Not Set
+
+
+
+ -
+
+
+ Client Certificate
+
+
+
+ -
+
+
+ Not Set
+
+
+
+ -
+
+
+ Client Key
+
+
+
+ -
+
+
+ Not Set
+
+
+
+ -
+
+
+ Disable SNI
+
+
+
+ -
+
+
+ TLS Min Version
+
+
+
+ -
+
+
+ -
+
+
+ TLS Max Version
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ EditAdvanced
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ EditAdvanced
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/configs/common/TLS.cpp b/src/configs/common/TLS.cpp
index 31b3e8a..07e0c4a 100644
--- a/src/configs/common/TLS.cpp
+++ b/src/configs/common/TLS.cpp
@@ -169,10 +169,10 @@ namespace Configs {
if (query.hasQueryItem("tls_max_version")) max_version = query.queryItemValue("tls_max_version");
if (query.hasQueryItem("tls_cipher_suites")) cipher_suites = query.queryItemValue("tls_cipher_suites").split(",");
if (query.hasQueryItem("tls_curve_preferences")) curve_preferences = query.queryItemValue("tls_curve_preferences").split(",");
- if (query.hasQueryItem("tls_certificate")) certificate = query.queryItemValue("tls_certificate");
+ if (query.hasQueryItem("tls_certificate")) certificate = query.queryItemValue("tls_certificate").split(",");
if (query.hasQueryItem("tls_certificate_path")) certificate_path = query.queryItemValue("tls_certificate_path");
if (query.hasQueryItem("tls_certificate_public_key_sha256")) certificate_public_key_sha256 = query.queryItemValue("tls_certificate_public_key_sha256").split(",");
- if (query.hasQueryItem("tls_client_certificate")) client_certificate = query.queryItemValue("tls_client_certificate");
+ if (query.hasQueryItem("tls_client_certificate")) client_certificate = query.queryItemValue("tls_client_certificate").split(",");
if (query.hasQueryItem("tls_client_certificate_path")) client_certificate_path = query.queryItemValue("tls_client_certificate_path");
if (query.hasQueryItem("tls_client_key")) client_key = query.queryItemValue("tls_client_key").split(",");
if (query.hasQueryItem("tls_client_key_path")) client_key_path = query.queryItemValue("tls_client_key_path");
@@ -203,12 +203,20 @@ namespace Configs {
if (object.contains("curve_preferences")) {
curve_preferences = QJsonArray2QListString(object["curve_preferences"].toArray());
}
- if (object.contains("certificate")) certificate = object["certificate"].toString();
+ if (object.contains("certificate")) {
+ if (object["certificate"].isString()) {
+ certificate = object["certificate"].toString().split("\n", Qt::SkipEmptyParts);
+ } else {
+ certificate = QJsonArray2QListString(object["certificate"].toArray());
+ }
+ }
if (object.contains("certificate_path")) certificate_path = object["certificate_path"].toString();
if (object.contains("certificate_public_key_sha256")) {
certificate_public_key_sha256 = QJsonArray2QListString(object["certificate_public_key_sha256"].toArray());
}
- if (object.contains("client_certificate")) client_certificate = object["client_certificate"].toString();
+ if (object.contains("client_certificate")) {
+ client_certificate = QJsonArray2QListString(object["client_certificate"].toArray());
+ }
if (object.contains("client_certificate_path")) client_certificate_path = object["client_certificate_path"].toString();
if (object.contains("client_key")) {
client_key = QJsonArray2QListString(object["client_key"].toArray());
@@ -235,10 +243,10 @@ namespace Configs {
if (!max_version.isEmpty()) query.addQueryItem("tls_max_version", max_version);
if (!cipher_suites.isEmpty()) query.addQueryItem("tls_cipher_suites", cipher_suites.join(","));
if (!curve_preferences.isEmpty()) query.addQueryItem("tls_curve_preferences", curve_preferences.join(","));
- if (!certificate.isEmpty()) query.addQueryItem("tls_certificate", certificate);
+ if (!certificate.isEmpty()) query.addQueryItem("tls_certificate", certificate.join(","));
if (!certificate_path.isEmpty()) query.addQueryItem("tls_certificate_path", certificate_path);
if (!certificate_public_key_sha256.isEmpty()) query.addQueryItem("tls_certificate_public_key_sha256", certificate_public_key_sha256.join(","));
- if (!client_certificate.isEmpty()) query.addQueryItem("tls_client_certificate", client_certificate);
+ if (!client_certificate.isEmpty()) query.addQueryItem("tls_client_certificate", client_certificate.join(","));
if (!client_certificate_path.isEmpty()) query.addQueryItem("tls_client_certificate_path", client_certificate_path);
if (!client_key.isEmpty()) query.addQueryItem("tls_client_key", client_key.join(","));
if (!client_key_path.isEmpty()) query.addQueryItem("tls_client_key_path", client_key_path);
@@ -269,12 +277,14 @@ namespace Configs {
if (!curve_preferences.isEmpty()) {
object["curve_preferences"] = QListStr2QJsonArray(curve_preferences);
}
- if (!certificate.isEmpty()) object["certificate"] = certificate;
+ if (!certificate.isEmpty()) {
+ object["certificate"] = QListStr2QJsonArray(certificate);
+ }
if (!certificate_path.isEmpty()) object["certificate_path"] = certificate_path;
if (!certificate_public_key_sha256.isEmpty()) {
object["certificate_public_key_sha256"] = QListStr2QJsonArray(certificate_public_key_sha256);
}
- if (!client_certificate.isEmpty()) object["client_certificate"] = client_certificate;
+ if (!client_certificate.isEmpty()) object["client_certificate"] = QListStr2QJsonArray(client_certificate);
if (!client_certificate_path.isEmpty()) object["client_certificate_path"] = client_certificate_path;
if (!client_key.isEmpty()) {
object["client_key"] = QListStr2QJsonArray(client_key);
diff --git a/src/configs/common/transport.cpp b/src/configs/common/transport.cpp
index be0dda7..43b0e91 100644
--- a/src/configs/common/transport.cpp
+++ b/src/configs/common/transport.cpp
@@ -103,6 +103,7 @@ namespace Configs {
QString Transport::ExportToLink()
{
QUrlQuery query;
+ if (type.isEmpty() || type == "tcp") return "";
if (!type.isEmpty()) query.addQueryItem("type", type);
if (!host.isEmpty()) query.addQueryItem("host", host);
if (!path.isEmpty()) query.addQueryItem("path", path);
@@ -118,6 +119,7 @@ namespace Configs {
QJsonObject Transport::ExportToJson()
{
QJsonObject object;
+ if (type.isEmpty() || type == "tcp") return object;
if (!type.isEmpty()) object["type"] = type;
if (!host.isEmpty()) object["host"] = host;
if (!path.isEmpty()) object["path"] = path;
diff --git a/src/configs/sub/GroupUpdater.cpp b/src/configs/sub/GroupUpdater.cpp
index 91dee8b..1218f4f 100644
--- a/src/configs/sub/GroupUpdater.cpp
+++ b/src/configs/sub/GroupUpdater.cpp
@@ -693,7 +693,7 @@ namespace Subscription {
bean->tls->enabled = true;
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
auto alpn = Node2QStringList(proxy["alpn"]);
- bean->tls->certificate = Node2QString(proxy["ca-str"]);
+ bean->tls->certificate = Node2QString(proxy["ca-str"]).split("\n", Qt::SkipEmptyParts);
if (!alpn.isEmpty()) bean->tls->alpn = {alpn[0]};
bean->tls->server_name = Node2QString(proxy["sni"]);
@@ -755,7 +755,7 @@ namespace Subscription {
bean->zero_rtt_handshake = Node2Bool(proxy["reduce-rtt"]);
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
bean->tls->alpn = Node2QStringList(proxy["alpn"]);
- bean->tls->certificate = Node2QString(proxy["ca-str"]);
+ bean->tls->certificate = Node2QString(proxy["ca-str"]).split("\n", Qt::SkipEmptyParts);
bean->tls->server_name = Node2QString(proxy["sni"]);
if (Node2Bool(proxy["udp-over-stream"])) bean->udp_over_stream = true;
diff --git a/src/ui/profile/dialog_edit_profile.cpp b/src/ui/profile/dialog_edit_profile.cpp
index 351a714..271b3f5 100644
--- a/src/ui/profile/dialog_edit_profile.cpp
+++ b/src/ui/profile/dialog_edit_profile.cpp
@@ -20,6 +20,7 @@
#include
+#include "include/ui/profile/edit_advanced.h"
#include "include/ui/profile/edit_hysteria.h"
#include "include/ui/profile/edit_socks.h"
#include "include/ui/profile/edit_trojan.h"
@@ -125,6 +126,12 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
}
});
+ // Advanced options
+ connect(ui->advanced_button, &QPushButton::clicked, this, [=,this]() {
+ auto advancedWidget = new EditAdvanced(this, ent);
+ advancedWidget->show();
+ });
+
newEnt = _type != "";
if (newEnt) {
this->groupId = profileOrGroupId;
@@ -476,9 +483,9 @@ void DialogEditProfile::editor_cache_updated_impl() {
void DialogEditProfile::on_certificate_edit_clicked() {
bool ok;
- auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.certificate, &ok);
+ auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.certificate.join("\n"), &ok);
if (ok) {
- CACHE.certificate = txt;
+ CACHE.certificate = txt.split("\n", Qt::SkipEmptyParts);
editor_cache_updated_impl();
}
}
diff --git a/src/ui/profile/edit_advanced.cpp b/src/ui/profile/edit_advanced.cpp
new file mode 100644
index 0000000..7125ff3
--- /dev/null
+++ b/src/ui/profile/edit_advanced.cpp
@@ -0,0 +1,130 @@
+#include "include/ui/profile/edit_advanced.h"
+
+#include
+
+#include "include/dataStore/ProxyEntity.hpp"
+
+EditAdvanced::EditAdvanced(QWidget *parent, const std::shared_ptr &_ent)
+ : QDialog(parent)
+ , ui(new Ui::EditAdvanced)
+{
+ ui->setupUi(this);
+ ent = _ent;
+ auto dialFieldsObj = ent->outbound->dialFields;
+ ui->reuse_addr->setChecked(dialFieldsObj->reuse_addr);
+ ui->tcp_fast_open->setChecked(dialFieldsObj->tcp_fast_open);
+ ui->udp_fragment->setChecked(dialFieldsObj->udp_fragment);
+ ui->tcp_multipath->setChecked(dialFieldsObj->tcp_multi_path);
+ ui->connect_timeout->setText(dialFieldsObj->connect_timeout);
+
+ if (ent->outbound->HasTLS()) {
+ auto tlsObj = ent->outbound->GetTLS();
+ ui->disable_sni->setChecked(tlsObj->disable_sni);
+ ui->min_version->setText(tlsObj->min_version);
+ ui->max_version->setText(tlsObj->max_version);
+ ui->enable_ech->setChecked(tlsObj->ech->enabled);
+
+ // TODO enable when sing-box version is >= 1.13
+ ui->cert_sha256->setEnabled(false);
+ ui->client_cert->setEnabled(false);
+ ui->client_key->setEnabled(false);
+ if (!tlsObj->ech->config.isEmpty()) {
+ ui->ech_config->setText("Already set");
+ CACHE.echConfig = tlsObj->ech->config;
+ }
+ if (!tlsObj->certificate_public_key_sha256.isEmpty()) {
+ ui->cert_sha256->setText("Already set");
+ CACHE.certSha256 = tlsObj->certificate_public_key_sha256;
+ }
+ if (!tlsObj->client_certificate.isEmpty()) {
+ ui->client_cert->setText("Already set");
+ CACHE.clientCert = tlsObj->client_certificate;
+ }
+ if (!tlsObj->client_key.isEmpty()) {
+ ui->client_key->setText("Already set");
+ CACHE.clientKey = tlsObj->client_key;
+ }
+ } else {
+ ui->tls_box->hide();
+ adjustSize();
+ }
+}
+
+EditAdvanced::~EditAdvanced()
+{
+ delete ui;
+}
+
+void EditAdvanced::accept() {
+ auto dialFieldsObj = ent->outbound->dialFields;
+ dialFieldsObj->reuse_addr = ui->reuse_addr->isChecked();
+ dialFieldsObj->tcp_fast_open = ui->tcp_fast_open->isChecked();
+ dialFieldsObj->udp_fragment = ui->udp_fragment->isChecked();
+ dialFieldsObj->tcp_multi_path = ui->tcp_multipath->isChecked();
+ dialFieldsObj->connect_timeout = ui->connect_timeout->text();
+
+ if (ent->outbound->HasTLS()) {
+ auto tlsObj = ent->outbound->GetTLS();
+ tlsObj->disable_sni = ui->disable_sni->isChecked();
+ tlsObj->min_version = ui->min_version->text();
+ tlsObj->max_version = ui->max_version->text();
+ tlsObj->ech->enabled = ui->enable_ech->isChecked();
+ tlsObj->ech->config = CACHE.echConfig;
+ tlsObj->client_certificate = CACHE.clientCert;
+ tlsObj->client_key = CACHE.clientKey;
+ tlsObj->certificate_public_key_sha256 = CACHE.certSha256;
+ }
+ QDialog::accept();
+}
+
+void EditAdvanced::on_ech_config_clicked() {
+ bool ok;
+ auto txt = QInputDialog::getMultiLineText(this, tr("ECH Config"), "", CACHE.echConfig.join("\n"), &ok);
+ if (ok) {
+ CACHE.echConfig = txt.split("\n", Qt::SkipEmptyParts);
+ if (!CACHE.echConfig.isEmpty()) {
+ ui->ech_config->setText("Already set");
+ } else {
+ ui->ech_config->setText("Not Set");
+ }
+ }
+}
+
+void EditAdvanced::on_client_cert_clicked() {
+ bool ok;
+ auto txt = QInputDialog::getMultiLineText(this, tr("Client Certificate"), "", CACHE.clientCert.join("\n"), &ok);
+ if (ok) {
+ CACHE.clientCert = txt.split("\n", Qt::SkipEmptyParts);
+ if (!CACHE.echConfig.isEmpty()) {
+ ui->client_cert->setText("Already set");
+ } else {
+ ui->client_cert->setText("Not Set");
+ }
+ }
+}
+
+void EditAdvanced::on_client_key_clicked() {
+ bool ok;
+ auto txt = QInputDialog::getMultiLineText(this, tr("Client Key"), "", CACHE.clientKey.join("\n"), &ok);
+ if (ok) {
+ CACHE.clientKey = txt.split("\n", Qt::SkipEmptyParts);
+ if (!CACHE.echConfig.isEmpty()) {
+ ui->client_key->setText("Already set");
+ } else {
+ ui->client_key->setText("Not Set");
+ }
+ }
+}
+
+void EditAdvanced::on_cert_sha256_clicked() {
+ bool ok;
+ auto txt = QInputDialog::getMultiLineText(this, tr("Certificate sha256"), "", CACHE.certSha256.join("\n"), &ok);
+ if (ok) {
+ CACHE.certSha256 = txt.split("\n", Qt::SkipEmptyParts);
+ if (!CACHE.echConfig.isEmpty()) {
+ ui->cert_sha256->setText("Already set");
+ } else {
+ ui->cert_sha256->setText("Not Set");
+ }
+ }
+}
\ No newline at end of file