Skip to content

Commit ca057ff

Browse files
committed
GUI/OptionsDialog: fix proxy field validation and checkbox handling
- Enable validation while typing for proxy fields (proxyIp, proxyPort, proxyIpTor, proxyPortTor) - Fix checkbox signal handling for Qt 6.7+ compatibility - Fix theme change color updates for QValidatedLineEdit fields - Add ProxyAddressValidator with inet_pton for proper IP validation - Ensure proxy fields are properly enabled/disabled based on checkbox state
1 parent 6edafad commit ca057ff

File tree

2 files changed

+128
-18
lines changed

2 files changed

+128
-18
lines changed

src/qt/forms/optionsdialog.ui

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@
473473
</widget>
474474
</item>
475475
<item>
476-
<widget class="QLineEdit" name="proxyPort">
476+
<widget class="QValidatedLineEdit" name="proxyPort">
477477
<property name="minimumSize">
478478
<size>
479479
<width>55</width>
@@ -660,7 +660,7 @@
660660
</widget>
661661
</item>
662662
<item>
663-
<widget class="QLineEdit" name="proxyPortTor">
663+
<widget class="QValidatedLineEdit" name="proxyPortTor">
664664
<property name="minimumSize">
665665
<size>
666666
<width>55</width>

src/qt/optionsdialog.cpp

Lines changed: 126 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@
3030
#include <util/strencodings.h>
3131
#include <chrono>
3232
#include <cmath>
33+
#include <regex>
3334
#include <utility>
3435

36+
#ifdef WIN32
37+
#include <winsock2.h>
38+
#include <ws2tcpip.h>
39+
#else
40+
#include <arpa/inet.h>
41+
#endif
42+
3543
#include <QApplication>
3644
#include <QBoxLayout>
3745
#include <QDataWidgetMapper>
@@ -278,13 +286,33 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
278286
ui->proxyPortTor->setEnabled(false);
279287
ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this));
280288

281-
connect(ui->connectSocks, &QPushButton::toggled, ui->proxyIp, &QWidget::setEnabled);
282-
connect(ui->connectSocks, &QPushButton::toggled, ui->proxyPort, &QWidget::setEnabled);
283-
connect(ui->connectSocks, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
284-
285-
connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyIpTor, &QWidget::setEnabled);
286-
connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled);
287-
connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
289+
#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
290+
connect(ui->connectSocks, &QCheckBox::checkStateChanged, [this](const Qt::CheckState state){
291+
const bool enabled = (state == Qt::Checked);
292+
ui->proxyIp->setEnabled(enabled);
293+
ui->proxyPort->setEnabled(enabled);
294+
updateProxyValidationState();
295+
});
296+
connect(ui->connectSocksTor, &QCheckBox::checkStateChanged, [this](const Qt::CheckState state){
297+
const bool enabled = (state == Qt::Checked);
298+
ui->proxyIpTor->setEnabled(enabled);
299+
ui->proxyPortTor->setEnabled(enabled);
300+
updateProxyValidationState();
301+
});
302+
#else
303+
connect(ui->connectSocks, &QCheckBox::stateChanged, [this](int state){
304+
const bool enabled = (state == Qt::Checked);
305+
ui->proxyIp->setEnabled(enabled);
306+
ui->proxyPort->setEnabled(enabled);
307+
updateProxyValidationState();
308+
});
309+
connect(ui->connectSocksTor, &QCheckBox::stateChanged, [this](int state){
310+
const bool enabled = (state == Qt::Checked);
311+
ui->proxyIpTor->setEnabled(enabled);
312+
ui->proxyPortTor->setEnabled(enabled);
313+
updateProxyValidationState();
314+
});
315+
#endif
288316

289317
ui->maxuploadtarget->setMinimum(144 /* MiB/day */);
290318
ui->maxuploadtarget->setMaximum(std::numeric_limits<int>::max());
@@ -720,10 +748,23 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
720748
/* setup/change UI elements when proxy IPs are invalid/valid */
721749
ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent));
722750
ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent));
751+
752+
// do not allow empty input for validation for all proxy fields
753+
ui->proxyIp->setAllowEmptyInput(false);
754+
ui->proxyIpTor->setAllowEmptyInput(false);
755+
ui->proxyPort->setAllowEmptyInput(false);
756+
ui->proxyPortTor->setAllowEmptyInput(false);
757+
758+
// Enable validation while typing for all proxy fields
759+
ui->proxyIp->setAllowValidationWhileEditing(true);
760+
ui->proxyPort->setAllowValidationWhileEditing(true);
761+
ui->proxyIpTor->setAllowValidationWhileEditing(true);
762+
ui->proxyPortTor->setAllowValidationWhileEditing(true);
763+
723764
connect(ui->proxyIp, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
724765
connect(ui->proxyIpTor, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
725-
connect(ui->proxyPort, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
726-
connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
766+
connect(ui->proxyPort, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
767+
connect(ui->proxyPortTor, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
727768

728769
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
729770
ui->showTrayIcon->setChecked(false);
@@ -1152,6 +1193,16 @@ void OptionsDialog::on_okButton_clicked()
11521193
model->setData(model->index(OptionsModel::dustdynamic, 0), "off");
11531194
}
11541195

1196+
// Before mapper->submit()
1197+
if (!ui->connectSocks->isChecked()) {
1198+
ui->proxyIp->clear();
1199+
ui->proxyPort->clear();
1200+
}
1201+
if (!ui->connectSocksTor->isChecked()) {
1202+
ui->proxyIpTor->clear();
1203+
ui->proxyPortTor->clear();
1204+
}
1205+
11551206
mapper->submit();
11561207
accept();
11571208
updateDefaultProxyNets();
@@ -1174,11 +1225,13 @@ void OptionsDialog::on_showTrayIcon_stateChanged(int state)
11741225

11751226
void OptionsDialog::changeEvent(QEvent* e)
11761227
{
1228+
// First let the base class update all child widget palettes
1229+
// required for qvalidatedlineedit invalid colors to update properly
1230+
QWidget::changeEvent(e);
11771231
if (e->type() == QEvent::PaletteChange) {
1232+
// Then update theme colors with the new palette
11781233
updateThemeColors();
11791234
}
1180-
1181-
QWidget::changeEvent(e);
11821235
}
11831236

11841237
void OptionsDialog::togglePruneWarning(bool enabled)
@@ -1211,17 +1264,47 @@ void OptionsDialog::clearStatusLabel()
12111264

12121265
void OptionsDialog::updateProxyValidationState()
12131266
{
1214-
QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
1215-
QValidatedLineEdit *otherProxyWidget = (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
1216-
if (pUiProxyIp->isValid() && (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) && (!ui->proxyPortTor->isEnabled() || ui->proxyPortTor->text().toInt() > 0))
1267+
bool socksProxyEnabled = ui->connectSocks->isChecked();
1268+
bool torProxyEnabled = ui->connectSocksTor->isChecked();
1269+
1270+
bool proxyIpValid = ui->proxyIp->isValid();
1271+
bool proxyPortValid = ui->proxyPort->isValid();
1272+
bool proxyIpTorValid = ui->proxyIpTor->isValid();
1273+
bool proxyPortTorValid = ui->proxyPortTor->isValid();
1274+
1275+
// proxy is OK if: disabled OR (enabled AND valid ip and valid port)
1276+
bool socksProxyOk = !socksProxyEnabled || (proxyIpValid && proxyPortValid);
1277+
bool torProxyOk = !torProxyEnabled || (proxyIpTorValid && proxyPortTorValid);
1278+
1279+
// Clear visual feedback when proxies are disabled
1280+
if (!socksProxyEnabled) { ui->proxyIp->setStyleSheet(""); ui->proxyPort->setStyleSheet(""); }
1281+
if (!torProxyEnabled) { ui->proxyIpTor->setStyleSheet(""); ui->proxyPortTor->setStyleSheet(""); }
1282+
1283+
// Both must be OK for the form to be valid
1284+
if (socksProxyOk && torProxyOk)
12171285
{
1218-
setOkButtonState(otherProxyWidget->isValid()); //only enable ok button if both proxies are valid
1286+
setOkButtonState(true);
12191287
clearStatusLabel();
12201288
}
12211289
else
12221290
{
12231291
setOkButtonState(false);
1224-
ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
1292+
QStringList socksErrors;
1293+
1294+
if (socksProxyEnabled) {
1295+
if (!proxyIpValid || !proxyPortValid) {
1296+
socksErrors.append(tr("The supplied proxy address and port is invalid."));
1297+
}
1298+
}
1299+
if (torProxyEnabled) {
1300+
if (!proxyIpTorValid || !proxyPortTorValid) {
1301+
socksErrors.append(tr("The supplied Tor proxy address and port is invalid."));
1302+
}
1303+
}
1304+
1305+
if (socksErrors.size() > 0) {
1306+
ui->statusLabel->setText(socksErrors.join(" "));
1307+
}
12251308
}
12261309
}
12271310

@@ -1264,6 +1347,13 @@ void OptionsDialog::updateThemeColors()
12641347
const QColor networkport_warning = networkport_dark ? QColor("#FF8080") : QColor("#FF0000");
12651348
ui->networkPort->setStyleSheet(QStringLiteral("color: %1;").arg(networkport_warning.name()));
12661349
}
1350+
// Re-trigger validation on all qvalidatedlineedit input fields to update their styling
1351+
// including background and text color
1352+
// Use setText to trigger validation
1353+
if (!ui->proxyIp->isValid()) ui->proxyIp->setText(ui->proxyIp->text());
1354+
if (!ui->proxyPort->isValid()) ui->proxyPort->setText(ui->proxyPort->text());
1355+
if (!ui->proxyIpTor->isValid()) ui->proxyIpTor->setText(ui->proxyIpTor->text());
1356+
if (!ui->proxyPortTor->isValid()) ui->proxyPortTor->setText(ui->proxyPortTor->text());
12671357
}
12681358

12691359
ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
@@ -1278,6 +1368,26 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
12781368
std::string hostname;
12791369
if (!SplitHostPort(input.toStdString(), port, hostname) || port != 0) return QValidator::Invalid;
12801370

1371+
// Check if it's a valid IPv4 address
1372+
struct sockaddr_in sa4;
1373+
if (inet_pton(AF_INET, hostname.c_str(), &sa4.sin_addr) == 1) {
1374+
return QValidator::Acceptable;
1375+
}
1376+
1377+
// Check if it's a valid IPv6 address
1378+
struct sockaddr_in6 sa6;
1379+
if (inet_pton(AF_INET6, hostname.c_str(), &sa6.sin6_addr) == 1) {
1380+
return QValidator::Acceptable;
1381+
}
1382+
1383+
// Reject partial IPv4 addresses (digits/dots but not a complete IPv4)
1384+
// This catches "127.0", "192.168", "10", etc.
1385+
std::regex partialIPv4("^[0-9]+(\\.[0-9]+){0,2}$");
1386+
if (std::regex_match(hostname, partialIPv4)) {
1387+
return QValidator::Invalid;
1388+
}
1389+
1390+
// Not a valid IP address, could be a hostname
12811391
CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
12821392
Proxy addrProxy = Proxy(serv, true);
12831393
if (addrProxy.IsValid())

0 commit comments

Comments
 (0)