You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

optionsmodel.cpp 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. // Copyright (c) 2011-2017 The Bitcoin Core developers
  2. // Distributed under the MIT software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #if defined(HAVE_CONFIG_H)
  5. #include <config/bitcoin-config.h>
  6. #endif
  7. #include <qt/optionsmodel.h>
  8. #include <qt/bitcoinunits.h>
  9. #include <qt/guiutil.h>
  10. #include <init.h>
  11. #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
  12. #include <net.h>
  13. #include <netbase.h>
  14. #include <txdb.h> // for -dbcache defaults
  15. #include <qt/intro.h>
  16. #ifdef ENABLE_WALLET
  17. #include <wallet/wallet.h>
  18. #include <wallet/walletdb.h>
  19. #endif
  20. #include <QNetworkProxy>
  21. #include <QSettings>
  22. #include <QStringList>
  23. const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
  24. static const QString GetDefaultProxyAddress();
  25. OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
  26. QAbstractListModel(parent)
  27. {
  28. Init(resetSettings);
  29. }
  30. void OptionsModel::addOverriddenOption(const std::string &option)
  31. {
  32. strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " ";
  33. }
  34. // Writes all missing QSettings with their default values
  35. void OptionsModel::Init(bool resetSettings)
  36. {
  37. if (resetSettings)
  38. Reset();
  39. checkAndMigrate();
  40. QSettings settings;
  41. // Ensure restart flag is unset on client startup
  42. setRestartRequired(false);
  43. // These are Qt-only settings:
  44. // Window
  45. if (!settings.contains("fHideTrayIcon"))
  46. settings.setValue("fHideTrayIcon", false);
  47. fHideTrayIcon = settings.value("fHideTrayIcon").toBool();
  48. Q_EMIT hideTrayIconChanged(fHideTrayIcon);
  49. if (!settings.contains("fMinimizeToTray"))
  50. settings.setValue("fMinimizeToTray", false);
  51. fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon;
  52. if (!settings.contains("fMinimizeOnClose"))
  53. settings.setValue("fMinimizeOnClose", false);
  54. fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
  55. // Display
  56. if (!settings.contains("nDisplayUnit"))
  57. settings.setValue("nDisplayUnit", BitcoinUnits::BTC);
  58. nDisplayUnit = settings.value("nDisplayUnit").toInt();
  59. if (!settings.contains("strThirdPartyTxUrls"))
  60. settings.setValue("strThirdPartyTxUrls", "");
  61. strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
  62. if (!settings.contains("fCoinControlFeatures"))
  63. settings.setValue("fCoinControlFeatures", false);
  64. fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
  65. // These are shared with the core or have a command-line parameter
  66. // and we want command-line parameters to overwrite the GUI settings.
  67. //
  68. // If setting doesn't exist create it with defaults.
  69. //
  70. // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were overridden
  71. // by command-line and show this in the UI.
  72. // Main
  73. if (!settings.contains("nDatabaseCache"))
  74. settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
  75. if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
  76. addOverriddenOption("-dbcache");
  77. if (!settings.contains("nThreadsScriptVerif"))
  78. settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
  79. if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
  80. addOverriddenOption("-par");
  81. if (!settings.contains("strDataDir"))
  82. settings.setValue("strDataDir", Intro::getDefaultDataDirectory());
  83. // Wallet
  84. #ifdef ENABLE_WALLET
  85. if (!settings.contains("bSpendZeroConfChange"))
  86. settings.setValue("bSpendZeroConfChange", true);
  87. if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
  88. addOverriddenOption("-spendzeroconfchange");
  89. #endif
  90. // Network
  91. if (!settings.contains("fUseUPnP"))
  92. settings.setValue("fUseUPnP", DEFAULT_UPNP);
  93. if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
  94. addOverriddenOption("-upnp");
  95. if (!settings.contains("fListen"))
  96. settings.setValue("fListen", DEFAULT_LISTEN);
  97. if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
  98. addOverriddenOption("-listen");
  99. if (!settings.contains("fUseProxy"))
  100. settings.setValue("fUseProxy", false);
  101. if (!settings.contains("addrProxy"))
  102. settings.setValue("addrProxy", GetDefaultProxyAddress());
  103. // Only try to set -proxy, if user has enabled fUseProxy
  104. if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
  105. addOverriddenOption("-proxy");
  106. else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
  107. addOverriddenOption("-proxy");
  108. if (!settings.contains("fUseSeparateProxyTor"))
  109. settings.setValue("fUseSeparateProxyTor", false);
  110. if (!settings.contains("addrSeparateProxyTor"))
  111. settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
  112. // Only try to set -onion, if user has enabled fUseSeparateProxyTor
  113. if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
  114. addOverriddenOption("-onion");
  115. else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
  116. addOverriddenOption("-onion");
  117. // Display
  118. if (!settings.contains("language"))
  119. settings.setValue("language", "");
  120. if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
  121. addOverriddenOption("-lang");
  122. language = settings.value("language").toString();
  123. }
  124. /** Helper function to copy contents from one QSettings to another.
  125. * By using allKeys this also covers nested settings in a hierarchy.
  126. */
  127. static void CopySettings(QSettings& dst, const QSettings& src)
  128. {
  129. for (const QString& key : src.allKeys()) {
  130. dst.setValue(key, src.value(key));
  131. }
  132. }
  133. /** Back up a QSettings to an ini-formatted file. */
  134. static void BackupSettings(const fs::path& filename, const QSettings& src)
  135. {
  136. qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
  137. QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
  138. dst.clear();
  139. CopySettings(dst, src);
  140. }
  141. void OptionsModel::Reset()
  142. {
  143. QSettings settings;
  144. // Backup old settings to chain-specific datadir for troubleshooting
  145. BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
  146. // Save the strDataDir setting
  147. QString dataDir = Intro::getDefaultDataDirectory();
  148. dataDir = settings.value("strDataDir", dataDir).toString();
  149. // Remove all entries from our QSettings object
  150. settings.clear();
  151. // Set strDataDir
  152. settings.setValue("strDataDir", dataDir);
  153. // Set that this was reset
  154. settings.setValue("fReset", true);
  155. // default setting for OptionsModel::StartAtStartup - disabled
  156. if (GUIUtil::GetStartOnSystemStartup())
  157. GUIUtil::SetStartOnSystemStartup(false);
  158. }
  159. int OptionsModel::rowCount(const QModelIndex & parent) const
  160. {
  161. return OptionIDRowCount;
  162. }
  163. struct ProxySetting {
  164. bool is_set;
  165. QString ip;
  166. QString port;
  167. };
  168. static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
  169. {
  170. static const ProxySetting default_val = {false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
  171. // Handle the case that the setting is not set at all
  172. if (!settings.contains(name)) {
  173. return default_val;
  174. }
  175. // contains IP at index 0 and port at index 1
  176. QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts);
  177. if (ip_port.size() == 2) {
  178. return {true, ip_port.at(0), ip_port.at(1)};
  179. } else { // Invalid: return default
  180. return default_val;
  181. }
  182. }
  183. static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
  184. {
  185. settings.setValue(name, ip_port.ip + ":" + ip_port.port);
  186. }
  187. static const QString GetDefaultProxyAddress()
  188. {
  189. return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
  190. }
  191. // read QSettings values and return them
  192. QVariant OptionsModel::data(const QModelIndex & index, int role) const
  193. {
  194. if(role == Qt::EditRole)
  195. {
  196. QSettings settings;
  197. switch(index.row())
  198. {
  199. case StartAtStartup:
  200. return GUIUtil::GetStartOnSystemStartup();
  201. case HideTrayIcon:
  202. return fHideTrayIcon;
  203. case MinimizeToTray:
  204. return fMinimizeToTray;
  205. case MapPortUPnP:
  206. #ifdef USE_UPNP
  207. return settings.value("fUseUPnP");
  208. #else
  209. return false;
  210. #endif
  211. case MinimizeOnClose:
  212. return fMinimizeOnClose;
  213. // default proxy
  214. case ProxyUse:
  215. return settings.value("fUseProxy", false);
  216. case ProxyIP:
  217. return GetProxySetting(settings, "addrProxy").ip;
  218. case ProxyPort:
  219. return GetProxySetting(settings, "addrProxy").port;
  220. // separate Tor proxy
  221. case ProxyUseTor:
  222. return settings.value("fUseSeparateProxyTor", false);
  223. case ProxyIPTor:
  224. return GetProxySetting(settings, "addrSeparateProxyTor").ip;
  225. case ProxyPortTor:
  226. return GetProxySetting(settings, "addrSeparateProxyTor").port;
  227. #ifdef ENABLE_WALLET
  228. case SpendZeroConfChange:
  229. return settings.value("bSpendZeroConfChange");
  230. #endif
  231. case DisplayUnit:
  232. return nDisplayUnit;
  233. case ThirdPartyTxUrls:
  234. return strThirdPartyTxUrls;
  235. case Language:
  236. return settings.value("language");
  237. case CoinControlFeatures:
  238. return fCoinControlFeatures;
  239. case DatabaseCache:
  240. return settings.value("nDatabaseCache");
  241. case ThreadsScriptVerif:
  242. return settings.value("nThreadsScriptVerif");
  243. case Listen:
  244. return settings.value("fListen");
  245. default:
  246. return QVariant();
  247. }
  248. }
  249. return QVariant();
  250. }
  251. // write QSettings values
  252. bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
  253. {
  254. bool successful = true; /* set to false on parse error */
  255. if(role == Qt::EditRole)
  256. {
  257. QSettings settings;
  258. switch(index.row())
  259. {
  260. case StartAtStartup:
  261. successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
  262. break;
  263. case HideTrayIcon:
  264. fHideTrayIcon = value.toBool();
  265. settings.setValue("fHideTrayIcon", fHideTrayIcon);
  266. Q_EMIT hideTrayIconChanged(fHideTrayIcon);
  267. break;
  268. case MinimizeToTray:
  269. fMinimizeToTray = value.toBool();
  270. settings.setValue("fMinimizeToTray", fMinimizeToTray);
  271. break;
  272. case MapPortUPnP: // core option - can be changed on-the-fly
  273. settings.setValue("fUseUPnP", value.toBool());
  274. MapPort(value.toBool());
  275. break;
  276. case MinimizeOnClose:
  277. fMinimizeOnClose = value.toBool();
  278. settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
  279. break;
  280. // default proxy
  281. case ProxyUse:
  282. if (settings.value("fUseProxy") != value) {
  283. settings.setValue("fUseProxy", value.toBool());
  284. setRestartRequired(true);
  285. }
  286. break;
  287. case ProxyIP: {
  288. auto ip_port = GetProxySetting(settings, "addrProxy");
  289. if (!ip_port.is_set || ip_port.ip != value.toString()) {
  290. ip_port.ip = value.toString();
  291. SetProxySetting(settings, "addrProxy", ip_port);
  292. setRestartRequired(true);
  293. }
  294. }
  295. break;
  296. case ProxyPort: {
  297. auto ip_port = GetProxySetting(settings, "addrProxy");
  298. if (!ip_port.is_set || ip_port.port != value.toString()) {
  299. ip_port.port = value.toString();
  300. SetProxySetting(settings, "addrProxy", ip_port);
  301. setRestartRequired(true);
  302. }
  303. }
  304. break;
  305. // separate Tor proxy
  306. case ProxyUseTor:
  307. if (settings.value("fUseSeparateProxyTor") != value) {
  308. settings.setValue("fUseSeparateProxyTor", value.toBool());
  309. setRestartRequired(true);
  310. }
  311. break;
  312. case ProxyIPTor: {
  313. auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
  314. if (!ip_port.is_set || ip_port.ip != value.toString()) {
  315. ip_port.ip = value.toString();
  316. SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
  317. setRestartRequired(true);
  318. }
  319. }
  320. break;
  321. case ProxyPortTor: {
  322. auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
  323. if (!ip_port.is_set || ip_port.port != value.toString()) {
  324. ip_port.port = value.toString();
  325. SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
  326. setRestartRequired(true);
  327. }
  328. }
  329. break;
  330. #ifdef ENABLE_WALLET
  331. case SpendZeroConfChange:
  332. if (settings.value("bSpendZeroConfChange") != value) {
  333. settings.setValue("bSpendZeroConfChange", value);
  334. setRestartRequired(true);
  335. }
  336. break;
  337. #endif
  338. case DisplayUnit:
  339. setDisplayUnit(value);
  340. break;
  341. case ThirdPartyTxUrls:
  342. if (strThirdPartyTxUrls != value.toString()) {
  343. strThirdPartyTxUrls = value.toString();
  344. settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
  345. setRestartRequired(true);
  346. }
  347. break;
  348. case Language:
  349. if (settings.value("language") != value) {
  350. settings.setValue("language", value);
  351. setRestartRequired(true);
  352. }
  353. break;
  354. case CoinControlFeatures:
  355. fCoinControlFeatures = value.toBool();
  356. settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
  357. Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
  358. break;
  359. case DatabaseCache:
  360. if (settings.value("nDatabaseCache") != value) {
  361. settings.setValue("nDatabaseCache", value);
  362. setRestartRequired(true);
  363. }
  364. break;
  365. case ThreadsScriptVerif:
  366. if (settings.value("nThreadsScriptVerif") != value) {
  367. settings.setValue("nThreadsScriptVerif", value);
  368. setRestartRequired(true);
  369. }
  370. break;
  371. case Listen:
  372. if (settings.value("fListen") != value) {
  373. settings.setValue("fListen", value);
  374. setRestartRequired(true);
  375. }
  376. break;
  377. default:
  378. break;
  379. }
  380. }
  381. Q_EMIT dataChanged(index, index);
  382. return successful;
  383. }
  384. /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
  385. void OptionsModel::setDisplayUnit(const QVariant &value)
  386. {
  387. if (!value.isNull())
  388. {
  389. QSettings settings;
  390. nDisplayUnit = value.toInt();
  391. settings.setValue("nDisplayUnit", nDisplayUnit);
  392. Q_EMIT displayUnitChanged(nDisplayUnit);
  393. }
  394. }
  395. bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
  396. {
  397. // Directly query current base proxy, because
  398. // GUI settings can be overridden with -proxy.
  399. proxyType curProxy;
  400. if (GetProxy(NET_IPV4, curProxy)) {
  401. proxy.setType(QNetworkProxy::Socks5Proxy);
  402. proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP()));
  403. proxy.setPort(curProxy.proxy.GetPort());
  404. return true;
  405. }
  406. else
  407. proxy.setType(QNetworkProxy::NoProxy);
  408. return false;
  409. }
  410. void OptionsModel::setRestartRequired(bool fRequired)
  411. {
  412. QSettings settings;
  413. return settings.setValue("fRestartRequired", fRequired);
  414. }
  415. bool OptionsModel::isRestartRequired() const
  416. {
  417. QSettings settings;
  418. return settings.value("fRestartRequired", false).toBool();
  419. }
  420. void OptionsModel::checkAndMigrate()
  421. {
  422. // Migration of default values
  423. // Check if the QSettings container was already loaded with this client version
  424. QSettings settings;
  425. static const char strSettingsVersionKey[] = "nSettingsVersion";
  426. int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
  427. if (settingsVersion < CLIENT_VERSION)
  428. {
  429. // -dbcache was bumped from 100 to 300 in 0.13
  430. // see https://github.com/bitcoin/bitcoin/pull/8273
  431. // force people to upgrade to the new value if they are using 100MB
  432. if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
  433. settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
  434. settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
  435. }
  436. // Overwrite the 'addrProxy' setting in case it has been set to an illegal
  437. // default value (see issue #12623; PR #12650).
  438. if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) {
  439. settings.setValue("addrProxy", GetDefaultProxyAddress());
  440. }
  441. // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to an illegal
  442. // default value (see issue #12623; PR #12650).
  443. if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
  444. settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
  445. }
  446. }