1 /* ============================================================
2 * QuiteRSS is a open-source cross-platform RSS/Atom news feeds reader
3 * Copyright (C) 2011-2020 QuiteRSS Team <quiterssteam@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 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "mainapplication.h"
19
20 #include "common.h"
21 #include "cookiejar.h"
22 #include "database.h"
23 #include "globals.h"
24 #include "networkmanager.h"
25 #include "adblockmanager.h"
26 #include "settings.h"
27 #include "splashscreen.h"
28 #include "updatefeeds.h"
29 #include "VersionNo.h"
30
MainApplication(int & argc,char ** argv)31 MainApplication::MainApplication(int &argc, char **argv)
32 : QtSingleApplication(argc, argv)
33 , isPortableAppsCom_(false)
34 , isClosing_(false)
35 , dbFileExists_(false)
36 , translator_(0)
37 , qt_translator_(0)
38 , mainWindow_(0)
39 , networkManager_(0)
40 , cookieJar_(0)
41 , diskCache_(0)
42 , downloadManager_(0)
43 , analytics_(0)
44 {
45 setApplicationName("QuiteRss");
46 setOrganizationName("QuiteRss");
47 setApplicationVersion(STRPRODUCTVER);
48 globals.init();
49
50 QString message = arguments().value(1);
51 if (isRunning()) {
52 if (argc == 1) {
53 sendMessage("--show");
54 } else {
55 for (int i = 2; i < argc; ++i)
56 message += '\n' + arguments().value(i);
57 sendMessage(message);
58 }
59 isClosing_ = true;
60 return;
61 } else {
62 if (message.contains("--exit", Qt::CaseInsensitive)) {
63 isClosing_ = true;
64 return;
65 }
66 }
67
68 setWindowIcon(QIcon(":/images/quiterss128"));
69 setQuitOnLastWindowClosed(false);
70
71 createSettings();
72
73 qWarning() << "Run application!";
74
75 setStyleApplication();
76 setTranslateApplication();
77 showSplashScreen();
78
79 createGoogleAnalytics();
80
81 connectDatabase();
82 setProgressSplashScreen(30);
83 qWarning() << "Run application 2";
84 mainWindow_ = new MainWindow();
85 qWarning() << "Run application 3";
86 setProgressSplashScreen(60);
87
88 loadSettings();
89 qWarning() << "Run application 4";
90 updateFeeds_ = new UpdateFeeds(mainWindow_);
91 setProgressSplashScreen(90);
92 qWarning() << "Run application 5";
93 mainWindow_->restoreFeedsOnStartUp();
94 setProgressSplashScreen(100);
95 qWarning() << "Run application 6";
96 if (!mainWindow_->startingTray_ || !mainWindow_->showTrayIcon_) {
97 mainWindow_->show();
98 }
99 mainWindow_->isMinimizeToTray_ = false;
100
101 closeSplashScreen();
102
103 if (mainWindow_->showTrayIcon_) {
104 QTimer::singleShot(0, mainWindow_->traySystem, SLOT(show()));
105 }
106
107 if (updateFeedsStartUp_) {
108 QTimer::singleShot(0, mainWindow_, SLOT(slotGetAllFeeds()));
109 }
110
111 receiveMessage(message);
112 connect(this, SIGNAL(messageReceived(QString)), SLOT(receiveMessage(QString)));
113 }
114
~MainApplication()115 MainApplication::~MainApplication()
116 {
117
118 }
119
getInstance()120 MainApplication *MainApplication::getInstance()
121 {
122 return static_cast<MainApplication*>(QCoreApplication::instance());
123 }
124
receiveMessage(const QString & message)125 void MainApplication::receiveMessage(const QString &message)
126 {
127 if (!message.isEmpty()) {
128 qWarning() << QString("Received message: %1").arg(message);
129
130 QStringList params = message.split('\n');
131 foreach (QString param, params) {
132 if (param == "--show") {
133 if (isClosing_)
134 return;
135 mainWindow_->showWindows();
136 }
137 if (param == "--exit") mainWindow_->quitApp();
138 if (param.contains("feed:", Qt::CaseInsensitive)) {
139 QClipboard *clipboard = QApplication::clipboard();
140 if (param.contains("https://", Qt::CaseInsensitive)) {
141 param.remove(0, 5);
142 clipboard->setText(param);
143 } else {
144 param.remove(0, 7);
145 clipboard->setText("http://" + param);
146 }
147 mainWindow_->addFeed();
148 }
149 }
150 }
151 }
152
createSettings()153 void MainApplication::createSettings()
154 {
155 Settings settings;
156 settings.beginGroup("Settings");
157 storeDBMemory_ = settings.value("storeDBMemory", true).toBool();
158 isSaveDataLastFeed_ = settings.value("createLastFeed", false).toBool();
159 styleApplication_ = settings.value("styleApplication", "greenStyle_").toString();
160 showSplashScreen_ = settings.value("showSplashScreen", true).toBool();
161 updateFeedsStartUp_ = settings.value("autoUpdatefeedsStartUp", false).toBool();
162
163 QString strLang;
164 QString strLocalLang = QLocale::system().name();
165 bool findLang = false;
166 QDir langDir(resourcesDir() + "/lang");
167 foreach (QString file, langDir.entryList(QStringList("*.qm"), QDir::Files)) {
168 strLang = file.section('.', 0, 0).section('_', 1);
169 if (strLocalLang == strLang) {
170 strLang = strLocalLang;
171 findLang = true;
172 break;
173 }
174 }
175 if (!findLang) {
176 strLocalLang = strLocalLang.left(2);
177 foreach (QString file, langDir.entryList(QStringList("*.qm"), QDir::Files)) {
178 strLang = file.section('.', 0, 0).section('_', 1);
179 if (strLocalLang.contains(strLang, Qt::CaseInsensitive)) {
180 strLang = strLocalLang;
181 findLang = true;
182 break;
183 }
184 }
185 }
186 if (!findLang) strLang = "en";
187 langFileName_ = settings.value("langFileName", strLang).toString();
188
189 settings.endGroup();
190
191 proxyLoadSettings();
192 }
193
createGoogleAnalytics()194 void MainApplication::createGoogleAnalytics()
195 {
196 Settings settings;
197 bool statisticsEnabled = settings.value("Settings/statisticsEnabled2", true).toBool();
198 if (statisticsEnabled) {
199 QString clientID;
200 if (!settings.contains("GAnalytics-cid")) {
201 settings.setValue("GAnalytics-cid", QUuid::createUuid().toString());
202 }
203 clientID = settings.value("GAnalytics-cid").toString();
204 analytics_ = new GAnalytics(this, TRACKING_ID, clientID);
205 analytics_->generateUserAgentEtc();
206 analytics_->startSession();
207 }
208 }
209
connectDatabase()210 void MainApplication::connectDatabase()
211 {
212 QString fileName(dbFileName() % ".bak");
213 if (QFile(fileName).exists()) {
214 QString sourceFileName = QFile::symLinkTarget(dbFileName());
215 if (sourceFileName.isEmpty()) {
216 sourceFileName = dbFileName();
217 }
218 if (QFile::remove(sourceFileName)) {
219 if (!QFile::rename(fileName, sourceFileName))
220 qCritical() << "Failed to rename new database file!";
221 } else {
222 qCritical() << "Failed to delete old database file!";
223 }
224 }
225
226 #if defined(HAVE_QT5) && defined(HAVE_X11)
227 fileName = "~/.local/share/data/QuiteRss/QuiteRss/feeds.db";
228 if (!QFile(dbFileName()).exists() && QFile(fileName).exists()) {
229 QFile::copy(fileName, dbFileName());
230 }
231 #endif
232
233 if (QFile(dbFileName()).exists()) {
234 dbFileExists_ = true;
235 }
236
237 Database::initialization();
238 }
239
loadSettings()240 void MainApplication::loadSettings()
241 {
242 c2fLoadSettings();
243 reloadUserStyleBrowser();
244 }
245
quitApplication()246 void MainApplication::quitApplication()
247 {
248 qWarning() << "quitApplication 1";
249 delete mainWindow_;
250 qWarning() << "quitApplication 2";
251 delete networkManager_;
252 delete cookieJar_;
253 delete closingWidget_;
254
255 if (analytics_) {
256 analytics_->endSession();
257 analytics_->waitForIdle();
258 delete analytics_;
259 }
260
261 qWarning() << "Quit application";
262
263 quit();
264 }
265
showClosingWidget()266 void MainApplication::showClosingWidget()
267 {
268 closingWidget_ = new QWidget(0, Qt::Dialog | Qt::WindowStaysOnTopHint | Qt::WindowCloseButtonHint);
269 closingWidget_->setFocusPolicy(Qt::NoFocus);
270 QVBoxLayout *layout = new QVBoxLayout(closingWidget_);
271 layout->addWidget(new QLabel(tr("Saving data...")));
272 closingWidget_->resize(150, 20);
273 closingWidget_->show();
274 closingWidget_->move(QApplication::desktop()->availableGeometry().width() - closingWidget_->frameSize().width(),
275 QApplication::desktop()->availableGeometry().height() - closingWidget_->frameSize().height());
276 closingWidget_->setFixedSize(closingWidget_->size());
277 qApp->processEvents();
278 }
279
commitData(QSessionManager & manager)280 void MainApplication::commitData(QSessionManager &manager)
281 {
282 manager.release();
283 mainWindow_->quitApp();
284 }
285
isPortable() const286 bool MainApplication::isPortable() const
287 {
288 return globals.isPortable_;
289 }
290
isPortableAppsCom() const291 bool MainApplication::isPortableAppsCom() const
292 {
293 return isPortableAppsCom_;
294 }
295
setClosing()296 void MainApplication::setClosing()
297 {
298 isClosing_ = true;
299 }
300
isClosing() const301 bool MainApplication::isClosing() const
302 {
303 return isClosing_;
304 }
305
isNoDebugOutput() const306 bool MainApplication::isNoDebugOutput() const
307 {
308 return globals.noDebugOutput_;
309 }
310
resourcesDir() const311 QString MainApplication::resourcesDir() const
312 {
313 return globals.resourcesDir_;
314 }
315
dataDir() const316 QString MainApplication::dataDir() const
317 {
318 return globals.dataDir_;
319 }
320
absolutePath(const QString & path) const321 QString MainApplication::absolutePath(const QString &path) const
322 {
323 QString absolutePath = path;
324 if (isPortable()) {
325 if (!QDir::isAbsolutePath(path)) {
326 absolutePath = dataDir() % "/" % path;
327 }
328 }
329 return absolutePath;
330 }
331
dbFileName() const332 QString MainApplication::dbFileName() const
333 {
334 return dataDir() % "/feeds.db";
335 }
336
isSaveDataLastFeed() const337 bool MainApplication::isSaveDataLastFeed() const
338 {
339 return isSaveDataLastFeed_;
340 }
341
storeDBMemory() const342 bool MainApplication::storeDBMemory() const
343 {
344 return storeDBMemory_;
345 }
346
setStyleApplication()347 void MainApplication::setStyleApplication()
348 {
349 QString fileName(resourcesDir());
350 if (styleApplication_ == "systemStyle_") {
351 fileName.append("/style/system.qss");
352 } else if (styleApplication_ == "system2Style_") {
353 fileName.append("/style/system2.qss");
354 } else if (styleApplication_ == "darkStyle_") {
355 fileName.append("/style/dark.qss");
356 } else if (styleApplication_ == "orangeStyle_") {
357 fileName.append("/style/orange.qss");
358 } else if (styleApplication_ == "purpleStyle_") {
359 fileName.append("/style/purple.qss");
360 } else if (styleApplication_ == "pinkStyle_") {
361 fileName.append("/style/pink.qss");
362 } else if (styleApplication_ == "grayStyle_") {
363 fileName.append("/style/gray.qss");
364 } else {
365 fileName.append("/style/green.qss");
366 }
367 QFile file(fileName);
368 if (!file.open(QFile::ReadOnly)) {
369 file.setFileName(":/style/systemStyle");
370 file.open(QFile::ReadOnly);
371 }
372 setStyleSheet(QLatin1String(file.readAll()));
373 file.close();
374
375 setStyle(new QProxyStyle);
376 }
377
setTranslateApplication()378 void MainApplication::setTranslateApplication()
379 {
380 if (!translator_)
381 translator_ = new QTranslator(this);
382 removeTranslator(translator_);
383 translator_->load(resourcesDir() + QString("/lang/quiterss_%1").arg(langFileName_));
384 installTranslator(translator_);
385
386 if (!qt_translator_)
387 qt_translator_ = new QTranslator(this);
388 removeTranslator(qt_translator_);
389 #ifdef HAVE_X11
390 qt_translator_->load(QLibraryInfo::location (QLibraryInfo::TranslationsPath) + "/qtbase_" + langFileName_);
391 #else
392 qt_translator_->load(resourcesDir() + "/lang/qtbase_" + langFileName_);
393 #endif
394 installTranslator(qt_translator_);
395 }
396
showSplashScreen()397 void MainApplication::showSplashScreen()
398 {
399 Settings settings;
400 int versionDB = settings.value("VersionDB", "1").toInt();
401 if ((versionDB != Database::version()) && QFile::exists(settings.fileName()))
402 showSplashScreen_ = true;
403
404 if (showSplashScreen_) {
405 splashScreen_ = new SplashScreen(QPixmap(":/images/images/splashScreen.png"));
406 splashScreen_->show();
407 processEvents();
408 if ((versionDB != Database::version()) && QFile::exists(settings.fileName())) {
409 splashScreen_->showMessage(QString("Converting database to version %1...").arg(Database::version()),
410 Qt::AlignRight | Qt::AlignTop, Qt::darkGray);
411 }
412 }
413 }
414
closeSplashScreen()415 void MainApplication::closeSplashScreen()
416 {
417 if (showSplashScreen_) {
418 splashScreen_->finish(mainWindow_);
419 splashScreen_->deleteLater();
420 }
421 }
422
setProgressSplashScreen(int value)423 void MainApplication::setProgressSplashScreen(int value)
424 {
425 if (showSplashScreen_)
426 splashScreen_->setProgress(value);
427 }
428
mainWindow()429 MainWindow *MainApplication::mainWindow()
430 {
431 return mainWindow_;
432 }
433
networkManager()434 NetworkManager *MainApplication::networkManager()
435 {
436 if (!networkManager_) {
437 networkManager_ = new NetworkManager(false, this);
438 setDiskCache();
439 }
440 return networkManager_;
441 }
442
cookieJar()443 CookieJar *MainApplication::cookieJar()
444 {
445 if (!cookieJar_) {
446 cookieJar_ = new CookieJar(this);
447 }
448 return cookieJar_;
449 }
450
setDiskCache()451 void MainApplication::setDiskCache()
452 {
453 Settings settings;
454 settings.beginGroup("Settings");
455
456 bool useDiskCache = settings.value("useDiskCache", true).toBool();
457 if (useDiskCache) {
458 if (!diskCache_) {
459 diskCache_ = new QNetworkDiskCache(this);
460 }
461
462 QString diskCacheDirPath = settings.value("dirDiskCache", cacheDefaultDir()).toString();
463 if (diskCacheDirPath.isEmpty()) diskCacheDirPath = cacheDefaultDir();
464 diskCacheDirPath = absolutePath(diskCacheDirPath);
465
466 bool cleanDiskCache = settings.value("cleanDiskCache", true).toBool();
467 if (cleanDiskCache) {
468 Common::removePath(diskCacheDirPath);
469 settings.setValue("cleanDiskCache", false);
470 }
471
472 diskCache_->setCacheDirectory(diskCacheDirPath);
473 int maxDiskCache = settings.value("maxDiskCache", 50).toInt();
474 diskCache_->setMaximumCacheSize(maxDiskCache*1024*1024);
475
476 networkManager()->setCache(diskCache_);
477 } else {
478 if (diskCache_) {
479 diskCache_->setMaximumCacheSize(0);
480 diskCache_->clear();
481 }
482 }
483
484 settings.endGroup();
485 }
486
cacheDefaultDir() const487 QString MainApplication::cacheDefaultDir() const
488 {
489 return globals.cacheDir_;
490 }
491
soundNotifyDefaultFile() const492 QString MainApplication::soundNotifyDefaultFile() const
493 {
494 return globals.soundNotifyDir_ % "/notification.wav";
495 }
496
styleSheetNewsDefaultFile() const497 QString MainApplication::styleSheetNewsDefaultFile() const
498 {
499 if (isPortable()) {
500 return "style/news.css";
501 } else {
502 return resourcesDir() % "/style/news.css";
503 }
504 }
505
styleSheetWebDarkFile() const506 QString MainApplication::styleSheetWebDarkFile() const
507 {
508 if (isPortable()) {
509 return "style/web_dark.css";
510 } else {
511 return resourcesDir() % "/style/web_dark.css";
512 }
513 }
514
updateFeeds()515 UpdateFeeds *MainApplication::updateFeeds()
516 {
517 return updateFeeds_;
518 }
519
runUserFilter(int feedId,int filterId)520 void MainApplication::runUserFilter(int feedId, int filterId)
521 {
522 emit signalRunUserFilter(feedId, filterId);
523 }
524
sqlQueryExec(const QString & query)525 void MainApplication::sqlQueryExec(const QString &query)
526 {
527 emit signalSqlQueryExec(query);
528 }
529
530 /** @brief Click to Flash
531 *---------------------------------------------------------------------------*/
c2fLoadSettings()532 void MainApplication::c2fLoadSettings()
533 {
534 Settings settings;
535 settings.beginGroup("ClickToFlash");
536 c2fWhitelist_ = settings.value("whitelist", QStringList()).toStringList();
537 c2fEnabled_ = settings.value("enabled", true).toBool();
538 #if QT_VERSION >= 0x050900
539 c2fEnabled_ = false;
540 #endif
541 settings.endGroup();
542 }
543
c2fSaveSettings()544 void MainApplication::c2fSaveSettings()
545 {
546 Settings settings;
547 settings.beginGroup("ClickToFlash");
548 settings.setValue("whitelist", c2fWhitelist_);
549 settings.setValue("enabled", c2fEnabled_);
550 settings.endGroup();
551 }
552
c2fIsEnabled() const553 bool MainApplication::c2fIsEnabled() const
554 {
555 return c2fEnabled_;
556 }
557
c2fSetEnabled(bool enabled)558 void MainApplication::c2fSetEnabled(bool enabled)
559 {
560 c2fEnabled_ = enabled;
561 }
562
c2fGetWhitelist()563 QStringList MainApplication::c2fGetWhitelist()
564 {
565 return c2fWhitelist_;
566 }
567
c2fSetWhitelist(QStringList whitelist)568 void MainApplication::c2fSetWhitelist(QStringList whitelist)
569 {
570 c2fWhitelist_ = whitelist;
571 }
572
c2fAddWhitelist(const QString & site)573 void MainApplication::c2fAddWhitelist(const QString &site)
574 {
575 c2fWhitelist_.append(site);
576 }
577
downloadManager()578 DownloadManager *MainApplication::downloadManager()
579 {
580 if (!downloadManager_) {
581 downloadManager_ = new DownloadManager();
582 }
583 return downloadManager_;
584 }
585
reloadUserStyleBrowser()586 void MainApplication::reloadUserStyleBrowser()
587 {
588 Settings settings;
589 settings.beginGroup("Settings");
590 QString userStyleBrowser = settings.value("userStyleBrowser", QString()).toString();
591 QWebSettings::globalSettings()->setUserStyleSheetUrl(userStyleSheet(userStyleBrowser));
592 settings.endGroup();
593 }
594
595 /** @brief Set user style sheet for browser
596 * @param filePath Filepath of user style
597 * @return URL-link to user style
598 *---------------------------------------------------------------------------*/
userStyleSheet(const QString & filePath) const599 QUrl MainApplication::userStyleSheet(const QString &filePath) const
600 {
601 QString userStyle;
602
603 #ifndef HAVE_X11
604 // Don't grey out selection on losing focus (to prevent graying out found text)
605 QString highlightColor;
606 QString highlightedTextColor;
607 #ifdef Q_OS_MAC
608 highlightColor = QLatin1String("#b6d6fc");
609 highlightedTextColor = QLatin1String("#000");
610 #else
611 QPalette pal = style()->standardPalette();
612 highlightColor = pal.color(QPalette::Highlight).name();
613 highlightedTextColor = pal.color(QPalette::HighlightedText).name();
614 #endif
615 userStyle += QString("::selection {background: %1; color: %2;} ").arg(highlightColor, highlightedTextColor);
616 #endif
617
618 userStyle += AdBlockManager::instance()->elementHidingRules();
619
620 QFile file(filePath);
621 if (!filePath.isEmpty() && file.open(QFile::ReadOnly)) {
622 QString fileData = QString::fromUtf8(file.readAll());
623 fileData.remove(QLatin1Char('\n'));
624 userStyle.append(fileData);
625 file.close();
626 }
627
628 const QString &encodedStyle = userStyle.toLatin1().toBase64();
629 const QString &dataString = QString("data:text/css;charset=utf-8;base64,%1").arg(encodedStyle);
630
631 return QUrl(dataString);
632 }
633
proxyLoadSettings()634 void MainApplication::proxyLoadSettings()
635 {
636 Settings settings;
637 settings.beginGroup("networkProxy");
638 networkProxy_.setType(static_cast<QNetworkProxy::ProxyType>(
639 settings.value("type", QNetworkProxy::DefaultProxy).toInt()));
640 networkProxy_.setHostName(settings.value("hostName", "").toString());
641 networkProxy_.setPort( settings.value("port", "").toUInt());
642 networkProxy_.setUser( settings.value("user", "").toString());
643 networkProxy_.setPassword(settings.value("password", "").toString());
644 settings.endGroup();
645
646 setProxy();
647 }
648
proxySaveSettings(const QNetworkProxy & proxy)649 void MainApplication::proxySaveSettings(const QNetworkProxy &proxy)
650 {
651 networkProxy_ = proxy;
652
653 Settings settings;
654 settings.beginGroup("networkProxy");
655 settings.setValue("type", networkProxy_.type());
656 settings.setValue("hostName", networkProxy_.hostName());
657 settings.setValue("port", networkProxy_.port());
658 settings.setValue("user", networkProxy_.user());
659 settings.setValue("password", networkProxy_.password());
660 settings.endGroup();
661
662 setProxy();
663 }
664
setProxy()665 void MainApplication::setProxy()
666 {
667
668 if (QNetworkProxy::DefaultProxy == networkProxy_.type())
669 QNetworkProxyFactory::setUseSystemConfiguration(true);
670 else
671 QNetworkProxy::setApplicationProxy(networkProxy_);
672 }
673