mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-28 21:34:25 +01:00
This fixes a crash bug when opening the options dialog. - Check the return value of split() to avoid segmentation faults due to out of bounds when the user manages to enter invalid proxy settings. This is reported resonably often. - Move the default proxy/port to a constant instead of hardcoding magic values. - Factor out some common code. - Revert #11448 because this proves a more robust replacement, it is no longer necessary and didn't generally solve the issue. No attempt is made to do full sanity checking on the proxy, so it can still be rejected by the core with an InitError message.
347 lines
13 KiB
C++
347 lines
13 KiB
C++
// Copyright (c) 2011-2016 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config/bitcoin-config.h>
|
|
#endif
|
|
|
|
#include <qt/optionsdialog.h>
|
|
#include <qt/forms/ui_optionsdialog.h>
|
|
|
|
#include <qt/bitcoinunits.h>
|
|
#include <qt/guiutil.h>
|
|
#include <qt/optionsmodel.h>
|
|
|
|
#include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
|
|
#include <netbase.h>
|
|
#include <txdb.h> // for -dbcache defaults
|
|
|
|
#include <QDataWidgetMapper>
|
|
#include <QDir>
|
|
#include <QIntValidator>
|
|
#include <QLocale>
|
|
#include <QMessageBox>
|
|
#include <QTimer>
|
|
|
|
OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
|
|
QDialog(parent),
|
|
ui(new Ui::OptionsDialog),
|
|
model(0),
|
|
mapper(0)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
/* Main elements init */
|
|
ui->databaseCache->setMinimum(nMinDbCache);
|
|
ui->databaseCache->setMaximum(nMaxDbCache);
|
|
ui->threadsScriptVerif->setMinimum(-GetNumCores());
|
|
ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS);
|
|
|
|
/* Network elements init */
|
|
#ifndef USE_UPNP
|
|
ui->mapPortUpnp->setEnabled(false);
|
|
#endif
|
|
|
|
ui->proxyIp->setEnabled(false);
|
|
ui->proxyPort->setEnabled(false);
|
|
ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
|
|
|
|
ui->proxyIpTor->setEnabled(false);
|
|
ui->proxyPortTor->setEnabled(false);
|
|
ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this));
|
|
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), this, SLOT(updateProxyValidationState()));
|
|
|
|
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyIpTor, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyPortTor, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), this, SLOT(updateProxyValidationState()));
|
|
|
|
/* Window elements init */
|
|
#ifdef Q_OS_MAC
|
|
/* remove Window tab on Mac */
|
|
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
|
|
#endif
|
|
|
|
/* remove Wallet tab in case of -disablewallet */
|
|
if (!enableWallet) {
|
|
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet));
|
|
}
|
|
|
|
/* Display elements init */
|
|
QDir translations(":translations");
|
|
|
|
ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME)));
|
|
ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME)));
|
|
|
|
ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME)));
|
|
|
|
ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME)));
|
|
ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant(""));
|
|
for (const QString &langStr : translations.entryList())
|
|
{
|
|
QLocale locale(langStr);
|
|
|
|
/** check if the locale name consists of 2 parts (language_country) */
|
|
if(langStr.contains("_"))
|
|
{
|
|
#if QT_VERSION >= 0x040800
|
|
/** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */
|
|
ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#else
|
|
/** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */
|
|
ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if QT_VERSION >= 0x040800
|
|
/** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */
|
|
ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#else
|
|
/** display language strings as "language (locale name)", e.g. "German (de)" */
|
|
ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#endif
|
|
}
|
|
}
|
|
#if QT_VERSION >= 0x040700
|
|
ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s");
|
|
#endif
|
|
|
|
ui->unit->setModel(new BitcoinUnits(this));
|
|
|
|
/* Widget-to-option mapper */
|
|
mapper = new QDataWidgetMapper(this);
|
|
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
|
|
mapper->setOrientation(Qt::Vertical);
|
|
|
|
/* setup/change UI elements when proxy IPs are invalid/valid */
|
|
ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent));
|
|
ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent));
|
|
connect(ui->proxyIp, SIGNAL(validationDidChange(QValidatedLineEdit *)), this, SLOT(updateProxyValidationState()));
|
|
connect(ui->proxyIpTor, SIGNAL(validationDidChange(QValidatedLineEdit *)), this, SLOT(updateProxyValidationState()));
|
|
connect(ui->proxyPort, SIGNAL(textChanged(const QString&)), this, SLOT(updateProxyValidationState()));
|
|
connect(ui->proxyPortTor, SIGNAL(textChanged(const QString&)), this, SLOT(updateProxyValidationState()));
|
|
}
|
|
|
|
OptionsDialog::~OptionsDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void OptionsDialog::setModel(OptionsModel *_model)
|
|
{
|
|
this->model = _model;
|
|
|
|
if(_model)
|
|
{
|
|
/* check if client restart is needed and show persistent message */
|
|
if (_model->isRestartRequired())
|
|
showRestartWarning(true);
|
|
|
|
QString strLabel = _model->getOverriddenByCommandLine();
|
|
if (strLabel.isEmpty())
|
|
strLabel = tr("none");
|
|
ui->overriddenByCommandLineLabel->setText(strLabel);
|
|
|
|
mapper->setModel(_model);
|
|
setMapper();
|
|
mapper->toFirst();
|
|
|
|
updateDefaultProxyNets();
|
|
}
|
|
|
|
/* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
|
|
|
|
/* Main */
|
|
connect(ui->databaseCache, SIGNAL(valueChanged(int)), this, SLOT(showRestartWarning()));
|
|
connect(ui->threadsScriptVerif, SIGNAL(valueChanged(int)), this, SLOT(showRestartWarning()));
|
|
/* Wallet */
|
|
connect(ui->spendZeroConfChange, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
|
|
/* Network */
|
|
connect(ui->allowIncoming, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
|
|
connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
|
|
connect(ui->connectSocksTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
|
|
/* Display */
|
|
connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning()));
|
|
connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning()));
|
|
}
|
|
|
|
void OptionsDialog::setMapper()
|
|
{
|
|
/* Main */
|
|
mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup);
|
|
mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif);
|
|
mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache);
|
|
|
|
/* Wallet */
|
|
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
|
|
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
|
|
|
|
/* Network */
|
|
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
|
|
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
|
|
|
|
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
|
|
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
|
|
mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
|
|
|
|
mapper->addMapping(ui->connectSocksTor, OptionsModel::ProxyUseTor);
|
|
mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor);
|
|
mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor);
|
|
|
|
/* Window */
|
|
#ifndef Q_OS_MAC
|
|
mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
|
|
mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
|
|
mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
|
|
#endif
|
|
|
|
/* Display */
|
|
mapper->addMapping(ui->lang, OptionsModel::Language);
|
|
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
|
|
mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
|
|
}
|
|
|
|
void OptionsDialog::setOkButtonState(bool fState)
|
|
{
|
|
ui->okButton->setEnabled(fState);
|
|
}
|
|
|
|
void OptionsDialog::on_resetButton_clicked()
|
|
{
|
|
if(model)
|
|
{
|
|
// confirmation dialog
|
|
QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm options reset"),
|
|
tr("Client restart required to activate changes.") + "<br><br>" + tr("Client will be shut down. Do you want to proceed?"),
|
|
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
|
|
|
|
if(btnRetVal == QMessageBox::Cancel)
|
|
return;
|
|
|
|
/* reset all options and close GUI */
|
|
model->Reset();
|
|
QApplication::quit();
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::on_openBitcoinConfButton_clicked()
|
|
{
|
|
/* explain the purpose of the config file */
|
|
QMessageBox::information(this, tr("Configuration options"),
|
|
tr("The configuration file is used to specify advanced user options which override GUI settings. "
|
|
"Additionally, any command-line options will override this configuration file."));
|
|
|
|
/* show an error if there was some problem opening the file */
|
|
if (!GUIUtil::openBitcoinConf())
|
|
QMessageBox::critical(this, tr("Error"), tr("The configuration file could not be opened."));
|
|
}
|
|
|
|
void OptionsDialog::on_okButton_clicked()
|
|
{
|
|
mapper->submit();
|
|
accept();
|
|
updateDefaultProxyNets();
|
|
}
|
|
|
|
void OptionsDialog::on_cancelButton_clicked()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
void OptionsDialog::on_hideTrayIcon_stateChanged(int fState)
|
|
{
|
|
if(fState)
|
|
{
|
|
ui->minimizeToTray->setChecked(false);
|
|
ui->minimizeToTray->setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
ui->minimizeToTray->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::showRestartWarning(bool fPersistent)
|
|
{
|
|
ui->statusLabel->setStyleSheet("QLabel { color: red; }");
|
|
|
|
if(fPersistent)
|
|
{
|
|
ui->statusLabel->setText(tr("Client restart required to activate changes."));
|
|
}
|
|
else
|
|
{
|
|
ui->statusLabel->setText(tr("This change would require a client restart."));
|
|
// clear non-persistent status label after 10 seconds
|
|
// Todo: should perhaps be a class attribute, if we extend the use of statusLabel
|
|
QTimer::singleShot(10000, this, SLOT(clearStatusLabel()));
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::clearStatusLabel()
|
|
{
|
|
ui->statusLabel->clear();
|
|
if (model && model->isRestartRequired()) {
|
|
showRestartWarning(true);
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::updateProxyValidationState()
|
|
{
|
|
QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
|
|
QValidatedLineEdit *otherProxyWidget = (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
|
|
if (pUiProxyIp->isValid() && (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) && (!ui->proxyPortTor->isEnabled() || ui->proxyPortTor->text().toInt() > 0))
|
|
{
|
|
setOkButtonState(otherProxyWidget->isValid()); //only enable ok button if both proxys are valid
|
|
clearStatusLabel();
|
|
}
|
|
else
|
|
{
|
|
setOkButtonState(false);
|
|
ui->statusLabel->setStyleSheet("QLabel { color: red; }");
|
|
ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::updateDefaultProxyNets()
|
|
{
|
|
proxyType proxy;
|
|
std::string strProxy;
|
|
QString strDefaultProxyGUI;
|
|
|
|
GetProxy(NET_IPV4, proxy);
|
|
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
|
|
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
|
|
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false);
|
|
|
|
GetProxy(NET_IPV6, proxy);
|
|
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
|
|
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
|
|
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false);
|
|
|
|
GetProxy(NET_TOR, proxy);
|
|
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
|
|
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
|
|
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
|
|
}
|
|
|
|
ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
|
|
QValidator(parent)
|
|
{
|
|
}
|
|
|
|
QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) const
|
|
{
|
|
Q_UNUSED(pos);
|
|
// Validate the proxy
|
|
CService serv(LookupNumeric(input.toStdString().c_str(), DEFAULT_GUI_PROXY_PORT));
|
|
proxyType addrProxy = proxyType(serv, true);
|
|
if (addrProxy.IsValid())
|
|
return QValidator::Acceptable;
|
|
|
|
return QValidator::Invalid;
|
|
}
|