30
30
#include < util/strencodings.h>
31
31
#include < chrono>
32
32
#include < cmath>
33
+ #include < regex>
33
34
#include < utility>
34
35
36
+ #ifdef WIN32
37
+ #include < winsock2.h>
38
+ #include < ws2tcpip.h>
39
+ #else
40
+ #include < arpa/inet.h>
41
+ #endif
42
+
35
43
#include < QApplication>
36
44
#include < QBoxLayout>
37
45
#include < QDataWidgetMapper>
@@ -278,13 +286,33 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
278
286
ui->proxyPortTor ->setEnabled (false );
279
287
ui->proxyPortTor ->setValidator (new QIntValidator (1 , 65535 , this ));
280
288
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
288
316
289
317
ui->maxuploadtarget ->setMinimum (144 /* MiB/day */ );
290
318
ui->maxuploadtarget ->setMaximum (std::numeric_limits<int >::max ());
@@ -720,10 +748,23 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
720
748
/* setup/change UI elements when proxy IPs are invalid/valid */
721
749
ui->proxyIp ->setCheckValidator (new ProxyAddressValidator (parent));
722
750
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
+
723
764
connect (ui->proxyIp , &QValidatedLineEdit::validationDidChange, this , &OptionsDialog::updateProxyValidationState);
724
765
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);
727
768
728
769
if (!QSystemTrayIcon::isSystemTrayAvailable ()) {
729
770
ui->showTrayIcon ->setChecked (false );
@@ -1152,6 +1193,16 @@ void OptionsDialog::on_okButton_clicked()
1152
1193
model->setData (model->index (OptionsModel::dustdynamic, 0 ), " off" );
1153
1194
}
1154
1195
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
+
1155
1206
mapper->submit ();
1156
1207
accept ();
1157
1208
updateDefaultProxyNets ();
@@ -1174,11 +1225,13 @@ void OptionsDialog::on_showTrayIcon_stateChanged(int state)
1174
1225
1175
1226
void OptionsDialog::changeEvent (QEvent* e)
1176
1227
{
1228
+ // First let the base class update all child widget palettes
1229
+ // required for qvalidatedlineedit invalid colors to update properly
1230
+ QWidget::changeEvent (e);
1177
1231
if (e->type () == QEvent::PaletteChange) {
1232
+ // Then update theme colors with the new palette
1178
1233
updateThemeColors ();
1179
1234
}
1180
-
1181
- QWidget::changeEvent (e);
1182
1235
}
1183
1236
1184
1237
void OptionsDialog::togglePruneWarning (bool enabled)
@@ -1211,17 +1264,47 @@ void OptionsDialog::clearStatusLabel()
1211
1264
1212
1265
void OptionsDialog::updateProxyValidationState ()
1213
1266
{
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)
1217
1285
{
1218
- setOkButtonState (otherProxyWidget-> isValid ()); // only enable ok button if both proxies are valid
1286
+ setOkButtonState (true );
1219
1287
clearStatusLabel ();
1220
1288
}
1221
1289
else
1222
1290
{
1223
1291
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
+ }
1225
1308
}
1226
1309
}
1227
1310
@@ -1264,6 +1347,13 @@ void OptionsDialog::updateThemeColors()
1264
1347
const QColor networkport_warning = networkport_dark ? QColor (" #FF8080" ) : QColor (" #FF0000" );
1265
1348
ui->networkPort ->setStyleSheet (QStringLiteral (" color: %1;" ).arg (networkport_warning.name ()));
1266
1349
}
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 ());
1267
1357
}
1268
1358
1269
1359
ProxyAddressValidator::ProxyAddressValidator (QObject *parent) :
@@ -1278,6 +1368,26 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
1278
1368
std::string hostname;
1279
1369
if (!SplitHostPort (input.toStdString (), port, hostname) || port != 0 ) return QValidator::Invalid;
1280
1370
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
1281
1391
CService serv (LookupNumeric (input.toStdString (), DEFAULT_GUI_PROXY_PORT));
1282
1392
Proxy addrProxy = Proxy (serv, true );
1283
1393
if (addrProxy.IsValid ())
0 commit comments