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
11751226void 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
11841237void OptionsDialog::togglePruneWarning (bool enabled)
@@ -1211,17 +1264,47 @@ void OptionsDialog::clearStatusLabel()
12111264
12121265void 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
12691359ProxyAddressValidator::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