Add advanced options &&

Fix some bugs
This commit is contained in:
Nova 2025-11-21 23:44:08 +03:30
parent 52e546242e
commit b2d917679f
11 changed files with 428 additions and 17 deletions

View File

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

View File

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

View File

@ -42,7 +42,7 @@ private:
QString network_title_base;
struct {
QString certificate;
QStringList certificate;
} CACHE;
void typeSelected(const QString &newType);

View File

@ -294,6 +294,13 @@
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="advanced_button">
<property name="text">
<string>Advanced Settings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -0,0 +1,42 @@
#pragma once
#include <QDialog>
#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<Configs::ProxyEntity> &_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<Configs::ProxyEntity> ent;
struct {
QStringList echConfig;
QStringList certSha256;
QStringList clientCert;
QStringList clientKey;
} CACHE;
};

View File

@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditAdvanced</class>
<widget class="QDialog" name="EditAdvanced">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>534</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="dial_box">
<property name="title">
<string>Dial Fields</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Connect Timeout</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="connect_timeout"/>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="tcp_fast_open">
<property name="text">
<string>TCP Fast Open</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="tcp_multipath">
<property name="text">
<string>TCP MultiPath</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="reuse_addr">
<property name="text">
<string>Reuse Address</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="udp_fragment">
<property name="text">
<string>UDP Fragment</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="tls_box">
<property name="title">
<string>TLS</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="3" column="0">
<widget class="QCheckBox" name="enable_ech">
<property name="text">
<string>Enable ECH</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>ECH Config</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="ech_config">
<property name="text">
<string>Not Set</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Certificate SHA256</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="cert_sha256">
<property name="text">
<string>Not Set</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Client Certificate</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="client_cert">
<property name="text">
<string>Not Set</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Client Key</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QPushButton" name="client_key">
<property name="text">
<string>Not Set</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="disable_sni">
<property name="text">
<string>Disable SNI</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>TLS Min Version</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="min_version"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>TLS Max Version</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="max_version"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EditAdvanced</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EditAdvanced</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@
#include <QInputDialog>
#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();
}
}

View File

@ -0,0 +1,130 @@
#include "include/ui/profile/edit_advanced.h"
#include <QInputDialog>
#include "include/dataStore/ProxyEntity.hpp"
EditAdvanced::EditAdvanced(QWidget *parent, const std::shared_ptr<Configs::ProxyEntity> &_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");
}
}
}