324 lines
12 KiB
C++
324 lines
12 KiB
C++
#include <QCoreApplication>
|
|
// 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 QString autoIpLabel() { return QCoreApplication::translate("ChatGui", "Auto (0.0.0.0)"); }
|
|
|
|
NetworkSettingsPage::NetworkSettingsPage(QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
auto* layout = new QVBoxLayout(this);
|
|
layout->setSpacing(12);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
auto* title = new QLabel(QCoreApplication::translate("ChatGui", "Listen Sockets"), this);
|
|
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
|
layout->addWidget(title);
|
|
|
|
m_autoSocketsCheck = new QCheckBox(QCoreApplication::translate("ChatGui", "Auto sockets (UDP+TCP from interfaces)"), this);
|
|
layout->addWidget(m_autoSocketsCheck);
|
|
|
|
m_serverList = new QListWidget(this);
|
|
m_serverList->setMinimumHeight(110);
|
|
layout->addWidget(m_serverList);
|
|
|
|
auto* btnRow = new QHBoxLayout();
|
|
m_addBtn = new QPushButton(QCoreApplication::translate("ChatGui", "Add"), this);
|
|
m_removeBtn = new QPushButton(QCoreApplication::translate("ChatGui", "Remove"), this);
|
|
btnRow->addWidget(m_addBtn);
|
|
btnRow->addWidget(m_removeBtn);
|
|
btnRow->addStretch();
|
|
layout->addLayout(btnRow);
|
|
|
|
// --- форма правки выбранного сокета ---
|
|
auto* formLayout = new QGridLayout();
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "Name:"), this), 0, 0);
|
|
m_nameEdit = new QLineEdit(this);
|
|
m_nameEdit->setMinimumWidth(180);
|
|
formLayout->addWidget(m_nameEdit, 0, 1);
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "IP Address:"), this), 1, 0);
|
|
m_ipCombo = new QComboBox(this);
|
|
m_ipCombo->setEditable(true);
|
|
m_ipCombo->setMinimumWidth(180);
|
|
formLayout->addWidget(m_ipCombo, 1, 1);
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "Port:"), this), 2, 0);
|
|
m_portSpin = new QSpinBox(this);
|
|
m_portSpin->setRange(1, 65535);
|
|
m_portSpin->setValue(1333);
|
|
formLayout->addWidget(m_portSpin, 2, 1);
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "Protocol:"), this), 3, 0);
|
|
m_protoCombo = new QComboBox(this);
|
|
m_protoCombo->addItems({"UDP", "TCP"});
|
|
formLayout->addWidget(m_protoCombo, 3, 1);
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "Type:"), this), 4, 0);
|
|
m_typeCombo = new QComboBox(this);
|
|
m_typeCombo->addItem(QCoreApplication::translate("ChatGui", "Public"), QStringLiteral("public"));
|
|
m_typeCombo->addItem(QStringLiteral("NAT"), QStringLiteral("nat"));
|
|
m_typeCombo->addItem(QCoreApplication::translate("ChatGui", "Private"), QStringLiteral("private"));
|
|
m_typeCombo->addItem(QCoreApplication::translate("ChatGui", "Local"), QStringLiteral("local"));
|
|
formLayout->addWidget(m_typeCombo, 4, 1);
|
|
|
|
formLayout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "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(QCoreApplication::translate("ChatGui", "Changes take effect after restart."), this);
|
|
note->setStyleSheet("color: gray; font-size: 11px;");
|
|
layout->addWidget(note);
|
|
layout->addStretch();
|
|
|
|
populateInterfaces();
|
|
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(); });
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
void NetworkSettingsPage::populateInterfaces() {
|
|
m_interfaceCombo->addItem(QCoreApplication::translate("ChatGui", "Auto (any)"));
|
|
for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) {
|
|
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
|
|
if (!(iface.flags() & QNetworkInterface::IsUp)) continue;
|
|
m_interfaceCombo->addItem(iface.name());
|
|
}
|
|
}
|
|
|
|
void NetworkSettingsPage::populateAddresses() {
|
|
m_ipCombo->addItem(autoIpLabel());
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = autoIpLabel();
|
|
m_ipCombo->setCurrentText(ip);
|
|
m_portSpin->setValue(port > 0 ? port : 1333);
|
|
m_protoCombo->setCurrentIndex(s.transport == "tcp" ? 1 : 0);
|
|
int typeIdx = m_typeCombo->findData(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 == autoIpLabel()) 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->currentData().toString();
|
|
QString iface = m_interfaceCombo->currentText();
|
|
s.netif = m_interfaceCombo->currentIndex() == 0 ? 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();
|
|
}
|