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