auto_socket: Windows-скан + список сокетов в Network Settings (add/remove, чистый rewrite конфига)
This commit is contained in:
1 parent
d5119ac7c6
commit
f82dca3185
8 files changed
+545
-160
No files matched your search
@@ -1,14 +1,19 @@
|
||||
// networksettingspage.cpp — Network configuration form
|
||||
// networksettingspage.cpp — Network configuration form (список listen-сокетов)
|
||||
#include "networksettingspage.h"
|
||||
#include "../transport/config_updater.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QNetworkInterface>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
static const char* SECTION = "server:lan";
|
||||
static const QString AUTO_IP_LABEL = "Auto (0.0.0.0)";
|
||||
|
||||
NetworkSettingsPage::NetworkSettingsPage(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
@@ -17,140 +22,92 @@ NetworkSettingsPage::NetworkSettingsPage(QWidget* parent)
|
||||
layout->setSpacing(12);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto* title = new QLabel("Listen Address", this);
|
||||
auto* title = new QLabel("Listen Sockets", this);
|
||||
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
||||
layout->addWidget(title);
|
||||
|
||||
// Interface
|
||||
auto* ifRow = new QHBoxLayout();
|
||||
ifRow->addWidget(new QLabel("Interface:", this));
|
||||
m_interfaceCombo = new QComboBox(this);
|
||||
m_interfaceCombo->setMinimumWidth(180);
|
||||
ifRow->addWidget(m_interfaceCombo, 1);
|
||||
layout->addLayout(ifRow);
|
||||
m_autoSocketsCheck = new QCheckBox("Auto sockets (UDP+TCP from interfaces)", this);
|
||||
layout->addWidget(m_autoSocketsCheck);
|
||||
|
||||
// IP
|
||||
auto* ipRow = new QHBoxLayout();
|
||||
ipRow->addWidget(new QLabel("IP Address:", this));
|
||||
m_serverList = new QListWidget(this);
|
||||
m_serverList->setMinimumHeight(110);
|
||||
layout->addWidget(m_serverList);
|
||||
|
||||
auto* btnRow = new QHBoxLayout();
|
||||
m_addBtn = new QPushButton("Add", this);
|
||||
m_removeBtn = new QPushButton("Remove", this);
|
||||
btnRow->addWidget(m_addBtn);
|
||||
btnRow->addWidget(m_removeBtn);
|
||||
btnRow->addStretch();
|
||||
layout->addLayout(btnRow);
|
||||
|
||||
// --- форма правки выбранного сокета ---
|
||||
auto* formLayout = new QGridLayout();
|
||||
|
||||
formLayout->addWidget(new QLabel("Name:", this), 0, 0);
|
||||
m_nameEdit = new QLineEdit(this);
|
||||
m_nameEdit->setMinimumWidth(180);
|
||||
formLayout->addWidget(m_nameEdit, 0, 1);
|
||||
|
||||
formLayout->addWidget(new QLabel("IP Address:", this), 1, 0);
|
||||
m_ipCombo = new QComboBox(this);
|
||||
m_ipCombo->setEditable(true);
|
||||
m_ipCombo->setMinimumWidth(180);
|
||||
ipRow->addWidget(m_ipCombo, 1);
|
||||
layout->addLayout(ipRow);
|
||||
formLayout->addWidget(m_ipCombo, 1, 1);
|
||||
|
||||
// Port
|
||||
auto* portRow = new QHBoxLayout();
|
||||
portRow->addWidget(new QLabel("Port:", this));
|
||||
formLayout->addWidget(new QLabel("Port:", this), 2, 0);
|
||||
m_portSpin = new QSpinBox(this);
|
||||
m_portSpin->setRange(1, 65535);
|
||||
m_portSpin->setValue(1333);
|
||||
m_portSpin->setMinimumWidth(100);
|
||||
portRow->addWidget(m_portSpin);
|
||||
portRow->addStretch();
|
||||
layout->addLayout(portRow);
|
||||
formLayout->addWidget(m_portSpin, 2, 1);
|
||||
|
||||
// Protocol
|
||||
auto* protoRow = new QHBoxLayout();
|
||||
protoRow->addWidget(new QLabel("Protocol:", this));
|
||||
formLayout->addWidget(new QLabel("Protocol:", this), 3, 0);
|
||||
m_protoCombo = new QComboBox(this);
|
||||
m_protoCombo->addItems({"UDP", "TCP"});
|
||||
m_protoCombo->setMinimumWidth(100);
|
||||
protoRow->addWidget(m_protoCombo);
|
||||
protoRow->addStretch();
|
||||
layout->addLayout(protoRow);
|
||||
formLayout->addWidget(m_protoCombo, 3, 1);
|
||||
|
||||
formLayout->addWidget(new QLabel("Type:", this), 4, 0);
|
||||
m_typeCombo = new QComboBox(this);
|
||||
m_typeCombo->addItems({"public", "nat", "private", "local"});
|
||||
formLayout->addWidget(m_typeCombo, 4, 1);
|
||||
|
||||
formLayout->addWidget(new QLabel("Interface:", this), 5, 0);
|
||||
m_interfaceCombo = new QComboBox(this);
|
||||
m_interfaceCombo->setMinimumWidth(180);
|
||||
formLayout->addWidget(m_interfaceCombo, 5, 1);
|
||||
|
||||
layout->addLayout(formLayout);
|
||||
|
||||
auto* note = new QLabel("Changes take effect after restart.", this);
|
||||
note->setStyleSheet("color: gray; font-size: 11px;");
|
||||
layout->addWidget(note);
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
populateInterfaces();
|
||||
connect(m_interfaceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &NetworkSettingsPage::onInterfaceChanged);
|
||||
populateAddresses();
|
||||
|
||||
connect(m_autoSocketsCheck, &QCheckBox::toggled, this, &NetworkSettingsPage::onAutoSocketsToggled);
|
||||
connect(m_addBtn, &QPushButton::clicked, this, &NetworkSettingsPage::onAddServer);
|
||||
connect(m_removeBtn, &QPushButton::clicked, this, &NetworkSettingsPage::onRemoveServer);
|
||||
connect(m_serverList, &QListWidget::currentRowChanged, this, [this](int) { onServerSelected(); });
|
||||
|
||||
connect(m_nameEdit, &QLineEdit::textChanged, this, [this](const QString&) { onFieldChanged(); });
|
||||
connect(m_ipCombo, &QComboBox::editTextChanged, this, [this](const QString&) { onFieldChanged(); });
|
||||
connect(m_portSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int) { onFieldChanged(); });
|
||||
connect(m_protoCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); });
|
||||
connect(m_typeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); });
|
||||
connect(m_interfaceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { onFieldChanged(); });
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::loadFromConfig(const QString& configPath) {
|
||||
// Read [server:lan] section from INI file
|
||||
QString addr, netif, transport;
|
||||
QFile f(configPath);
|
||||
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
bool inSection = false;
|
||||
QTextStream in(&f);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
inSection = (line.mid(1, line.length() - 2) == SECTION);
|
||||
continue;
|
||||
}
|
||||
if (!inSection || !line.contains('=')) continue;
|
||||
QString key = line.section('=', 0, 0).trimmed();
|
||||
QString val = line.section('=', 1).trimmed();
|
||||
if (key == "addr") addr = val;
|
||||
else if (key == "netif") netif = val;
|
||||
else if (key == "transport") transport = val;
|
||||
}
|
||||
f.close();
|
||||
static void splitAddr(const QString& addr, QString& ip, int& port) {
|
||||
int lastColon = addr.lastIndexOf(':');
|
||||
if (lastColon > 0) {
|
||||
ip = addr.left(lastColon);
|
||||
port = addr.mid(lastColon + 1).toInt();
|
||||
} else {
|
||||
ip = addr;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
// Parse addr: IP:port
|
||||
QString ip;
|
||||
if (!addr.isEmpty()) {
|
||||
int lastColon = addr.lastIndexOf(':');
|
||||
if (lastColon > 0) {
|
||||
ip = addr.left(lastColon);
|
||||
int port = addr.mid(lastColon + 1).toInt();
|
||||
if (port > 0 && port <= 65535) m_portSpin->setValue(port);
|
||||
}
|
||||
}
|
||||
|
||||
// Set protocol
|
||||
int protoIdx = (transport == "tcp") ? 1 : 0;
|
||||
m_protoCombo->setCurrentIndex(protoIdx);
|
||||
|
||||
// Set interface
|
||||
int ifIdx = m_interfaceCombo->findText(netif);
|
||||
if (ifIdx >= 0) m_interfaceCombo->setCurrentIndex(ifIdx);
|
||||
|
||||
// Set IP — populate addresses first, then select
|
||||
populateAddresses(netif.isEmpty() ? QString() : netif);
|
||||
if (ip.isEmpty() || ip == "0.0.0.0") ip = "Auto (0.0.0.0)";
|
||||
int ipIdx = m_ipCombo->findText(ip);
|
||||
if (ipIdx >= 0)
|
||||
m_ipCombo->setCurrentIndex(ipIdx);
|
||||
else
|
||||
m_ipCombo->setCurrentText(ip);
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::saveToConfig(const QString& configPath) const {
|
||||
// addr
|
||||
QString ip = m_ipCombo->currentText().trimmed();
|
||||
if (ip.isEmpty() || ip == "Auto (0.0.0.0)") ip = "0.0.0.0";
|
||||
config_update_option(configPath, SECTION, "addr",
|
||||
QString("%1:%2").arg(ip).arg(m_portSpin->value()),
|
||||
CONFIG_UPDATE_REPLACE);
|
||||
|
||||
// netif: set or remove (empty value means no netif binding)
|
||||
QString iface = m_interfaceCombo->currentText();
|
||||
if (iface.startsWith("Auto"))
|
||||
iface.clear();
|
||||
config_update_option(configPath, SECTION, "netif", iface, CONFIG_UPDATE_REPLACE);
|
||||
|
||||
// transport
|
||||
config_update_option(configPath, SECTION, "transport",
|
||||
m_protoCombo->currentText().toLower(),
|
||||
CONFIG_UPDATE_REPLACE);
|
||||
|
||||
// IPv6 dual-stack: always add [server:lan6] with same port
|
||||
config_update_option(configPath, "server:lan6", "addr",
|
||||
QString("[::]:%1").arg(m_portSpin->value()),
|
||||
CONFIG_UPDATE_REPLACE);
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onInterfaceChanged(int index) {
|
||||
Q_UNUSED(index);
|
||||
QString iface = m_interfaceCombo->currentText();
|
||||
populateAddresses(iface.startsWith("Auto") ? QString() : iface);
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::populateInterfaces() {
|
||||
@@ -162,30 +119,201 @@ void NetworkSettingsPage::populateInterfaces() {
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::populateAddresses(const QString& ifaceName) {
|
||||
QString current = m_ipCombo->currentText();
|
||||
m_ipCombo->clear();
|
||||
m_ipCombo->addItem("Auto (0.0.0.0)");
|
||||
|
||||
if (ifaceName.isEmpty()) {
|
||||
for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) {
|
||||
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
|
||||
if (!(iface.flags() & QNetworkInterface::IsUp)) continue;
|
||||
for (const QNetworkAddressEntry& entry : iface.addressEntries()) {
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
|
||||
m_ipCombo->addItem(entry.ip().toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
|
||||
if (iface.isValid()) {
|
||||
for (const QNetworkAddressEntry& entry : iface.addressEntries()) {
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
|
||||
m_ipCombo->addItem(entry.ip().toString());
|
||||
}
|
||||
void NetworkSettingsPage::populateAddresses() {
|
||||
m_ipCombo->addItem(AUTO_IP_LABEL);
|
||||
for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) {
|
||||
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
|
||||
if (!(iface.flags() & QNetworkInterface::IsUp)) continue;
|
||||
for (const QNetworkAddressEntry& entry : iface.addressEntries()) {
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
|
||||
m_ipCombo->addItem(entry.ip().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = m_ipCombo->findText(current);
|
||||
if (idx >= 0) m_ipCombo->setCurrentIndex(idx);
|
||||
void NetworkSettingsPage::applyAutoSocketsState() {
|
||||
bool manual = !m_autoSocketsCheck->isChecked();
|
||||
m_serverList->setEnabled(manual);
|
||||
m_addBtn->setEnabled(manual);
|
||||
m_removeBtn->setEnabled(manual);
|
||||
m_nameEdit->setEnabled(manual);
|
||||
m_ipCombo->setEnabled(manual);
|
||||
m_portSpin->setEnabled(manual);
|
||||
m_protoCombo->setEnabled(manual);
|
||||
m_typeCombo->setEnabled(manual);
|
||||
m_interfaceCombo->setEnabled(manual);
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::refreshServerList(int selectRow) {
|
||||
m_updating = true;
|
||||
m_serverList->clear();
|
||||
for (const ConfigServer& s : m_servers) {
|
||||
m_serverList->addItem(QString("%1 %2 (%3)").arg(s.name, s.addr, s.transport));
|
||||
}
|
||||
if (selectRow >= 0 && selectRow < m_serverList->count())
|
||||
m_serverList->setCurrentRow(selectRow);
|
||||
else if (m_serverList->count() > 0)
|
||||
m_serverList->setCurrentRow(0);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::loadFormFromSelected() {
|
||||
int row = m_serverList->currentRow();
|
||||
if (row < 0 || row >= m_servers.size()) return;
|
||||
const ConfigServer& s = m_servers[row];
|
||||
|
||||
m_updating = true;
|
||||
m_nameEdit->setText(s.name);
|
||||
QString ip; int port = 0;
|
||||
splitAddr(s.addr, ip, port);
|
||||
if (ip == "0.0.0.0") ip = AUTO_IP_LABEL;
|
||||
m_ipCombo->setCurrentText(ip);
|
||||
m_portSpin->setValue(port > 0 ? port : 1333);
|
||||
m_protoCombo->setCurrentIndex(s.transport == "tcp" ? 1 : 0);
|
||||
int typeIdx = m_typeCombo->findText(s.type);
|
||||
m_typeCombo->setCurrentIndex(typeIdx >= 0 ? typeIdx : 0);
|
||||
int ifIdx = m_interfaceCombo->findText(s.netif);
|
||||
m_interfaceCombo->setCurrentIndex(ifIdx >= 0 ? ifIdx : 0);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::updateSelectedServerFromForm() {
|
||||
int row = m_serverList->currentRow();
|
||||
if (row < 0 || row >= m_servers.size()) return;
|
||||
|
||||
ConfigServer& s = m_servers[row];
|
||||
s.name = m_nameEdit->text().trimmed();
|
||||
|
||||
QString ip = m_ipCombo->currentText().trimmed();
|
||||
if (ip == AUTO_IP_LABEL) ip = "0.0.0.0";
|
||||
s.addr = ip + ":" + QString::number(m_portSpin->value());
|
||||
|
||||
s.transport = (m_protoCombo->currentIndex() == 1) ? "tcp" : "udp";
|
||||
s.type = m_typeCombo->currentText().trimmed();
|
||||
QString iface = m_interfaceCombo->currentText();
|
||||
s.netif = iface.startsWith("Auto") ? QString() : iface;
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::updateSelectedListItemText() {
|
||||
int row = m_serverList->currentRow();
|
||||
if (row < 0 || row >= m_servers.size()) return;
|
||||
const ConfigServer& s = m_servers[row];
|
||||
m_updating = true;
|
||||
m_serverList->item(row)->setText(QString("%1 %2 (%3)").arg(s.name, s.addr, s.transport));
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::loadFromConfig(const QString& configPath) {
|
||||
m_configPath = configPath;
|
||||
m_servers.clear();
|
||||
|
||||
QFile f(configPath);
|
||||
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString section;
|
||||
ConfigServer cur;
|
||||
bool inServer = false;
|
||||
QTextStream in(&f);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
if (inServer) m_servers.append(cur);
|
||||
inServer = false;
|
||||
section = line.mid(1, line.length() - 2);
|
||||
if (section.startsWith("server:")) {
|
||||
cur = ConfigServer();
|
||||
cur.name = section.mid(7);
|
||||
cur.transport = "udp";
|
||||
cur.type = "public";
|
||||
inServer = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!inServer || !line.contains('=')) continue;
|
||||
QString key = line.section('=', 0, 0).trimmed();
|
||||
QString val = line.section('=', 1).trimmed();
|
||||
if (key == "addr") cur.addr = val;
|
||||
else if (key == "transport") cur.transport = val;
|
||||
else if (key == "type") cur.type = val;
|
||||
else if (key == "netif") cur.netif = val;
|
||||
}
|
||||
if (inServer) m_servers.append(cur);
|
||||
f.close();
|
||||
}
|
||||
|
||||
/* auto_sockets из [global] */
|
||||
bool autoSockets = false;
|
||||
f.setFileName(configPath);
|
||||
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString section;
|
||||
QTextStream in(&f);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
section = line.mid(1, line.length() - 2);
|
||||
continue;
|
||||
}
|
||||
if ((section == "global" || section.isEmpty()) && line.startsWith("auto_sockets=")) {
|
||||
QString v = line.section('=', 1).trimmed();
|
||||
autoSockets = (v.compare("yes", Qt::CaseInsensitive) == 0 || v == "1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
m_autoSocketsCheck->setChecked(autoSockets);
|
||||
|
||||
refreshServerList();
|
||||
applyAutoSocketsState();
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::saveToConfig(const QString& configPath) const {
|
||||
/* 1. Серверные секции — полная перезапись */
|
||||
config_rewrite_servers(configPath, m_servers);
|
||||
|
||||
/* 2. auto_sockets */
|
||||
config_update_option(configPath, "global", "auto_sockets",
|
||||
m_autoSocketsCheck->isChecked() ? "yes" : "no",
|
||||
CONFIG_UPDATE_REPLACE);
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onAutoSocketsToggled(bool /*on*/) {
|
||||
applyAutoSocketsState();
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onAddServer() {
|
||||
int n = m_servers.size() + 1;
|
||||
QString name;
|
||||
for (;;) {
|
||||
name = QString("srv%1").arg(n);
|
||||
bool used = false;
|
||||
for (const ConfigServer& s : m_servers) if (s.name == name) { used = true; break; }
|
||||
if (!used) break;
|
||||
n++;
|
||||
}
|
||||
ConfigServer s;
|
||||
s.name = name;
|
||||
s.addr = "0.0.0.0:1333";
|
||||
s.transport = "udp";
|
||||
s.type = "public";
|
||||
m_servers.append(s);
|
||||
refreshServerList(m_servers.size() - 1);
|
||||
loadFormFromSelected();
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onRemoveServer() {
|
||||
int row = m_serverList->currentRow();
|
||||
if (row < 0 || row >= m_servers.size()) return;
|
||||
m_servers.removeAt(row);
|
||||
refreshServerList(row);
|
||||
loadFormFromSelected();
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onServerSelected() {
|
||||
loadFormFromSelected();
|
||||
}
|
||||
|
||||
void NetworkSettingsPage::onFieldChanged() {
|
||||
if (m_updating) return;
|
||||
updateSelectedServerFromForm();
|
||||
updateSelectedListItemText();
|
||||
}
|
||||
Reference in new issue
Block a user