1 //  This file is part of Qt Bitcoin Trader
2 //      https://github.com/JulyIGHOR/QtBitcoinTrader
3 //  Copyright (C) 2013-2021 July Ighor <julyighor@gmail.com>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  In addition, as a special exception, the copyright holders give
11 //  permission to link the code of portions of this program with the
12 //  OpenSSL library under certain conditions as described in each
13 //  individual source file, and distribute linked combinations including
14 //  the two.
15 //
16 //  You must obey the GNU General Public License in all respects for all
17 //  of the code used other than OpenSSL. If you modify file(s) with this
18 //  exception, you may extend this exception to your version of the
19 //  file(s), but you are not obligated to do so. If you do not wish to do
20 //  so, delete this exception statement from your version. If you delete
21 //  this exception statement from all source files in the program, then
22 //  also delete it here.
23 //
24 //  This program is distributed in the hope that it will be useful,
25 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
26 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27 //  GNU General Public License for more details.
28 //
29 //  You should have received a copy of the GNU General Public License
30 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
31 
32 #include "main.h"
33 #include "config/config_manager.h"
34 #include "datafolderchusedialog.h"
35 #include "exchange/exchange.h"
36 #include "iniengine.h"
37 #include "julyaes256.h"
38 #include "julylockfile.h"
39 #include "login/allexchangesdialog.h"
40 #include "login/featuredexchangesdialog.h"
41 #include "login/newpassworddialog.h"
42 #include "login/passworddialog.h"
43 #include "login/qttraderinform.h"
44 #include "qsystemdetection.h"
45 #include "timesync.h"
46 #include "translationdialog.h"
47 #include "updaterdialog.h"
48 #include "utils/utils.h"
49 #include <QApplication>
50 #include <QDesktopServices>
51 #include <QDir>
52 #include <QFileInfo>
53 #include <QLibraryInfo>
54 #include <QLoggingCategory>
55 #include <QMessageBox>
56 #include <QMetaEnum>
57 #include <QNetworkProxy>
58 #include <QNetworkProxyFactory>
59 #include <QProcess>
60 #include <QSettings>
61 #include <QStandardPaths>
62 #include <QStyle>
63 #include <QStyleFactory>
64 #include <QThread>
65 #include <QTranslator>
66 #include <QUrl>
67 #include <QUuid>
68 
69 #ifdef Q_OS_WIN
70 #include <windows.h>
71 #endif
72 
73 #ifdef Q_OS_UNIX
74 #include <initializer_list>
75 #include <signal.h>
quitOnSignals(std::initializer_list<int> quitSignals)76 void quitOnSignals(std::initializer_list<int> quitSignals)
77 {
78     auto handler = [](int sig) -> void
79     {
80         qDebug().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") << "Shutdown signal received" << sig;
81         QCoreApplication::quit();
82     };
83 
84     sigset_t blocking_mask;
85     sigemptyset(&blocking_mask);
86 
87     for (auto sig : quitSignals)
88         sigaddset(&blocking_mask, sig);
89 
90     struct sigaction sa;
91     sa.sa_handler = handler;
92     sa.sa_mask = blocking_mask;
93     sa.sa_flags = 0;
94 
95     for (auto sig : quitSignals)
96         sigaction(sig, &sa, nullptr);
97 }
98 #endif
99 
100 BaseValues* baseValues_;
101 
BaseValues()102 BaseValues::BaseValues()
103 {
104     forceDotInSpinBoxes = true;
105     scriptsThatUseOrderBookCount = 0;
106     trafficSpeed = 0;
107     trafficTotal = 0;
108     trafficTotalType = 0;
109     feeDecimals = 2;
110     currentExchange_ = nullptr;
111     currentTheme = 0;
112     gzipEnabled = true;
113     appVerIsBeta = false;
114     jlScriptVersion = 1.0;
115     appVerStr = "1.4055";
116     appVerReal = appVerStr.toDouble();
117 
118     if (appVerStr.size() > 4)
119     {
120         if (appVerStr.size() == 7)
121             appVerStr.remove(6, 1);
122 
123         appVerStr.insert(4, ".");
124     }
125 
126     appVerLastReal = appVerReal;
127 
128     logThread_ = nullptr;
129 
130     highResolutionDisplay = true;
131     timeFormat = QLocale().timeFormat(QLocale::LongFormat).replace(" ", "").replace("t", "");
132     dateTimeFormat = QLocale().dateFormat(QLocale::ShortFormat) + " " + timeFormat;
133     depthCountLimit = 100;
134     depthCountLimitStr = "100";
135     uiUpdateInterval = 100;
136     supportsUtfUI = true;
137     debugLevel_ = 0;
138 
139 #ifdef Q_WS_WIN
140 
141     if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP)
142         supportsUtfUI = false;
143 
144 #endif
145 
146     upArrow = QByteArray::fromBase64("4oaR");
147     downArrow = QByteArray::fromBase64("4oaT");
148 
149     if (supportsUtfUI)
150     {
151         upArrowNoUtf8 = upArrow;
152         downArrowNoUtf8 = downArrow;
153     }
154     else
155     {
156         upArrowNoUtf8 = ">";
157         downArrowNoUtf8 = "<";
158     }
159 
160     httpRequestInterval = 400;
161     minimumRequestInterval = 400;
162     httpRequestTimeout = 5000;
163     minimumRequestTimeout = 5000;
164     httpRetryCount = 5;
165     apiDownCount = 0;
166     groupPriceValue = 0.0;
167     defaultHeightForRow_ = 22;
168 
169     tempLocation = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first().replace('\\', '/') + "/";
170     desktopLocation = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first().replace('\\', '/') + "/";
171     logFileName = QLatin1String("QtBitcoinTrader.log");
172     iniFileName = QLatin1String("QtBitcoinTrader.ini");
173 
174     selectSystemLanguage();
175 }
176 
selectSystemLanguage()177 void BaseValues::selectSystemLanguage()
178 {
179     QString sysLocale = QLocale().name().toLower();
180 
181     if (sysLocale.startsWith("de"))
182         defaultLangFile = ":/Resources/Language/German.lng";
183     else if (sysLocale.startsWith("fr"))
184         defaultLangFile = ":/Resources/Language/French.lng";
185     else if (sysLocale.startsWith("zh"))
186         defaultLangFile = ":/Resources/Language/Chinese.lng";
187     else if (sysLocale.startsWith("ru"))
188         defaultLangFile = ":/Resources/Language/Russian.lng";
189     else if (sysLocale.startsWith("uk"))
190         defaultLangFile = ":/Resources/Language/Ukrainian.lng";
191     else if (sysLocale.startsWith("pl"))
192         defaultLangFile = ":/Resources/Language/Polish.lng";
193     else if (sysLocale.startsWith("nl"))
194         defaultLangFile = ":/Resources/Language/Dutch.lng";
195     else if (sysLocale.startsWith("es"))
196         defaultLangFile = ":/Resources/Language/Spanish.lng";
197     else if (sysLocale.startsWith("nb"))
198         defaultLangFile = ":/Resources/Language/Norwegian.lng";
199     else if (sysLocale.startsWith("bg"))
200         defaultLangFile = ":/Resources/Language/Bulgarian.lng";
201     else if (sysLocale.startsWith("cs"))
202         defaultLangFile = ":/Resources/Language/Czech.lng";
203     else if (sysLocale.startsWith("tr"))
204         defaultLangFile = ":/Resources/Language/Turkish.lng";
205     else if (sysLocale.startsWith("it"))
206         defaultLangFile = ":/Resources/Language/Italiano.lng";
207     else
208         defaultLangFile = ":/Resources/Language/English.lng";
209 }
210 
initHiDpi()211 void BaseValues::initHiDpi()
212 {
213 #ifdef Q_OS_LINUX
214     defaultEnableHiDPI = false;
215 #else
216     defaultEnableHiDPI = true;
217 #endif
218 
219     QSettings hiDpiSettings("Centrabit", "Qt Bitcoin Trader");
220 
221     if (hiDpiSettings.value("HiDPI", defaultEnableHiDPI).toBool())
222     {
223         QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
224         QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
225     }
226     else
227         QApplication::setAttribute(Qt::AA_Use96Dpi);
228 }
229 
initAppDataDir(QApplication & a)230 bool BaseValues::initAppDataDir(QApplication& a)
231 {
232     bool portableModeSupported(false);
233 #ifdef Q_OS_MAC
234 
235     if (!a.applicationDirPath().startsWith("/Applications/"))
236         portableModeSupported = true;
237 
238 #endif
239 #ifdef Q_OS_WIN
240     portableModeSupported = true;
241 #endif
242 #ifdef QTBUILDTARGETLINUX64
243     portableModeSupported = true;
244 #endif
245     QString systemAppDataDir(QStandardPaths::standardLocations(QStandardPaths::DataLocation).first());
246 #ifdef Q_OS_WIN
247     systemAppDataDir.replace('\\', '/');
248 #endif
249 
250     if (portableModeSupported)
251     {
252         QString portableAppDataDir(a.applicationDirPath() + QLatin1String("/QtBitcoinTrader.data"));
253 #ifdef Q_OS_WIN
254 
255         if (QFile::exists(a.applicationDirPath() + QLatin1String("/QtBitcoinTrader")) &&
256             QFileInfo(a.applicationDirPath() + QLatin1String("/QtBitcoinTrader")).isDir())
257             QFile::rename(a.applicationDirPath() + QLatin1String("/QtBitcoinTrader"), portableAppDataDir);
258 
259 #endif
260         appDataDir = systemAppDataDir;
261 
262         if (!QFile::exists(portableAppDataDir) && !QFile::exists(appDataDir))
263         {
264             julyTranslator.loadFromFile(defaultLangFile);
265             DataFolderChuseDialog chuseStorageLocation(appDataDir, portableAppDataDir);
266 
267             if (chuseStorageLocation.exec() == QDialog::Rejected)
268                 return false;
269 
270             if (chuseStorageLocation.isPortable)
271                 QDir().mkdir(portableAppDataDir);
272             else
273             {
274                 QDir().mkdir(systemAppDataDir);
275                 QString installedBin = systemAppDataDir + "/QtBitcoinTrader";
276                 const QString desktopLocation(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first());
277 #ifdef Q_OS_WIN
278                 installedBin.append(".exe");
279 #else
280                 installedBin.append(".bin");
281 #endif
282 
283                 QFile::Permissions selfPerms = QFile(a.applicationFilePath()).permissions();
284 
285                 if (a.applicationFilePath().startsWith(desktopLocation))
286                     QFile::rename(a.applicationFilePath(), installedBin);
287                 else
288                     QFile::copy(a.applicationFilePath(), installedBin);
289 
290                 if (QFile::exists(installedBin))
291                 {
292                     QFile(installedBin).setPermissions(selfPerms);
293 #ifdef Q_OS_WIN
294                     {
295                         QString desktopFile(desktopLocation + "/Qt Bitcoin Trader.lnk");
296                         QFile::link(installedBin, desktopFile);
297                         QProcess proc;
298                         proc.startDetached(installedBin, QStringList() << "/installed");
299                         proc.waitForStarted(3000);
300                         return false;
301                     }
302 #endif
303 #ifdef Q_OS_LINUX
304                     {
305                         QString desktopFile(desktopLocation + "/Qt Bitcoin Trader.desktop");
306                         QString desktopIconFile(systemAppDataDir + "/QtBitcoinTrader.png");
307                         {
308                             QByteArray iconData;
309                             QFile rF(":/Resources/QtBitcoinTrader.png");
310                             rF.open(QFile::ReadOnly);
311                             QFile wF(desktopIconFile);
312 
313                             if (wF.open(QFile::WriteOnly))
314                             {
315                                 wF.write(rF.readAll());
316                                 wF.close();
317                             }
318 
319                             rF.close();
320                         }
321                         {
322                             QFile wF(desktopFile);
323 
324                             if (wF.open(QFile::WriteOnly))
325                             {
326                                 wF.write("[Desktop Entry]\n"
327                                          "Encoding=UTF-8\n"
328                                          "Name=Qt Bitcoin Trader\n"
329                                          "GenericName=Secure Multi Trading Client\n"
330                                          "Exec=\"" +
331                                          installedBin.toUtf8() +
332                                          "\"\n"
333                                          "Icon=" +
334                                          desktopIconFile.toUtf8() +
335                                          "\n"
336                                          "Terminal=false\n"
337                                          "Type=Application\n"
338                                          "Categories=Qt;Office;Finance;\n");
339                                 wF.close();
340                             }
341                         }
342                         QProcess proc;
343                         proc.startDetached(installedBin, QStringList() << "/installed");
344                         proc.waitForStarted(3000);
345                         return false;
346                     }
347 #endif
348                 }
349             }
350         }
351 
352         if (QFile::exists(portableAppDataDir))
353         {
354             portableMode = true;
355             appDataDir = portableAppDataDir;
356         }
357 
358         if (!QFile::exists(appDataDir + "/Language"))
359             QDir().mkpath(appDataDir + "/Language");
360 
361         if (!QFile::exists(appDataDir))
362         {
363             QMessageBox::warning(
364                 nullptr, "Qt Bitcoin Trader", julyTr("CAN_NOT_WRITE_TO_FOLDER", "Can not write to folder") + ": \"" + appDataDir + "\"");
365             return false;
366         }
367     }
368     else
369     {
370         appDataDir = systemAppDataDir;
371         QString oldAppDataDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first() + "/.config/QtBitcoinTrader";
372 
373         if (!QFile::exists(appDataDir) && oldAppDataDir != appDataDir && QFile::exists(oldAppDataDir))
374         {
375             QFile::rename(oldAppDataDir, appDataDir);
376 
377             if (QFile::exists(oldAppDataDir))
378             {
379                 if (!QFile::exists(appDataDir))
380                     QDir().mkpath(appDataDir);
381 
382                 QStringList fileList = QDir(oldAppDataDir).entryList();
383 
384                 for (int n = 0; n < fileList.size(); n++)
385                     if (fileList.at(n).length() > 2)
386                     {
387                         QFile::copy(oldAppDataDir + fileList.at(n), appDataDir + fileList.at(n));
388 
389                         if (QFile::exists(oldAppDataDir + fileList.at(n)))
390                             QFile::remove(oldAppDataDir + fileList.at(n));
391                     }
392             }
393         }
394 
395         if (!QFile::exists(appDataDir))
396             QDir().mkpath(appDataDir);
397 
398         if (!QFile::exists(appDataDir))
399         {
400             QMessageBox::warning(
401                 nullptr, "Qt Bitcoin Trader", julyTr("CAN_NOT_WRITE_TO_FOLDER", "Can not write to folder") + ": \"" + appDataDir + "\"");
402             return false;
403         }
404     }
405 
406     return true;
407 }
408 
initValues(QApplication & a)409 void BaseValues::initValues(QApplication& a)
410 {
411     qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
412     qRegisterMetaType<QList<QSslError>>("QList<QSslError>");
413 
414     QThread::currentThread()->setObjectName("Main Thread");
415 
416     a.setWindowIcon(QIcon(":/Resources/QtBitcoinTrader.png"));
417     a.setApplicationVersion(appVerStr);
418 
419     QTranslator qTranslator;
420     qTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
421     a.installTranslator(&qTranslator);
422 
423 #ifdef Q_OS_WIN // DPI Fix
424     QFont font = a.font();
425     font.setPixelSize(11);
426     a.setFont(font);
427 #endif
428 
429     fontMetrics_ = new QFontMetrics(a.font());
430     scriptFolder = appDataDir + "/Scripts/";
431 
432     if (QFile::exists(a.applicationFilePath() + ".upd"))
433         QFile::remove(a.applicationFilePath() + ".upd");
434 
435     if (QFile::exists(a.applicationFilePath() + ".bkp"))
436         QFile::remove(a.applicationFilePath() + ".bkp");
437 }
438 
initThemes(QApplication & a)439 void BaseValues::initThemes(QApplication& a)
440 {
441     a.setStyle(QStyleFactory::create("Fusion"));
442 
443     if (QFile::exists(appDataDir + "/Themes"))
444     {
445         themeFolder = appDataDir + "/Themes";
446 
447         if (!QFile::exists(themeFolder + "/Dark.thm"))
448             QFile::copy("://Resources/Themes/Dark.thm", themeFolder + "/Dark.thm");
449 
450         if (!QFile::exists(themeFolder + "/Light.thm"))
451             QFile::copy("://Resources/Themes/Light.thm", themeFolder + "/Light.thm");
452 
453         if (!QFile::exists(themeFolder + "/Gray.thm"))
454             QFile::copy("://Resources/Themes/Gray.thm", themeFolder + "/Gray.thm");
455     }
456     else
457         themeFolder = "://Resources/Themes";
458 
459     appThemeLight.palette = a.palette();
460     appThemeDark.palette = a.palette();
461     appThemeGray.palette = a.palette();
462 
463     appThemeLight.loadTheme("Light");
464     appThemeDark.loadTheme("Dark");
465     appThemeGray.loadTheme("Gray");
466     appTheme = appThemeLight;
467 
468     osStyle = a.style()->objectName();
469 
470     a.setPalette(appTheme.palette);
471     a.setStyleSheet(appTheme.styleSheet);
472 }
473 
initSettings()474 void BaseValues::initSettings()
475 {
476     QSettings settingsMain(appDataDir + "/QtBitcoinTrader.cfg", QSettings::IniFormat);
477 
478     settingsMain.beginGroup("Proxy");
479     bool proxyEnabled = settingsMain.value("Enabled", true).toBool();
480     bool proxyAuto = settingsMain.value("Auto", true).toBool();
481     QString proxyHost = settingsMain.value("Host", "127.0.0.1").toString();
482     quint16 proxyPort = static_cast<quint16>(settingsMain.value("Port", 1234).toUInt());
483     QString proxyUser = settingsMain.value("User", "username").toString();
484     QString proxyPassword = settingsMain.value("Password", "password").toString();
485     QNetworkProxy::ProxyType proxyType;
486 
487     if (settingsMain.value("Type", "HttpProxy").toString() == "Socks5Proxy")
488         proxyType = QNetworkProxy::Socks5Proxy;
489     else
490         proxyType = QNetworkProxy::HttpProxy;
491 
492     settingsMain.setValue("Enabled", proxyEnabled);
493     settingsMain.setValue("Auto", proxyAuto);
494     settingsMain.setValue("Host", proxyHost);
495     settingsMain.setValue("Port", proxyPort);
496     settingsMain.setValue("User", proxyUser);
497     settingsMain.setValue("Password", proxyPassword);
498     settingsMain.endGroup();
499 
500     QNetworkProxy proxy;
501 
502     if (proxyEnabled)
503     {
504         if (proxyAuto)
505         {
506             QList<QNetworkProxy> proxyList = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(QUrl("https://")));
507 
508             if (!proxyList.empty())
509                 proxy = proxyList.first();
510         }
511         else
512         {
513             proxy.setHostName(proxyHost);
514             proxy.setUser(proxyUser);
515             proxy.setPort(proxyPort);
516             proxy.setPassword(proxyPassword);
517             proxy.setType(proxyType);
518         }
519 
520         QNetworkProxy::setApplicationProxy(proxy);
521     }
522 
523     appVerLastReal = settingsMain.value("Version", 1.0).toDouble();
524 
525     if (!qFuzzyCompare(appVerLastReal + 1.0, appVerReal + 1.0))
526     {
527         settingsMain.setValue("Version", appVerReal);
528         QStringList cacheFiles = QDir(appDataDir + "/cache").entryList(QStringList("*.cache"), QDir::Files);
529 
530         for (int i = 0; i < cacheFiles.size(); ++i)
531             QFile(appDataDir + "/cache/" + cacheFiles.at(i)).remove();
532 
533         if (qFuzzyIsNull(appVerLastReal))
534             appVerLastReal = appVerReal;
535     }
536 
537     appVerIsBeta = settingsMain.value("CheckForUpdatesBeta", false).toBool();
538     use24HourTimeFormat = settingsMain.value("Use24HourTimeFormat", true).toBool();
539 
540     settingsMain.beginGroup("Decimals");
541     decimalsAmountMyTransactions = settingsMain.value("AmountMyTransactions", 8).toInt();
542     decimalsPriceMyTransactions = settingsMain.value("PriceMyTransactions", 8).toInt();
543     decimalsTotalMyTransactions = settingsMain.value("TotalMyTransactions", 8).toInt();
544     decimalsAmountOrderBook = settingsMain.value("AmountOrderBook", 8).toInt();
545     decimalsPriceOrderBook = settingsMain.value("PriceOrderBook", 8).toInt();
546     decimalsTotalOrderBook = settingsMain.value("TotalOrderBook", 8).toInt();
547     decimalsAmountLastTrades = settingsMain.value("AmountLastTrades", 8).toInt();
548     decimalsPriceLastTrades = settingsMain.value("PriceLastTrades", 8).toInt();
549     decimalsTotalLastTrades = settingsMain.value("TotalLastTrades", 8).toInt();
550     settingsMain.endGroup();
551 
552     {
553         if (!QFile::exists(appDataDir + "/Language"))
554             QDir().mkpath(appDataDir + "/Language");
555 
556         QString langFile = settingsMain.value("LanguageFile", "").toString();
557 
558         if (langFile.isEmpty() || !QFile::exists(langFile))
559             langFile = defaultLangFile;
560 
561         julyTranslator.loadFromFile(langFile);
562     }
563 }
564 
main(int argc,char * argv[])565 int main(int argc, char* argv[])
566 {
567     if (QSslSocket::sslLibraryVersionString().isEmpty())
568     {
569         QMessageBox::critical(nullptr, "Qt Bitcoin Trader", julyTr("CANT_LOAD_OPENSSL", "Can't load OpenSSL libraries"));
570         return 0;
571     }
572 
573     QScopedPointer<JulyLockFile> julyLock(nullptr);
574     QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
575     baseValues_ = new BaseValues();
576     baseValues.initHiDpi();
577     QApplication a(argc, argv);
578 
579     if (argc > 1)
580     {
581 #ifdef Q_OS_LINUX
582 
583         if (a.arguments().contains("/test"))
584         {
585             qDebug().noquote() << "(-: OK :-)";
586             return 0;
587         }
588 
589 #endif
590 
591         if (a.arguments().contains("/uninstall"))
592         {
593             QThread::msleep(2000);
594 #ifndef Q_OS_MAC
595             QString tmpDir = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first();
596 
597             if (!a.applicationFilePath().startsWith(tmpDir))
598                 return 0;
599 
600 #ifdef Q_OS_WIN
601             QString desktopShortcut = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first() + "/Qt Bitcoin Trader.lnk";
602 
603             if (QFile::exists(desktopShortcut))
604                 DeleteFile(reinterpret_cast<LPCWSTR>(desktopShortcut.replace('/', '\\').utf16()));
605 
606 #else
607             QString desktopShortcut =
608                 QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first() + "/Qt Bitcoin Trader.desktop";
609 
610             if (!desktopShortcut.isEmpty() && QFile::exists(desktopShortcut))
611                 QFile::remove(desktopShortcut);
612 
613 #endif
614 
615 #endif
616             QDir appdataDir(QStandardPaths::standardLocations(QStandardPaths::DataLocation).first());
617 
618             if (appdataDir.exists())
619                 appdataDir.removeRecursively();
620 
621             QMessageBox::information(
622                 nullptr, "Qt Bitcoin Trader", julyTr("QT_BITCOIN_TRADER_UNINSTALLED", "Qt Bitcoin Trader completely uninstalled"));
623 #ifndef Q_OS_MAC
624             QFile::remove(a.applicationFilePath());
625 #endif
626             return 0;
627         }
628 
629         if (a.arguments().contains("/installed"))
630         {
631             QMessageBox::information(nullptr,
632                                      "Qt Bitcoin Trader",
633                                      julyTr("QT_BITCOIN_TRADER_INSTALLED",
634                                             "Qt Bitcoin Trader installed into system folder<br>"
635                                             "Launch shortcut have been placed in your Desktop folder<br>"
636                                             "To uninstall it you can use menu Help->Uninstall<br>"
637                                             "You can now delete installation file")
638                                          .replace("<br>", "<br><br>"));
639         }
640     }
641 
642     a.setApplicationName("QtBitcoinTrader");
643 
644     if (!baseValues.initAppDataDir(a))
645         return 0;
646 
647     baseValues.initValues(a);
648     baseValues.initThemes(a);
649     baseValues.initSettings();
650 
651     if (argc > 1)
652     {
653         if (a.arguments().last().startsWith("/checkupdate"))
654         {
655             QSettings settings(appDataDir + "/QtBitcoinTrader.cfg", QSettings::IniFormat);
656             QString langFile = settings.value("LanguageFile", "").toString();
657 
658             if (langFile.isEmpty() || !QFile::exists(langFile))
659                 langFile = baseValues.defaultLangFile;
660 
661             julyTranslator.loadFromFile(langFile);
662             QTimer::singleShot(1000000, &a, &QCoreApplication::quit);
663             UpdaterDialog updater(a.arguments().last() != "/checkupdate");
664 #ifdef Q_OS_UNIX
665             quitOnSignals({SIGHUP, SIGQUIT, SIGABRT, SIGTERM, SIGXCPU, SIGXFSZ, SIGINT});
666 #endif
667             return a.exec();
668         }
669     }
670 
671     IniEngine::global();
672     TimeSync::global();
673 
674     {
675         bool tryDecrypt = true;
676         bool showNewPasswordDialog = false;
677 
678         while (tryDecrypt)
679         {
680             QString tryPassword;
681             baseValues.restKey.clear();
682             baseValues.restSign.clear();
683             bool noProfiles = QDir(appDataDir, "*.ini").entryList().isEmpty();
684 
685             if (noProfiles || showNewPasswordDialog)
686             {
687                 FeaturedExchangesDialog featuredExchanges;
688 
689                 if (featuredExchanges.exchangeNum != -3)
690                 {
691                     int execResult = featuredExchanges.exec();
692 
693                     if (noProfiles && execResult == QDialog::Rejected)
694                         return 0;
695 
696                     if (featuredExchanges.exchangeNum != -2)
697                         if (execResult == QDialog::Rejected || featuredExchanges.exchangeNum == -1)
698                         {
699                             showNewPasswordDialog = false;
700                             continue;
701                         }
702                 }
703 
704                 qint32 exchangeNumber = 0;
705 
706                 if (featuredExchanges.exchangeNum >= 0)
707                     exchangeNumber = featuredExchanges.exchangeNum;
708                 else
709                 {
710                     AllExchangesDialog allExchanges(featuredExchanges.exchangeNum);
711 
712                     if (allExchanges.exec() == QDialog::Rejected)
713                     {
714                         showNewPasswordDialog = false;
715                         continue;
716                     }
717 
718                     if (allExchanges.exchangeNum == -1)
719                     {
720                         showNewPasswordDialog = false;
721                         continue;
722                     }
723 
724                     if (allExchanges.exchangeNum == -2)
725                         continue;
726 
727                     exchangeNumber = allExchanges.exchangeNum;
728                 }
729 
730                 if (exchangeNumber == 0)
731                 {
732                     QMessageBox msgBox;
733                     msgBox.setIcon(QMessageBox::Question);
734                     msgBox.setWindowTitle("Qt Bitcoin Trader");
735                     msgBox.setText(julyTr("AGREE_CLOSED_SOURCE", "Do you agree to download the enclosed application?"));
736                     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
737                     msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
738                     msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
739 
740                     if (msgBox.exec() == QMessageBox::No)
741                         continue;
742                 }
743 
744                 NewPasswordDialog newPassword(exchangeNumber);
745 
746                 if (newPassword.exec() == QDialog::Accepted)
747                 {
748                     tryPassword = newPassword.getPassword();
749                     newPassword.updateIniFileName();
750                     baseValues.restKey = newPassword.getRestKey().toLatin1();
751                     QSettings settings(baseValues.iniFileName, QSettings::IniFormat);
752                     settings.setValue("Profile/ExchangeId", newPassword.getExchangeId());
753                     settings.sync();
754 
755                     if (!QFile::exists(baseValues.iniFileName))
756                         QMessageBox::warning(nullptr, "Qt Bitcoin Trader", "Can't write file: \"" + baseValues.iniFileName + "\"");
757 
758                     QByteArray encryptedData;
759 
760                     switch (newPassword.getExchangeId())
761                     {
762                     case 0:
763                         {
764                             // Qt Trader Exchange
765                             baseValues.restSign = newPassword.getRestSign().toLatin1();
766                             encryptedData =
767                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
768                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
769                                                     tryPassword.toUtf8());
770                         }
771                         break;
772 
773                     case 2:
774                         {
775                             // Bitstamp
776                             baseValues.restSign = newPassword.getRestSign().toLatin1();
777                             encryptedData =
778                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
779                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
780                                                     tryPassword.toUtf8());
781                         }
782                         break;
783 
784                     case 4:
785                         {
786                             // Bitfinex
787                             baseValues.restSign = newPassword.getRestSign().toLatin1();
788                             encryptedData =
789                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
790                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
791                                                     tryPassword.toUtf8());
792                         }
793                         break;
794 
795                     case 6:
796                         {
797                             // Indacoin
798                             baseValues.restSign = newPassword.getRestSign().toLatin1();
799                             encryptedData =
800                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
801                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
802                                                     tryPassword.toUtf8());
803                         }
804                         break;
805 
806                     case 10:
807                         {
808                             // YObit
809                             baseValues.restSign = newPassword.getRestSign().toLatin1();
810                             encryptedData =
811                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
812                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
813                                                     tryPassword.toUtf8());
814                         }
815                         break;
816 
817                     case 11:
818                         {
819                             // Binance
820                             baseValues.restSign = newPassword.getRestSign().toLatin1();
821                             encryptedData =
822                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
823                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
824                                                     tryPassword.toUtf8());
825                         }
826                         break;
827 
828                     case 12:
829                         {
830                             // Bittrex
831                             baseValues.restSign = newPassword.getRestSign().toLatin1();
832                             encryptedData =
833                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
834                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
835                                                     tryPassword.toUtf8());
836                         }
837                         break;
838 
839                     case 13:
840                         {
841                             // HitBTC
842                             baseValues.restSign = newPassword.getRestSign().toLatin1();
843                             encryptedData =
844                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
845                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
846                                                     tryPassword.toUtf8());
847                         }
848                         break;
849 
850                     case 14:
851                         {
852                             // Poloniex
853                             baseValues.restSign = newPassword.getRestSign().toLatin1();
854                             encryptedData =
855                                 JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + baseValues.restKey + "\r\n" + baseValues.restSign.toBase64() +
856                                                         "\r\n" + QUuid::createUuid().toString().toLatin1(),
857                                                     tryPassword.toUtf8());
858                         }
859                         break;
860 
861                     default:
862                         break;
863                     }
864 
865                     settings.setValue("Profile/Name", newPassword.selectedProfileName());
866                     settings.setValue("EncryptedData/ApiKeySign", QString(encryptedData.toBase64()));
867                     settings.sync();
868 
869                     showNewPasswordDialog = false;
870                 }
871                 else if (noProfiles)
872                     continue;
873             }
874 
875             PasswordDialog enterPassword;
876 
877             if (enterPassword.exec() == QDialog::Rejected)
878                 return 0;
879 
880             if (enterPassword.resetData)
881                 continue;
882 
883             if (enterPassword.newProfile)
884             {
885                 showNewPasswordDialog = true;
886                 continue;
887             }
888 
889             tryPassword = enterPassword.getPassword();
890 
891             if (!tryPassword.isEmpty())
892             {
893                 baseValues.iniFileName = enterPassword.getIniFilePath();
894                 baseValues.logFileName = baseValues.iniFileName;
895                 baseValues.logFileName.replace(".ini", ".log", Qt::CaseInsensitive);
896 
897                 if (julyLock)
898                     julyLock->free();
899 
900                 julyLock.reset(new JulyLockFile(baseValues.iniFileName, appDataDir));
901                 bool profileLocked = julyLock->isLocked();
902 
903                 if (profileLocked)
904                 {
905                     QMessageBox msgBox(nullptr);
906                     msgBox.setIcon(QMessageBox::Question);
907                     msgBox.setWindowTitle("Qt Bitcoin Trader");
908                     msgBox.setText(julyTr(
909                         "THIS_PROFILE_ALREADY_USED",
910                         "This profile is already used by another instance.<br>API does not allow to run two instances with same key sign pair.<br>Please create new profile if you want to use two instances."));
911                     msgBox.setStandardButtons(QMessageBox::Ok);
912                     msgBox.setDefaultButton(QMessageBox::Ok);
913                     msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
914                     msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
915 
916                     msgBox.exec();
917 
918                     tryPassword.clear();
919                 }
920 
921                 if (!profileLocked)
922                 {
923                     QSettings settings(baseValues.iniFileName, QSettings::IniFormat);
924                     QStringList decryptedList =
925                         QString(JulyAES256::decrypt(
926                                     QByteArray::fromBase64(settings.value("EncryptedData/ApiKeySign", "").toString().toLatin1()),
927                                     tryPassword.toUtf8()))
928                             .split("\r\n");
929 
930                     if (decryptedList.size() < 3 || decryptedList.first() != "Qt Bitcoin Trader")
931                     {
932                         decryptedList =
933                             QString(JulyAES256::decrypt(
934                                         QByteArray::fromBase64(settings.value("EncryptedData/ApiKeySign", "").toString().toLatin1()),
935                                         tryPassword.toLatin1()))
936                                 .split("\r\n");
937                     }
938 
939                     if (decryptedList.size() >= 3 && decryptedList.first() == "Qt Bitcoin Trader")
940                     {
941                         baseValues.restKey = decryptedList.at(1).toLatin1();
942                         baseValues.restSign = QByteArray::fromBase64(decryptedList.at(2).toLatin1());
943 
944                         if (decryptedList.size() == 3)
945                         {
946                             decryptedList << QUuid::createUuid().toString().toLatin1();
947                             settings.setValue(
948                                 "EncryptedData/ApiKeySign",
949                                 QString(JulyAES256::encrypt("Qt Bitcoin Trader\r\n" + decryptedList.at(1).toLatin1() + "\r\n" +
950                                                                 decryptedList.at(2).toLatin1() + "\r\n" + decryptedList.at(3).toLatin1(),
951                                                             tryPassword.toUtf8())
952                                             .toBase64()));
953                             settings.sync();
954                         }
955 
956                         baseValues.randomPassword = decryptedList.at(3).toLatin1();
957                         tryDecrypt = false;
958                     }
959                 }
960             }
961         }
962 
963         baseValues.scriptFolder += QFileInfo(baseValues.iniFileName).completeBaseName() + "/";
964 
965         QSettings iniSettings(baseValues.iniFileName, QSettings::IniFormat);
966 
967         if (iniSettings.value("Debug/LogEnabled", false).toBool())
968             debugLevel = 1;
969 
970         iniSettings.setValue("Debug/LogEnabled", debugLevel > 0);
971         baseValues.logThread_ = nullptr;
972 
973         if (debugLevel)
974         {
975             baseValues.logThread_ = new LogThread;
976             logThread->writeLog("Proxy settings: " + QNetworkProxy::applicationProxy().hostName().toUtf8() + ":" +
977                                 QByteArray::number(QNetworkProxy::applicationProxy().port()) + " " +
978                                 QNetworkProxy::applicationProxy().user().toUtf8());
979         }
980 
981         QSettings settingsMain(appDataDir + "/QtBitcoinTrader.cfg", QSettings::IniFormat);
982 
983         if (settingsMain.value("ShowQtTraderInform2", true).toBool())
984         {
985             QtTraderInform inform;
986             int informRez = inform.exec();
987 
988             if (inform.dontShowAgain())
989             {
990                 settingsMain.setValue("ShowQtTraderInform2", false);
991                 settingsMain.sync();
992             }
993 
994             if (informRez == QDialog::Accepted)
995                 QDesktopServices::openUrl(QUrl("https://qttrader.com/"));
996         }
997 
998         ::config = new ConfigManager(slash(appDataDir, "QtBitcoinTrader.ws.cfg"), &a);
999         baseValues.mainWindow_ = new QtBitcoinTrader;
1000     }
1001 
1002     baseValues.mainWindow_->setupClass();
1003 
1004 #ifdef Q_OS_UNIX
1005     quitOnSignals({SIGHUP, SIGQUIT, SIGABRT, SIGTERM, SIGXCPU, SIGXFSZ, SIGINT});
1006 #endif
1007     int rezult = a.exec();
1008     return rezult;
1009 }
1010