RabbitCommon v2.3.3
Loading...
Searching...
No Matches
FrmUpdater.cpp
1// Copyright Copyright (c) Kang Lin studio, All Rights Reserved
2// Author Kang Lin <kl222@126.com>
3
4#include <QUrl>
5#include <QStandardPaths>
6#include <QFinalState>
7#include <QJsonDocument>
8#include <QJsonObject>
9#include <QJsonArray>
10#include <QDomDocument>
11#include <QDomText>
12#include <QDomElement>
13#include <QProcess>
14#include <QDir>
15#include <QSsl>
16#include <QDesktopServices>
17#include <QInputDialog>
18#include <QMessageBox>
19#include <QMenu>
20#include <QSettings>
21#include <QLoggingCategory>
22#include <QRegularExpression>
23#include <QStateMachine>
24
25#include "Download.h"
26#include "FrmUpdater.h"
27#include "RabbitCommonDir.h"
28#include "RabbitCommonTools.h"
29#include "ui_FrmUpdater.h"
30
31static Q_LOGGING_CATEGORY(log, "RabbitCommon.Updater")
32
33CFrmUpdater::CFrmUpdater(QWidget *parent) :
34 QWidget(parent),
35 ui(new Ui::CFrmUpdater),
36 m_InstallAutoStartupType(false),
37 m_ButtonGroup(this),
38 m_bDownload(false),
39 m_StateMachine(nullptr),
40 m_pStateDownloadSetupFile(nullptr)
41{
42 bool check = false;
43 setAttribute(Qt::WA_DeleteOnClose);
44 ui->setupUi(this);
45 ui->lbNewArch->hide();
46 ui->lbNewVersion->hide();
47 ui->progressBar->hide();
48 ui->cbHomePage->hide();
49 ui->pbOK->hide();
50
51 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
52 QSettings::IniFormat);
53 ui->cbPrompt->setChecked(set.value("Updater/Prompt", false).toBool());
54 ui->cbHomePage->setChecked(set.value("Updater/ShowHomePage", true).toBool());
55
56 check = connect(&m_TrayIcon,
57 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
58 this,
59 SLOT(slotShowWindow(QSystemTrayIcon::ActivationReason)));
60 Q_ASSERT(check);
61 m_TrayIcon.setIcon(this->windowIcon());
62 m_TrayIcon.setToolTip(windowTitle() + " - "
63 + qApp->applicationDisplayName());
64
65 int id = set.value("Update/RadioButton", -2).toInt();
66 m_ButtonGroup.addButton(ui->rbEveryTime);
67 m_ButtonGroup.addButton(ui->rbEveryDate);
68 m_ButtonGroup.addButton(ui->rbEveryWeek);
69 m_ButtonGroup.addButton(ui->rbEveryMonth);
70 m_ButtonGroup.button(id)->setChecked(true);
71 check = connect(&m_ButtonGroup,
72 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
73 SIGNAL(idClicked(int)),
74 #else
75 SIGNAL(buttonClicked(int)),
76 #endif
77 this, SLOT(slotButtonClickd(int)));
78 Q_ASSERT(check);
79 SetTitle();
80
81 ui->lbCurrentArch->setText(tr("Current archecture: %1")
82 .arg(QSysInfo::currentCpuArchitecture()));
83
84 QString szVerion = qApp->applicationVersion();
85#ifdef RabbitCommon_VERSION
86 if(szVerion.isEmpty())
87 szVerion = RabbitCommon_VERSION;
88#else
89 szVerion = "0.0.1";
90#endif
91 SetVersion(szVerion);
92
93 if(QSslSocket::supportsSsl())
94 {
95 QString szMsg;
96#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
97 szMsg = "Build Version: " + QSslSocket::sslLibraryBuildVersionString();
98#endif
99 szMsg += "; Installed Version: " + QSslSocket::sslLibraryVersionString();
100 qInfo(log) << "QSslSocket support ssl:" << szMsg;
101 } else {
102 QString szMsg;
103#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
104 szMsg = "BuildVersion: " + QSslSocket::sslLibraryBuildVersionString();
105#endif
106 qCritical(log) <<
107 "QSslSocket is not support ssl. The system is not install the OPENSSL dynamic library[" << szMsg << "]."
108 " Please install OPENSSL dynamic library [" << szMsg << "]";
109 }
110}
111
112CFrmUpdater::CFrmUpdater(QVector<QUrl> urls, QWidget *parent): CFrmUpdater(parent)
113{
114 if(urls.isEmpty())
115 {
116 // [Redirect configure file default urls]
117 QUrl github("https://github.com/KangLin/"
118 + qApp->applicationName() + "/raw/master/Update/update.json");
119 QUrl gitlab("https://gitlab.com/kl222/"
120 + qApp->applicationName() + "/-/raw/master/Update/update.json");
121 QUrl gitee("https://gitee.com/kl222/"
122 + qApp->applicationName() + "/raw/master/Update/update.json");
123 QUrl sourceforge("https://sourceforge.net/p/"
124 + qApp->applicationName() + "/ci/master/tree/Update/update.json?format=raw");
125 // [Redirect configure file default urls]
126 m_Urls << github << gitee << sourceforge << gitlab;
127 } else {
128 m_Urls = urls;
129 }
130 qDebug(log) << "Urls:" << m_Urls;
132}
133
134CFrmUpdater::~CFrmUpdater()
135{
136 qDebug(log) << "CFrmUpdater::~CFrmUpdater()";
137 m_DownloadFile.close();
138 if(m_Download)
139 m_Download.reset();
140 if(m_StateMachine)
141 m_StateMachine->deleteLater();
142 delete ui;
143}
144
197{
198 qDebug(log) << "Init State Machine";
199 QFinalState *sFinal = new QFinalState();
200 QState *sCheck = new QState();
201 QState *s = new QState();
202 QState *sDownloadConfigFile = new QState(s);
203 QState *sCheckConfigFile = new QState(s);
204 QState *sDownloadSetupFile = new QState(s);
205 QState *sUpdate = new QState(s);
206
207 bool check = connect(sFinal, SIGNAL(entered()),
208 this, SLOT(slotStateFinished()));
209 Q_ASSERT(check);
210
211 sCheck->addTransition(this, SIGNAL(sigError()), sFinal);
212 sCheck->addTransition(this, SIGNAL(sigFinished()), s);
213 check = connect(sCheck, SIGNAL(entered()), this, SLOT(slotCheck()));
214 Q_ASSERT(check);
215
216 s->addTransition(this, SIGNAL(sigError()), sFinal);
217 s->addTransition(this, SIGNAL(sigFinished()), sFinal);
218
219 s->setInitialState(sDownloadConfigFile);
220 sDownloadConfigFile->assignProperty(ui->lbState, "text", tr("Being Download config file"));
221 sDownloadConfigFile->addTransition(this, SIGNAL(sigFinished()), sCheckConfigFile);
222 check = connect(sDownloadConfigFile, SIGNAL(entered()),
223 this, SLOT(slotDownloadFile()));
224 Q_ASSERT(check);
225
226 sCheckConfigFile->addTransition(this, SIGNAL(sigDownLoadRedire()), sDownloadConfigFile);
227 sCheckConfigFile->addTransition(this, SIGNAL(sigFinished()), sDownloadSetupFile);
228 sCheckConfigFile->addTransition(ui->pbOK, SIGNAL(clicked()), sDownloadSetupFile);
229 sCheckConfigFile->assignProperty(ui->pbOK, "text", tr("OK(&O)"));
230 check = connect(sCheckConfigFile, SIGNAL(entered()), this, SLOT(slotCheckConfigFile()));
231 Q_ASSERT(check);
232
233 m_pStateDownloadSetupFile = sDownloadSetupFile;
234 sDownloadSetupFile->addTransition(this, SIGNAL(sigFinished()), sUpdate);
235 sDownloadSetupFile->assignProperty(ui->lbState, "text", tr("Being download update file"));
236 check = connect(sDownloadSetupFile, SIGNAL(entered()), this, SLOT(slotDownloadSetupFile()));
237 Q_ASSERT(check);
238
239 sUpdate->assignProperty(ui->lbState, "text", tr("Being install update"));
240 check = connect(sUpdate, SIGNAL(entered()), this, SLOT(slotUpdate()));
241 Q_ASSERT(check);
242
243 if(!m_StateMachine)
244 m_StateMachine = new QStateMachine(this);
245 m_StateMachine->addState(sCheck);
246 m_StateMachine->addState(s);
247 m_StateMachine->addState(sFinal);
248 m_StateMachine->setInitialState(sCheck);
249 m_StateMachine->start();
250 return 0;
251}
252
253int CFrmUpdater::SetTitle(QImage icon, const QString &szTitle)
254{
255 QString title = szTitle;
256 if(szTitle.isEmpty())
257 title = qApp->applicationDisplayName();
258 ui->lbTitle->setText(title);
259
260 QPixmap pixmpa = QPixmap::fromImage(icon);
261 if(pixmpa.isNull())
262 pixmpa.load(":/icon/RabbitCommon/App", "PNG");
263 ui->lbTitleIcon->setPixmap(pixmpa);
264 return 0;
265}
266
267int CFrmUpdater::SetVersion(const QString &szVersion)
268{
269 m_szCurrentVersion = szVersion;
270 ui->lbCurrentVersion->setText(tr("Current version: %1")
271 .arg(m_szCurrentVersion));
272 return 0;
273}
274
275void CFrmUpdater::slotStateFinished()
276{
277 qDebug(log) << "slotStateFinished()";
278 if(m_Download)
279 m_Download.reset();
280}
281
282void CFrmUpdater::slotCheck()
283{
284 qDebug(log) << "CFrmUpdater::slotCheck()";
285 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
286 QSettings::IniFormat);
287 QDateTime d = set.value("Update/DateTime").toDateTime();
288 set.setValue("Update/DateTime", QDateTime::currentDateTime());
289 if(m_bDownload)
290 {
291 emit sigFinished();
292 return;
293 }
294
295 int n = 0;
296 if(ui->rbEveryDate->isChecked())
297 n = 1;
298 else if(ui->rbEveryWeek->isChecked())
299 n = 7;
300 else if(ui->rbEveryMonth->isChecked())
301 n = 30;
302
303 if(n <= d.daysTo(QDateTime::currentDateTime()))
304 emit sigFinished();
305 else
306 emit sigError();
307}
308
309// [Process the signals of RabbitCommon::CDownload]
310void CFrmUpdater::slotDownloadError(int nErr, const QString szError)
311{
312 ui->progressBar->hide();
313 ui->progressBar->setRange(0, 100);;
314 QString szMsg;
315 szMsg = tr("Failed:") + tr("Download file is Failed.");
316 if(!szError.isEmpty())
317 szMsg += "(" + szError + ")";
318 ui->lbState->setText(szMsg);
319 qCritical(log) << szMsg << nErr;
320 emit sigError();
321}
322
323void CFrmUpdater::slotDownloadFileFinished(const QString szFile)
324{
325 qDebug(log) << "slotDownloadFileFinished:" << szFile;
326 ui->progressBar->hide();
327 ui->progressBar->setRange(0, 100);;
328 if(m_DownloadFile.isOpen())
329 m_DownloadFile.close();
330
331 QString szTmp
332 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
333 szTmp = szTmp + QDir::separator() + "Rabbit"
334 + QDir::separator() + qApp->applicationName();
335
336 QString szFileName(m_ConfigFile.szFileName);
337 if(szFileName.isEmpty())
338 {
339 szFileName = szFile.mid(szFile.lastIndexOf("/"));
340 }
341 if(szFileName.left(1) != "/" && szFileName.left(1) != "\\")
342 szFileName = QDir::separator() + szFileName;
343 QString f = szTmp + szFileName;
344 if(QFile::exists(f))
345 QFile::remove(f);
346#if HAVE_TEST
347 if(QFile::copy(szFile, f))
348#else
349 if(QFile::rename(szFile, f))
350#endif
351 {
352 m_DownloadFile.setFileName(f);
353 qInfo(log) << "Download finished: rename"
354 << szFile << "to" << f;
355 } else {
356 qCritical(log) << "Download finished. rename fail from"
357 << szFile << "to" << f;
358 m_DownloadFile.setFileName(szFile);
359 }
360 emit sigFinished();
361}
362
363void CFrmUpdater::slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
364{
365 if(ui->progressBar->isHidden())
366 {
367 ui->progressBar->show();
368 ui->progressBar->setRange(0, static_cast<int>(bytesTotal));
369 }
370 if(ui->progressBar->maximum() != bytesTotal)
371 ui->progressBar->setRange(0, static_cast<int>(bytesTotal));
372
373 ui->progressBar->setValue(static_cast<int>(bytesReceived));
374 if(bytesTotal > 0) {
375 QString szInfo = tr("Downloading %1% [%2/%3]")
376 .arg(QString::number(bytesReceived * 100 / bytesTotal))
377 .arg(QString::number(bytesReceived)).arg(QString::number(bytesTotal));
378 //qDebug(log) << szInfo;
379 m_TrayIcon.setToolTip(windowTitle() + " - "
380 + qApp->applicationDisplayName()
381 + ": " + szInfo);
382 }
383}
384// [Process the signals of RabbitCommon::CDownload]
385
386void CFrmUpdater::slotDownloadFile()
387{
388 qDebug(log) << "CFrmUpdater::slotDownloadFile";
389 // [Use RabbitCommon::CDownload download file]
390 if(!m_Urls.isEmpty())
391 {
392 m_Download = QSharedPointer<RabbitCommon::CDownload>(
393 new RabbitCommon::CDownload(), &QObject::deleteLater);
394 bool check = connect(m_Download.data(), SIGNAL(sigFinished(const QString)),
395 this, SLOT(slotDownloadFileFinished(const QString)));
396 Q_ASSERT(check);
397 check = connect(m_Download.data(), SIGNAL(sigError(int, const QString)),
398 this, SLOT(slotDownloadError(int, const QString)));
399 Q_ASSERT(check);
400 check = connect(m_Download.data(), SIGNAL(sigDownloadProgress(qint64, qint64)),
401 this, SLOT(slotDownloadProgress(qint64, qint64)));
402 Q_ASSERT(check);
403 m_Download->Start(m_Urls);
404 }
405 // [Use RabbitCommon::CDownload download file]
406}
407
408void CFrmUpdater::slotCheckConfigFile()
409{
410 m_TrayIcon.setToolTip(windowTitle() + " - "
411 + qApp->applicationDisplayName());
412 qDebug(log) << "CFrmUpdater::slotCheckConfigFile()";
413
414 // Redirect
415 int nRet = 0;
417 if(nRet <= 0) return;
418 if(2 == nRet)
419 {
420 QString szText(tr("There is laster version"));
421 ui->lbState->setText(szText);
422 qInfo(log) << szText;
423 emit sigError();
424 return;
425 }
426 if(1 == nRet)
428}
429
445{
446 int nRet = 0;
447 qDebug(log) << "CFrmUpdater::CheckRedirectConfigFile()"
448 << m_DownloadFile.fileName();
449
450 QVector<CONFIG_REDIRECT> conf;
451 nRet = GetRedirectFromFile(m_DownloadFile.fileName(), conf);
452 if(nRet) {
453 if(nRet < 0) {
454 QString szError = tr("Failed:") + tr("%2 process the file: %1")
455 .arg(m_DownloadFile.fileName()).arg(nRet);
456 ui->lbState->setText(szError);
457 qCritical(log) << szError;
458 emit sigError();
459 }
460 return nRet;
461 }
462
463 CONFIG_REDIRECT redirect;
464 for(auto it = conf.begin(); it != conf.end(); it++) {
465
466 QString szVersion = it->szVersion;
467 if(szVersion.isEmpty())
468 {
469 QString szError = tr("Failed:") + tr("Configure file content error:")
470 + m_DownloadFile.fileName();
471 ui->lbState->setText(szError);
472 qCritical(log) << szError;
473 emit sigError();
474 return -2;
475 }
476
477 if(CompareVersion(szVersion, m_szCurrentVersion) <= 0)
478 continue;
479
480 QString szMinVersion = it->szMinUpdateVersion;
481 if(!szMinVersion.isEmpty()) {
482 if(CompareVersion(szMinVersion, m_szCurrentVersion) > 0)
483 continue;
484 }
485
486 redirect = *it;
487 break;
488 }
489
490 if(redirect.szVersion.isEmpty())
491 return 2;
492
493 CONFIG_FILE file;
494 foreach (auto f, redirect.files) {
495 if(!f.szSystem.isEmpty()) {
496 if(QSysInfo::productType() != f.szSystem)
497 continue;
498 }
499 if(!f.szArchitecture.isEmpty())
500 {
501 QString szArchitecture = QSysInfo::currentCpuArchitecture();
502#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
503 if(!szArchitecture.compare("i386", Qt::CaseInsensitive)
504 && !f.szArchitecture.compare("x86_64", Qt::CaseInsensitive))
505 continue;
506#else
507 if(szArchitecture != f.szArchitecture)
508 continue;
509#endif
510 }
511 file = f;
512 break;
513 }
514
515 m_Urls = file.urls;
516 if(m_Urls.isEmpty())
517 {
518 if(redirect.files.isEmpty()) {
519 // [Update configure file default urls]
520 QUrl github("https://github.com/KangLin/"
521 + qApp->applicationName() + "/releases/download/"
522 + redirect.szVersion + "/update.json");
523 m_Urls.push_back(github);
524 // https://master.dl.sourceforge.net/project/serialportassistant/v0.5.29/update.json?viasf=1
525 QUrl sourceforge("https://sourceforge.net/projects/"
526 + qApp->applicationName() +"/"
527 + redirect.szVersion + "/update.json?viasf=1");
528 m_Urls.push_back(sourceforge);
529 // [Update configure file default urls]
530 } else {
531 QString szError;
532 szError = tr("Failed:")
533 + tr("Don't find the urls in configure file:")
534 + m_DownloadFile.fileName()
535 + "; " + tr("Current version:") + m_szCurrentVersion
536 + "; " + tr("version:") + redirect.szVersion
537 + "; " + tr("min update version:")
538 + redirect.szMinUpdateVersion;
539 qCritical(log) << szError;
540 ui->lbState->setText(szError);
541 return -3;
542 }
543 }
544
545 qInfo(log) << "Redirect. Version:" << redirect.szVersion << m_Urls;
546
547 emit sigDownLoadRedire();
548
549 return 0;
550}
551
585{
586 int nRet = 0;
587 qDebug(log) << "CFrmUpdater::CheckUpdateConfigFile()";
588 CONFIG_INFO info;
589 nRet = GetConfigFromFile(m_DownloadFile.fileName(), info);
590 if(nRet) {
591 QString szError = tr("Failed:") + tr("%2 process the file: %1")
592 .arg(m_DownloadFile.fileName()).arg(nRet);
593 ui->lbState->setText(szError);
594 qCritical(log) << szError;
595 emit sigError();
596 return nRet;
597 }
598
599 if(CompareVersion(info.version.szVerion, m_szCurrentVersion) <= 0)
600 {
601 QString szError;
602 szError = tr("There is laster version");
603 ui->lbState->setText(szError);
604 qInfo(log) << szError;
605 emit sigError();
606 return -4;
607 }
608
609 if(info.files.isEmpty()) {
610 QString szError;
611 szError = tr("Failed:") + tr("There is not files in the configure file ")
612 + m_DownloadFile.fileName();
613 ui->lbState->setText(szError);
614 qCritical(log) << szError;
615 emit sigError();
616 return -5;
617 }
618
619 QString szSystem = QSysInfo::productType();
620#if defined(Q_OS_LINUX)
621 QString szAppImage = QString::fromLocal8Bit(qgetenv("APPIMAGE"));
622 if(!szAppImage.isEmpty())
623 szSystem = "AppImage";
624#endif
625 QString szArchitecture = QSysInfo::currentCpuArchitecture();
626 CONFIG_FILE file;
627 foreach (auto f, info.files) {
628 if(f.szSystem.compare(szSystem, Qt::CaseInsensitive))
629 continue;
630
631 if(szArchitecture != f.szArchitecture) {
632#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
633 if(!szArchitecture.compare("x86_64", Qt::CaseInsensitive)
634 && !f.szArchitecture.compare("i386", Qt::CaseInsensitive))
635 {
636 ;
637 } else
638 continue;
639#else
640 continue;
641#endif
642 }
643
644 file = f;
645 break;
646 }
647
648 if(file.szSystem.compare(szSystem, Qt::CaseInsensitive)) {
649 QString szErr;
650 szErr = tr("Failed:")
651 + tr("The system or architecture is not exist in the configure file ")
652 + m_DownloadFile.fileName();
653 ui->lbState->setText(szErr);
654 qCritical(log) << szErr;
655 emit sigError();
656 return -6;
657 }
658
659 m_Info.version = info.version;
660 m_ConfigFile = file;
661
662 ui->lbNewVersion->setText(tr("New version: %1").arg(info.version.szVerion));
663 ui->lbNewVersion->show();
664 ui->lbNewArch->setText(tr("New architecture: %1").arg(file.szArchitecture));
665 ui->lbNewArch->show();
666 ui->lbState->setText(tr("There is a new version, is it updated?"));
667 if(info.version.bForce)
668 {
669 qDebug(log) << "Force update";
670 emit sigFinished();
671 }
672 else
673 {
674 ui->cbHomePage->show();
675 ui->cbPrompt->show();
676 ui->pbOK->setText(tr("OK(&O)"));
677 ui->pbOK->show();
678 if(!CheckPrompt(info.version.szVerion) && this->isHidden())
679 emit sigError();
680 else
681 show();
682 }
683
684 return nRet;
685}
686
698int CFrmUpdater::GetRedirectFromFile(const QString& szFile, QVector<CONFIG_REDIRECT> &conf)
699{
700 QFile f(szFile);
701 if(!f.open(QFile::ReadOnly))
702 {
703 QString szError = tr("Open file fail").arg(szFile);
704 qCritical(log) << szError;
705 return -1;
706 }
707
708 QJsonDocument doc;
709 doc = QJsonDocument::fromJson(f.readAll());
710 f.close();
711 if(!doc.isObject())
712 {
713 QString szError = tr("Parse file %1 fail. It isn't configure file")
714 .arg(f.fileName());
715 qCritical(log) << szError;
716 return -2;
717 }
718
719 QJsonObject objRoot = doc.object();
720 if(!objRoot.contains("redirect"))
721 return 1;
722
723 QJsonArray arrRedirect = objRoot["redirect"].toArray();
724 for(auto it = arrRedirect.begin(); it != arrRedirect.end(); it++) {
725 QJsonObject obj = it->toObject();
726 CONFIG_REDIRECT objRedirect;
727 objRedirect.szVersion = obj["version"].toString();
728 objRedirect.szMinUpdateVersion = obj["min_update_version"].toString();
729 qDebug(log) << "version:" << objRedirect.szVersion
730 << "min_update_version:" << objRedirect.szMinUpdateVersion;
731
732 QJsonArray objFiles = obj["files"].toArray();
733 for(auto it = objFiles.begin(); it != objFiles.end(); it++) {
734 QJsonObject f = it->toObject();
735 CONFIG_FILE file;
736 file.szSystem = f["os"].toString();
737 file.szSystemMinVersion = f["os_min_version"].toString();
738 file.szArchitecture = f["arch"].toString();
739 file.szArchitectureMinVersion = f["arch_min_version"].toString();
740 file.szMd5sum = f["md5"].toString();
741 file.szFileName = f["name"].toString();
742
743 QJsonArray urls = f["urls"].toArray();
744 foreach(auto u, urls)
745 {
746 file.urls.append(u.toString());
747 }
748
749 objRedirect.files.append(file);
750 //*
751 qDebug(log) << "OS:" << file.szSystem
752 << "os_min_version:" << file.szSystemMinVersion
753 << "arch:" << file.szArchitecture
754 << "arch_min_version:" << file.szArchitectureMinVersion
755 << "md5:" << file.szMd5sum
756 << "name:" << file.szFileName
757 << "urls:" << file.urls;//*/
758 }
759
760 conf.append(objRedirect);
761 }
762
763 return 0;
764}
765
818int CFrmUpdater::GetConfigFromFile(const QString &szFile, CONFIG_INFO& conf)
819{
820 QFile file(szFile);
821 if(!file.open(QFile::ReadOnly)) {
822 qDebug(log) << "The file isn't opened:" << szFile;
823 return -1;
824 }
825
826 QJsonDocument doc;
827 doc = QJsonDocument::fromJson(file.readAll());
828 file.close();
829 if(!doc.isObject())
830 {
831 qCritical(log) << "Parser configure file fail." << szFile;
832 return -2;
833 }
834
835 QJsonObject obj = doc.object();
836 if(obj.contains("version")) {
837 QJsonObject objVersion = obj["version"].toObject();
838 conf.version.szVerion = objVersion["version"].toString();
839 conf.version.szMinUpdateVersion = objVersion["min_update_version"].toString();
840 conf.version.szTime = objVersion["time"].toString();
841 conf.version.szInfomation = objVersion["information"].toString();
842 conf.version.szHome = objVersion["home"].toString();
843 conf.version.bForce = objVersion["force"].toBool();
844 //*
845 qDebug(log) << "Current version:" << m_szCurrentVersion
846 << "version:" << conf.version.szVerion
847 << "minUpdateVersion:" << conf.version.szMinUpdateVersion
848 << "time:" << conf.version.szTime
849 << "information:" << conf.version.szInfomation
850 << "home:" << conf.version.szHome
851 << "bForce:" << conf.version.bForce
852 ;//*/
853 }
854
855 if(!obj.contains("files")) {
856 qDebug(log) << "Configure file isn't contains files array";
857 return 0;
858 }
859
860 QJsonArray objFiles = obj["files"].toArray();
861 for(auto it = objFiles.begin(); it != objFiles.end(); it++) {
862 QJsonObject f = it->toObject();
863 CONFIG_FILE file;
864 file.szSystem = f["os"].toString();
865 file.szSystemMinVersion = f["os_min_version"].toString();
866 file.szArchitecture = f["arch"].toString();
867 file.szArchitectureMinVersion = f["arch_min_version"].toString();
868 file.szMd5sum = f["md5"].toString();
869 file.szFileName = f["name"].toString();
870
871 QJsonArray urls = f["urls"].toArray();
872 foreach(auto u, urls)
873 {
874 file.urls.append(u.toString());
875 }
876
877 conf.files.append(file);
878 //*
879 qDebug(log) << "OS:" << file.szSystem
880 << "os_min_version:" << file.szSystemMinVersion
881 << "arch:" << file.szArchitecture
882 << "arch_min_version:" << file.szArchitectureMinVersion
883 << "md5:" << file.szMd5sum
884 << "name:" << file.szFileName
885 << "urls:" << file.urls;//*/
886 }
887
888 return 0;
889}
890
891void CFrmUpdater::slotDownloadSetupFile()
892{
893 qDebug(log) << "CFrmUpdater::slotDownloadSetupFile()";
894 ui->pbOK->setText(tr("Hide"));
895 ui->lbState->setText(tr("Download ......"));
896 if(IsDownLoad())
897 emit sigFinished();
898 else
899 {
900 m_Urls = m_ConfigFile.urls;
901 slotDownloadFile();
902 }
903}
904
905void CFrmUpdater::slotUpdate()
906{
907 m_TrayIcon.setToolTip(windowTitle() + " - "
908 + qApp->applicationDisplayName());
909 ui->lbState->setText(tr("Being install update ......"));
910 ui->progressBar->hide();
911 ui->progressBar->setRange(0, 100);;
912 //qDebug(log) << "CFrmUpdater::slotUpdate()";
913
914 // Check file md5sum
915 bool bSuccess = false;
916 do {
917 if(!m_DownloadFile.open(QIODevice::ReadOnly))
918 {
919 QString szErr;
920 szErr = tr("Failed:") + tr("Don't open download file ")
921 + m_DownloadFile.fileName();
922 qCritical(log) << szErr;
923 ui->lbState->setText(szErr);
924 break;
925 }
926 QCryptographicHash md5sum(QCryptographicHash::Md5);
927 if(!md5sum.addData(&m_DownloadFile))
928 {
929 QString szErr;
930 szErr = tr("Failed:") + tr("Don't open download file ")
931 + m_DownloadFile.fileName();
932 qCritical(log) << szErr;
933 ui->lbState->setText(szErr);
934 break;
935 }
936 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
937 {
938 QString szFail;
939 szFail = tr("Failed:") + tr("Md5sum is different. ")
940 + "\n" + tr("Download file md5sum: ")
941 + md5sum.result().toHex()
942 + "\n" + tr("md5sum in Update.xml:")
943 + m_ConfigFile.szMd5sum;
944 ui->lbState->setText(szFail);
945 qCritical(log) << szFail;
946 break;
947 }
948 bSuccess = true;
949 } while(0);
950
951 m_DownloadFile.close();
952 if(!bSuccess)
953 {
954 emit sigError();
955 return;
956 }
957
958 // Exec download file
959 bSuccess = false;
960 do {
961 //修改文件执行权限
962 /*QFileInfo info(m_szDownLoadFile);
963 if(!info.permission(QFile::ExeUser))
964 {
965 //修改文件执行权限
966 QString szErr = tr("Download file don't execute permissions. Please modify permission then manually execute it.\n%1").arg(m_szDownLoadFile);
967 slotError(-2, szErr);
968 return;
969 }*/
970
971 QProcess proc;
972 QFileInfo fi(m_DownloadFile.fileName());
973 if(!fi.suffix().compare("AppImage", Qt::CaseInsensitive)) {
974
975 QString szAppImage = QString::fromLocal8Bit(qgetenv("APPIMAGE"));
976 bool bRet = false;
977 if(!szAppImage.isEmpty()) {
978 QFile f(fi.filePath());
979 QFileInfo cf(szAppImage);
980 QString szExec;
981 szExec = cf.absoluteDir().absolutePath()
982 + QDir::separator() + fi.fileName();
983 bRet = f.copy(szExec);
984 if(bRet) {
985 QString szMsg(tr("Please exec:") + szExec);
986 ui->lbState->setText(szMsg);
987 qInfo(log) << szMsg;
988 QUrl url = QUrl::fromLocalFile(cf.absoluteDir().absolutePath());
989 if(!QDesktopServices::openUrl(url))
990 {
991 QString szErr;
992 szErr = tr("Failed:") + tr("Open the folder fail:")
993 + cf.absoluteDir().absolutePath();
994 qCritical(log) << szErr;
995 }
996 }
997 }
998 if(!bRet) {
999 QString szMsg(tr("Please exec: ") + fi.absoluteFilePath());
1000 ui->lbState->setText(szMsg);
1001 qInfo(log) << szMsg;
1002 QUrl url = QUrl::fromLocalFile(fi.absoluteDir().absolutePath());
1003 if(!QDesktopServices::openUrl(url))
1004 {
1005 QString szErr;
1006 szErr = tr("Failed:") + tr("Open the folder fail:")
1007 + fi.absoluteDir().absolutePath();
1008 qCritical(log) << szErr;
1009 }
1010 }
1011
1012 } else if(fi.suffix().compare("gz", Qt::CaseInsensitive)) {
1013 QString szCmd;
1014 szCmd = m_DownloadFile.fileName();
1015 //启动安装程序
1016 qInfo(log) << "Start"
1017 << szCmd
1018 << "in a new process, and detaches from it.";
1019 if(!proc.startDetached(szCmd))
1020 {
1021 qInfo(log) << "Start new process fail."
1022 << "Use system installer to install"
1023 << m_DownloadFile.fileName();
1024 QUrl url(m_DownloadFile.fileName());
1025 if(!QDesktopServices::openUrl(url))
1026 {
1027 QString szErr = tr("Failed:")
1028 + tr("Execute install program error.%1")
1029 .arg(m_DownloadFile.fileName());
1030 ui->lbState->setText(szErr);
1031 break;
1032 }
1033 }
1034 } else {
1035 QString szInstall = fi.absolutePath() + QDir::separator() + "setup.sh";
1036 QFile f(szInstall);
1037 if(!f.open(QFile::WriteOnly))
1038 {
1039 QString szErr = tr("Failed:")
1040 + tr("Open file %1 fail").arg(fi.absolutePath());
1041 ui->lbState->setText(szErr);
1042 break;
1043 }
1044 QString szCmd = InstallScript(m_DownloadFile.fileName(),
1045 qApp->applicationName());
1046 f.write(szCmd.toStdString().c_str());
1047 qDebug(log) << szCmd << szInstall;
1048 f.close();
1049
1050 //启动安装程序
1052 QStringList() << szInstall))
1053 {
1054 QString szErr = tr("Failed:") + tr("Execute") + "/bin/bash "
1055 + szInstall + "fail";
1056 ui->lbState->setText(szErr);
1057 break;
1058 }
1059
1060 //启动程序
1061// int nRet = QMessageBox::information(this, tr("Run"),
1062// tr("Run after install"),
1063// QMessageBox::Yes|QMessageBox::No,
1064// QMessageBox::Yes);
1065// if(QMessageBox::No == nRet)
1066// break;
1067// QString szProgram = "/opt/"
1068// + qApp->applicationName()
1069// + "/install1.sh start "
1070// + qApp->applicationName();
1071// QProcess exe;
1072// if(!exe.startDetached(szProgram))
1073// {
1074// QString szErr = tr("Failed:") + tr("Execute program error.%1")
1075// .arg(szProgram);
1076// ui->lbState->setText(szErr);
1077// break;
1078// }
1079 }
1080
1081 ui->lbState->setText(tr("The installer has started, Please close the application"));
1082
1083 //system(m_DownloadFile.fileName().toStdString().c_str());
1084 //int nRet = QProcess::execute(m_DownloadFile.fileName());
1085 //qDebug(log) << "QProcess::execute return: " << nRet;
1086
1087 bSuccess = true;
1088 } while(0);
1089
1090 QProcess procHome;
1091 QString szHome = m_Info.version.szHome;
1092 if((!bSuccess || ui->cbHomePage->isChecked()) && !szHome.isEmpty())
1093 if(!procHome.startDetached(szHome))
1094 {
1095 QUrl url(szHome);
1096 if(!QDesktopServices::openUrl(url))
1097 {
1098 QString szErr = tr("Failed:") + tr("Open home page fail");
1099 ui->lbState->setText(szErr);
1100 }
1101 }
1102
1103 if(bSuccess)
1104 {
1105 emit sigFinished();
1106 qApp->quit();
1107 return;
1108 }
1109
1110 emit sigError();
1111 QUrl url(szHome);
1112 if(!QDesktopServices::openUrl(url))
1113 {
1114 QString szErr = tr("Open home page fail");
1115 qCritical(log) << szErr;
1116 }
1117}
1118
1119QString CFrmUpdater::InstallScript(const QString szDownLoadFile,
1120 const QString szApplicationName)
1121{
1122 QFileInfo fi(szDownLoadFile);
1123 QString szCmd;
1124 szCmd = "#!/bin/bash\n";
1125 szCmd += "set -e\n";
1126 szCmd += "if [ ! -d /opt/" + szApplicationName + " ]; then\n";
1127 szCmd += " mkdir -p /opt/" + szApplicationName + "\n";
1128 szCmd += "fi\n";
1129 szCmd += "cd /opt/" + szApplicationName + "\n";
1130 szCmd += "if [ -f install1.sh ]; then\n";
1131 szCmd += " ./install1.sh remove " + szApplicationName + "\n";
1132 //szCmd += " rm -fr *\n";
1133 szCmd += "fi\n";
1134 szCmd += "cp " + szDownLoadFile + " ." + "\n";
1135 szCmd += "tar xvfz " + fi.fileName() + "\n";
1136 szCmd += "rm " + fi.fileName() + "\n";
1137
1138 //See: Install/install.sh
1139 szCmd += "./install1.sh ";
1140 if(m_InstallAutoStartupType)
1141 szCmd += "install_autostart";
1142 else
1143 szCmd += "install";
1144 //启动程序
1145 int nRet = QMessageBox::information(this, tr("Run"),
1146 tr("Run after install"),
1147 QMessageBox::Yes|QMessageBox::No,
1148 QMessageBox::Yes);
1149 if(QMessageBox::Yes == nRet)
1150 {
1151 szCmd += "_run";
1152 }
1153 szCmd += " " + szApplicationName + "\n";
1154 return szCmd;
1155}
1156
1166int CFrmUpdater::CompareVersion(const QString &newVersion, const QString &currentVersion)
1167{
1168 int nRet = 0;
1169 QString sN = newVersion;
1170 QString sC = currentVersion;
1171
1172 if(sN.isEmpty() || sC.isEmpty())
1173 return sN.length() - sC.length();
1174
1175 sN = sN.replace("-", ".");
1176 sC = sC.replace("-", ".");
1177
1178 QStringList szNew = sN.split(".");
1179 QStringList szCur = sC.split(".");
1180
1181 int count = qMin(szNew.length(), szCur.length());
1182 qDebug(log) << "count:" << count;
1183
1184 if(count <= 1)
1185 return szNew.length() - szCur.length();
1186
1187 QString firstNew = szNew.at(0);
1188 QString firstCur = szCur.at(0);
1189 firstNew = firstNew.remove(QChar('v'), Qt::CaseInsensitive);
1190 firstCur = firstCur.remove(QChar('v'), Qt::CaseInsensitive);
1191 nRet = firstNew.toInt() - firstCur.toInt();
1192 if(nRet)
1193 return nRet;
1194
1195 if(count < 2)
1196 return sN.length() - sC.length();
1197 nRet = szNew.at(1).toInt() - szCur.at(1).toInt();
1198 if(nRet)
1199 return nRet;
1200
1201 if(count < 3)
1202 return sN.length() - sC.length();
1203 return szNew.at(2).toInt() - szCur.at(2).toInt();
1204}
1205
1211{
1212 bool bRet = false;
1213 QString szTmp
1214 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
1215 szTmp = szTmp + QDir::separator() + "Rabbit"
1216 + QDir::separator() + qApp->applicationName();
1217
1218 QString szFile = szTmp + QDir::separator() + m_ConfigFile.szFileName;
1219
1220 QFile f(szFile);
1221 if(!f.open(QIODevice::ReadOnly))
1222 return false;
1223
1224 m_DownloadFile.setFileName(szFile);
1225 do {
1226 QCryptographicHash md5sum(QCryptographicHash::Md5);
1227 if(!md5sum.addData(&f))
1228 {
1229 bRet = false;
1230 break;
1231 }
1232 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
1233 {
1234 bRet = false;
1235 break;
1236 }
1237 else
1238 {
1239 bRet = true;
1240 break;
1241 }
1242 } while(0);
1243 f.close();
1244 return bRet;
1245}
1246
1247void CFrmUpdater::on_pbOK_clicked()
1248{
1249 qDebug(log) << "CFrmUpdater::on_pbOK_clicked()";
1250 if(!m_pStateDownloadSetupFile->active())
1251 return;
1252
1253 m_TrayIcon.show();
1254 hide();
1255}
1256
1257void CFrmUpdater::on_pbClose_clicked()
1258{
1259 if(m_StateMachine->isRunning())
1260 {
1261 QMessageBox::StandardButton ret = QMessageBox::warning(this, tr("Close"),
1262 tr("Is updating, be sure to close?"), QMessageBox::Yes|QMessageBox::No);
1263 if(QMessageBox::No == ret)
1264 {
1265 return;
1266 }
1267 }
1268 emit sigError();
1269 close();
1270}
1271
1272void CFrmUpdater::slotButtonClickd(int id)
1273{
1274 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(), QSettings::IniFormat);
1275 set.setValue("Update/RadioButton", id);
1276}
1277
1279{
1280 QCommandLineParser parser;
1281 int nRet = GenerateUpdateJson(parser);
1282 parser.process(qApp->arguments());
1283 return nRet;
1284}
1285
1286int CFrmUpdater::GenerateUpdateJson(QCommandLineParser &parser)
1287{
1288 QString szFile;
1289 CONFIG_INFO info;
1290 CONFIG_TYPE type;
1291 int nRet = GetConfigFromCommandLine(parser, szFile, info, type);
1292 if(nRet)
1293 return nRet;
1294 return GenerateJsonFile(szFile, info, type);
1295}
1296
1305int CFrmUpdater::GenerateJsonFile(const QString &szFile, const CONFIG_INFO &info, CONFIG_TYPE type)
1306{
1307 QJsonDocument doc;
1308
1309 QJsonObject version;
1310 version.insert("version", info.version.szVerion);
1311 version.insert("min_update_version", info.version.szMinUpdateVersion);
1312 version.insert("time", info.version.szTime);
1313 version.insert("information", info.version.szInfomation);
1314 version.insert("home", info.version.szHome);
1315 version.insert("force", info.version.bForce);
1316
1317 QJsonArray files;
1318 foreach (auto f, info.files) {
1319 QJsonObject file;
1320 file.insert("os", f.szSystem);
1321 if(!f.szSystemMinVersion.isEmpty())
1322 file.insert("os_min_version", f.szSystemMinVersion);
1323 file.insert("arch", f.szArchitecture);
1324 if(!f.szArchitectureMinVersion.isEmpty())
1325 file.insert("arch_min_version", f.szArchitectureMinVersion);
1326 file.insert("md5", f.szMd5sum);
1327 file.insert("name", f.szFileName);
1328 QJsonArray urls;
1329 foreach (auto u, f.urls) {
1330 urls.append(u.toString());
1331 }
1332 file.insert("urls", urls);
1333 files.append(file);
1334 }
1335
1336 switch(type) {
1337 case CONFIG_TYPE::VERSION:
1338 doc.setObject(version);
1339 break;
1340 case CONFIG_TYPE::FILE:
1341 doc.setObject(files[0].toObject());
1342 break;
1343 case CONFIG_TYPE::VERSION_FILE:
1344 {
1345 QJsonObject root;
1346 root.insert("version", version);
1347 root.insert("files", files);
1348 doc.setObject(root);
1349 }
1350 default:
1351 break;
1352 };
1353
1354 QFile f(szFile);
1355 if(!f.open(QIODevice::WriteOnly))
1356 {
1357 qCritical(log) << "Open file fail:" << f.fileName();
1358 return -1;
1359 }
1360 f.write(doc.toJson());
1361 f.close();
1362 return 0;
1363}
1364
1365int CFrmUpdater::GenerateUpdateXmlFile(const QString &szFile, const CONFIG_INFO &info, CONFIG_TYPE &type)
1366{
1367 QDomDocument doc;
1368 QDomProcessingInstruction ins;
1369 //<?xml version='1.0' encoding='UTF-8'?>
1370 ins = doc.createProcessingInstruction("xml", "version=\'1.0\' encoding=\'UTF-8\'");
1371 doc.appendChild(ins);
1372 QDomElement root = doc.createElement("UPDATE");
1373 doc.appendChild(root);
1374
1375 QDomText version = doc.createTextNode("VERSION");
1376 version.setData(info.version.szVerion);
1377 QDomElement eVersion = doc.createElement("VERSION");
1378 eVersion.appendChild(version);
1379 root.appendChild(eVersion);
1380
1381 QDomText time = doc.createTextNode("TIME");
1382 time.setData(info.version.szTime);
1383 QDomElement eTime = doc.createElement("TIME");
1384 eTime.appendChild(time);
1385 root.appendChild(eTime);
1386
1387 QDomText i = doc.createTextNode("INFO");
1388 i.setData(info.version.szInfomation);
1389 QDomElement eInfo = doc.createElement("INFO");
1390 eInfo.appendChild(i);
1391 root.appendChild(eInfo);
1392
1393 QDomText force = doc.createTextNode("FORCE");
1394 force.setData(QString::number(info.version.bForce));
1395 QDomElement eForce = doc.createElement("FORCE");
1396 eForce.appendChild(force);
1397 root.appendChild(eForce);
1398
1399 CONFIG_FILE file = info.files[0];
1400 QDomText system = doc.createTextNode("SYSTEM");
1401 system.setData(file.szSystem);
1402 QDomElement eSystem = doc.createElement("SYSTEM");
1403 eSystem.appendChild(system);
1404 root.appendChild(eSystem);
1405
1406 QDomText arch = doc.createTextNode("ARCHITECTURE");
1407 arch.setData(file.szArchitecture);
1408 QDomElement architecture = doc.createElement("ARCHITECTURE");
1409 architecture.appendChild(arch);
1410 root.appendChild(architecture);
1411
1412 QDomText md5 = doc.createTextNode("MD5SUM");
1413 md5.setData(file.szMd5sum);
1414 QDomElement eMd5 = doc.createElement("MD5SUM");
1415 eMd5.appendChild(md5);
1416 root.appendChild(eMd5);
1417
1418 QDomText fileName = doc.createTextNode("FILENAME");
1419 fileName.setData(file.szFileName);
1420 QDomElement eFileName = doc.createElement("FILENAME");
1421 eFileName.appendChild(fileName);
1422 root.appendChild(eFileName);
1423
1424 foreach(auto u, file.urls)
1425 {
1426 QDomText url = doc.createTextNode("URL");
1427 url.setData(u.toString());
1428 QDomElement eUrl = doc.createElement("URL");
1429 eUrl.appendChild(url);
1430 root.appendChild(eUrl);
1431 }
1432
1433 QDomText urlHome = doc.createTextNode("HOME");
1434 urlHome.setData(info.version.szHome);
1435 QDomElement eUrlHome = doc.createElement("HOME");
1436 eUrlHome.appendChild(urlHome);
1437 root.appendChild(eUrlHome);
1438
1439 QDomText min = doc.createTextNode("MIN_UPDATE_VERSION");
1440 min.setData(info.version.szMinUpdateVersion);
1441 QDomElement eMin = doc.createElement("MIN_UPDATE_VERSION");
1442 eMin.appendChild(min);
1443 root.appendChild(eMin);
1444
1445 QFile f;
1446 f.setFileName(szFile);
1447 if(!f.open(QIODevice::WriteOnly))
1448 {
1449 qCritical(log)
1450 << "CFrmUpdater::GenerateUpdateXml file open file fail:"
1451 << f.fileName();
1452 return -1;
1453 }
1454 QTextStream stream(&f);
1455#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1456 stream.setCodec("UTF-8");
1457#endif
1458 doc.save(stream, 4);
1459 f.close();
1460 return 0;
1461}
1462
1464{
1465 QCommandLineParser parser;
1466 int nRet = GenerateUpdateXml(parser);
1467 parser.process(qApp->arguments());
1468 return nRet;
1469}
1470
1471int CFrmUpdater::GenerateUpdateXml(QCommandLineParser &parser)
1472{
1473 QString szFile;
1474 CONFIG_INFO info;
1475 CONFIG_TYPE type;
1476 int nRet = GetConfigFromCommandLine(parser, szFile, info, type);
1477 if(nRet)
1478 return nRet;
1479 return GenerateUpdateXmlFile(szFile + ".xml", info, type);
1480}
1481
1491int CFrmUpdater::GetConfigFromCommandLine(/*[in]*/QCommandLineParser &parser,
1492 /*[out]*/QString &szFile,
1493 /*[out]*/CONFIG_INFO &info,
1494 /*[out]*/CONFIG_TYPE &type)
1495{
1496 QString szSystem = QSysInfo::productType();
1497#if defined(Q_OS_LINUX)
1498 QString szAppImage = QString::fromLocal8Bit(qgetenv("APPIMAGE"));
1499 if(!szAppImage.isEmpty())
1500 szSystem = "AppImage";
1501#endif
1502
1503 QString szFileName;
1504#if defined (Q_OS_WIN)
1505 szFileName = qApp->applicationName() + "_" + m_szCurrentVersion + "_Setup" + ".exe";
1506#elif defined(Q_OS_ANDROID)
1507 szFileName = qApp->applicationName().toLower() + "_" + m_szCurrentVersion + ".apk";
1508#elif defined(Q_OS_LINUX)
1509 QFileInfo f;
1510 if(szAppImage.isEmpty())
1511 f = QFileInfo(qApp->applicationFilePath());
1512 else
1513 f = QFileInfo(szAppImage);
1514 if(f.suffix().compare("AppImage", Qt::CaseInsensitive))
1515 {
1516 QString szVersion = m_szCurrentVersion;
1517 szVersion.replace("v", "", Qt::CaseInsensitive);
1518 szFileName = qApp->applicationName().toLower()
1519 + "_" + szVersion + "_amd64.deb";
1520 } else {
1521 szFileName = qApp->applicationName()
1522 + "_" + m_szCurrentVersion + ".AppImage";
1523 }
1524#endif
1525
1526 QString szUrl;
1527 szUrl = "https://github.com/KangLin/"
1528 + qApp->applicationName()
1529 + "/releases/download/"
1530 + m_szCurrentVersion + "/" + szFileName;
1531
1532 parser.addHelpOption();
1533 parser.addVersionOption();
1534
1535 QCommandLineOption oFile(QStringList() << "f" << "file",
1536 tr("Configure file name"),
1537 "Configure file name",
1538 "update.json");
1539 parser.addOption(oFile);
1540 QCommandLineOption oFileOuputContent(QStringList() << "foc" << "file-output-content",
1541 tr("Configure file output content:") + "\n"
1542 + QString::number(static_cast<int>(CONFIG_TYPE::VERSION)) + tr(": content is version") + "\n"
1543 + QString::number(static_cast<int>(CONFIG_TYPE::FILE)) + tr(": content is file") + "\n"
1544 + QString::number(static_cast<int>(CONFIG_TYPE::VERSION_FILE)) + tr(": content is version and file"),
1545 "Configure file output content",
1546 QString::number(static_cast<int>(CONFIG_TYPE::VERSION_FILE)));
1547 parser.addOption(oFileOuputContent);
1548 QCommandLineOption oPackageVersion(QStringList() << "pv" << "package-version",
1549 tr("Package version"),
1550 "Package version",
1551 m_szCurrentVersion);
1552 parser.addOption(oPackageVersion);
1553 QCommandLineOption oTime(QStringList() << "t" << "time",
1554 tr("Time"),
1555 "Time",
1556 QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
1557 parser.addOption(oTime);
1558 QCommandLineOption oInfo(QStringList() << "i" << "info",
1559 tr("Information"),
1560 "Information",
1561 qApp->applicationName() + " " + m_szCurrentVersion);
1562 parser.addOption(oInfo);
1563
1564 QCommandLineOption oSystem(QStringList() << "s" << "system",
1565 tr("Operating system"),
1566 "Operating system",
1567 szSystem);
1568 parser.addOption(oSystem);
1569 QCommandLineOption oArch(QStringList() << "a" << "arch",
1570 tr("Architecture"),
1571 "Architecture",
1572 QSysInfo::buildCpuArchitecture());
1573 parser.addOption(oArch);
1574 QCommandLineOption oMd5(QStringList() << "c" << "md5",
1575 tr("MD5 checksum"),
1576 "MD5 checksum");
1577 parser.addOption(oMd5);
1578 QCommandLineOption oPackageFile(QStringList() << "p" << "pf" << "package-file",
1579 tr("Package file, Is used to calculate md5sum"),
1580 "Package file"
1581 );
1582 parser.addOption(oPackageFile);
1583 QCommandLineOption oFileName(QStringList() << "n" << "file-name",
1584 tr("File name"),
1585 "File name"
1586 );
1587 parser.addOption(oFileName);
1588 QCommandLineOption oUrl(QStringList() << "u" << "urls",
1589 tr("Package download urls"),
1590 "Download urls",
1591 szUrl);
1592 parser.addOption(oUrl);
1593 QString szHome = "https://github.com/KangLin/" + qApp->applicationName();
1594 QCommandLineOption oUrlHome("home",
1595 tr("Project home url"),
1596 "Project home url",
1597 szHome);
1598 parser.addOption(oUrlHome);
1599 QCommandLineOption oMin(QStringList() << "m" << "min" << "min-update-version",
1600 tr("Min update version"),
1601 "Min update version",
1602 m_szCurrentVersion);
1603 parser.addOption(oMin);
1604 QCommandLineOption oForce(QStringList() << "force",
1605 tr("Set force flag"),
1606 "Force flag",
1607 "false");
1608 parser.addOption(oForce);
1609
1610 if(!parser.parse(QApplication::arguments())) {
1611 qDebug(log) << "parser.parse fail" << parser.errorText()
1612 << qApp->arguments();
1613 }
1614
1615 szFile = parser.value(oFile);
1616 if(szFile.isEmpty())
1617 qDebug(log) << "File is empty";
1618
1619 type = static_cast<CONFIG_TYPE>(parser.value(oFileOuputContent).toInt());
1620 qDebug(log) << "File content is:" << (int)type;
1621
1622 info.version.szVerion = parser.value(oPackageVersion);
1623 info.version.szMinUpdateVersion = parser.value(oMin);
1624 info.version.szTime = parser.value(oTime);
1625 info.version.szInfomation = parser.value(oInfo);
1626 info.version.szHome = parser.value(oUrlHome);
1627 QString szForce = parser.value(oForce).trimmed();
1628 if(szForce.compare("true", Qt::CaseInsensitive))
1629 info.version.bForce = false;
1630 else
1631 info.version.bForce = true;
1632
1633 CONFIG_FILE file;
1634 file.szSystem = parser.value(oSystem);
1635 file.szArchitecture = parser.value(oArch);
1636 file.szMd5sum = parser.value(oMd5);
1637 file.szFileName = parser.value(oFileName);
1638
1639 QString szPackageFile = parser.value(oPackageFile);
1640 if(!szPackageFile.isEmpty()) {
1641 QFileInfo fi(szPackageFile);
1642 if(file.szFileName.isEmpty())
1643 file.szFileName = fi.fileName();
1644 if(file.szMd5sum.isEmpty())
1645 {
1646 //计算包的 MD5 和
1647 QCryptographicHash md5sum(QCryptographicHash::Md5);
1648 QFile app(szPackageFile);
1649 if(app.open(QIODevice::ReadOnly))
1650 {
1651 if(md5sum.addData(&app))
1652 {
1653 file.szMd5sum = md5sum.result().toHex();
1654 }
1655 app.close();
1656 } else {
1657 qCritical(log) << "Don't open package file:" << szPackageFile;
1658 }
1659 }
1660 }
1661
1662 if(file.szMd5sum.isEmpty())
1663 qWarning(log) << "Md5 is empty. please set -c or --md5 or -p";
1664
1665 /* 注意:这里要放在包文件后。
1666 * 优先级:
1667 * 1. -n 参数设置
1668 * 2. 从包文件中提取
1669 * 3. 默认值
1670 */
1671 if(file.szFileName.isEmpty())
1672 file.szFileName = szFileName;
1673
1674 QString szUrls = parser.value(oUrl);
1675 foreach(auto u, szUrls.split(QRegularExpression("[;,]")))
1676 {
1677 file.urls.push_back(QUrl(u));
1678 }
1679
1680 info.files.append(file);
1681
1682 return 0;
1683}
1684
1685void CFrmUpdater::showEvent(QShowEvent *event)
1686{
1687 Q_UNUSED(event)
1688 if(!m_StateMachine->isRunning())
1689 m_StateMachine->start();
1690}
1691
1692void CFrmUpdater::slotShowWindow(QSystemTrayIcon::ActivationReason reason)
1693{
1694 Q_UNUSED(reason)
1695#if defined(Q_OS_ANDROID)
1696 showMaximized();
1697#else
1698 show();
1699#endif
1700 m_TrayIcon.hide();
1701}
1702
1703bool CFrmUpdater::CheckPrompt(const QString &szVersion)
1704{
1705 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1706 QSettings::IniFormat);
1707 QString version = set.value("Updater/Version", m_szCurrentVersion).toString();
1708 set.setValue("Updater/Version", szVersion);
1709 int nRet = CompareVersion(szVersion, version);
1710 if (nRet > 0)
1711 return true;
1712 else if(nRet == 0)
1713 return !ui->cbPrompt->isChecked();
1714 return false;
1715}
1716
1717void CFrmUpdater::on_cbPrompt_clicked(bool checked)
1718{
1719 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1720 QSettings::IniFormat);
1721 set.setValue("Updater/Prompt", checked);
1722}
1723
1724void CFrmUpdater::on_cbHomePage_clicked(bool checked)
1725{
1726 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1727 QSettings::IniFormat);
1728 set.setValue("Updater/ShowHomePage", checked);
1729}
1730
1732{
1733 m_InstallAutoStartupType = bAutoStart;
1734 return 0;
1735}
Updater.
Definition FrmUpdater.h:75
CFrmUpdater(QVector< QUrl > urls=QVector< QUrl >(), QWidget *parent=nullptr)
CFrmUpdater.
int GenerateUpdateJson()
Generate update json configure file.
int CheckRedirectConfigFile()
检查重定向配置文件
int GetConfigFromCommandLine(QCommandLineParser &parser, QString &szFile, CONFIG_INFO &info, CONFIG_TYPE &type)
Get configure from command-line.
bool IsDownLoad()
Check file is exist.
int InitStateMachine()
Initialization state machine.
int GenerateJsonFile(const QString &szFile, const CONFIG_INFO &info, CONFIG_TYPE type)
Generate Json File.
Q_DECL_DEPRECATED int GenerateUpdateXml()
Update XML file used only to generate programs.
int SetTitle(QImage icon=QImage(), const QString &szTitle=QString())
SetTitle.
int CompareVersion(const QString &newVersion, const QString &currentVersion)
CFrmUpdater::CompareVersion.
int SetInstallAutoStartup(bool bAutoStart=true)
Set install and automation startup.
int GetConfigFromFile(const QString &szFile, CONFIG_INFO &conf)
json 格式:
int GetRedirectFromFile(const QString &szFile, QVector< CONFIG_REDIRECT > &conf)
Get redirect configure from file.
int CheckUpdateConfigFile()
检查更新配置文件
Download the same file from multiple URLs.
Definition Download.h:48
static bool executeByRoot(const QString &program, const QStringList &arguments=QStringList())
executeByRoot: Run with administrator privileges