5#include <QStandardPaths>
7#include <QJsonDocument>
10#include <QDomDocument>
16#include <QDesktopServices>
17#include <QInputDialog>
21#include <QLoggingCategory>
22#include <QRegularExpression>
23#include <QStateMachine>
26#include "FrmUpdater.h"
27#include "RabbitCommonDir.h"
28#include "RabbitCommonTools.h"
29#include "ui_FrmUpdater.h"
31static Q_LOGGING_CATEGORY(log,
"RabbitCommon.Updater")
36 m_InstallAutoStartupType(false),
39 m_StateMachine(
nullptr),
40 m_pStateDownloadSetupFile(
nullptr),
44 setAttribute(Qt::WA_DeleteOnClose);
46 ui->lbNewArch->hide();
47 ui->lbNewVersion->hide();
48 ui->progressBar->hide();
49 ui->cbHomePage->hide();
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());
57 check = connect(&m_TrayIcon,
58 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
60 SLOT(slotShowWindow(QSystemTrayIcon::ActivationReason)));
62 m_TrayIcon.setIcon(this->windowIcon());
63 m_TrayIcon.setToolTip(windowTitle() +
" - "
64 + qApp->applicationDisplayName());
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)),
76 SIGNAL(buttonClicked(
int)),
78 this, SLOT(slotButtonClickd(
int)));
82 ui->lbCurrentArch->setText(tr(
"Current architecture: %1")
83 .arg(QSysInfo::currentCpuArchitecture()));
85 QString szVerion = qApp->applicationVersion();
86#ifdef RabbitCommon_VERSION
87 if(szVerion.isEmpty())
88 szVerion = RabbitCommon_VERSION;
94 if(QSslSocket::supportsSsl())
97#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
98 szMsg =
"Build Version: " + QSslSocket::sslLibraryBuildVersionString();
100 szMsg +=
"; Installed Version: " + QSslSocket::sslLibraryVersionString();
101 qInfo(log) <<
"QSslSocket support ssl:" << szMsg;
104#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
105 szMsg =
"BuildVersion: " + QSslSocket::sslLibraryBuildVersionString();
108 "QSslSocket is not support ssl. The system is not install the OPENSSL dynamic library[" << szMsg <<
"]."
109 " Please install OPENSSL dynamic library [" << szMsg <<
"]";
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");
127 m_Urls << github << gitee << sourceforge << gitlab;
131 qDebug(log) <<
"Urls:" << m_Urls;
135CFrmUpdater::~CFrmUpdater()
137 qDebug(log) <<
"CFrmUpdater::~CFrmUpdater()";
138 m_DownloadFile.close();
142 m_StateMachine->deleteLater();
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);
208 bool check = connect(sFinal, SIGNAL(entered()),
209 this, SLOT(slotStateFinished()));
212 sCheck->addTransition(
this, SIGNAL(sigError()), sFinal);
213 sCheck->addTransition(
this, SIGNAL(sigFinished()), s);
214 check = connect(sCheck, SIGNAL(entered()),
this, SLOT(slotCheck()));
217 s->addTransition(
this, SIGNAL(sigError()), sFinal);
218 s->addTransition(
this, SIGNAL(sigFinished()), sFinal);
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()));
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()));
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()));
240 sUpdate->assignProperty(ui->lbState,
"text", tr(
"Installing update"));
241 check = connect(sUpdate, SIGNAL(entered()),
this, SLOT(slotUpdate()));
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();
256 QString title = szTitle;
257 if(szTitle.isEmpty())
258 title = qApp->applicationDisplayName();
259 ui->lbTitle->setText(title);
261 QPixmap pixmpa = QPixmap::fromImage(icon);
263 pixmpa.load(
":/icon/RabbitCommon/App",
"PNG");
264 ui->lbTitleIcon->setPixmap(pixmpa);
268int CFrmUpdater::SetVersion(
const QString &szVersion)
270 m_szCurrentVersion = szVersion;
271 ui->lbCurrentVersion->setText(tr(
"Current version: %1")
272 .arg(m_szCurrentVersion));
276void CFrmUpdater::slotStateFinished()
278 qDebug(log) <<
"slotStateFinished()";
283void CFrmUpdater::slotCheck()
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());
297 if(ui->rbEveryDate->isChecked())
299 else if(ui->rbEveryWeek->isChecked())
301 else if(ui->rbEveryMonth->isChecked())
304 if(n <= d.daysTo(QDateTime::currentDateTime()))
311void CFrmUpdater::slotDownloadError(
int nErr,
const QString szError)
313 ui->progressBar->hide();
314 ui->progressBar->setRange(0, 100);;
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;
324void CFrmUpdater::slotDownloadFileFinished(
const QString szFile)
326 qDebug(log) <<
"slotDownloadFileFinished:" << szFile;
327 ui->progressBar->hide();
328 ui->progressBar->setRange(0, 100);;
329 if(m_DownloadFile.isOpen())
330 m_DownloadFile.close();
333 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
334 szTmp = szTmp + QDir::separator() +
"Rabbit"
335 + QDir::separator() + qApp->applicationName();
337 QString szFileName(m_ConfigFile.szFileName);
338 if(szFileName.isEmpty())
340 szFileName = szFile.mid(szFile.lastIndexOf(
"/"));
342 if(szFileName.left(1) !=
"/" && szFileName.left(1) !=
"\\")
343 szFileName = QDir::separator() + szFileName;
344 QString f = szTmp + szFileName;
348 if(QFile::copy(szFile, f))
350 if(QFile::rename(szFile, f))
353 m_DownloadFile.setFileName(f);
354 qInfo(log) <<
"Download finished: rename"
355 << szFile <<
"to" << f;
357 qCritical(log) <<
"Download finished. rename fail from"
358 << szFile <<
"to" << f;
359 m_DownloadFile.setFileName(szFile);
364void CFrmUpdater::slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
366 if(ui->progressBar->isHidden())
368 ui->progressBar->show();
369 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
371 if(ui->progressBar->maximum() != bytesTotal)
372 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
374 ui->progressBar->setValue(
static_cast<int>(bytesReceived));
376 QString szInfo = tr(
"Downloading %1% [%2/%3]")
377 .arg(QString::number(bytesReceived * 100 / bytesTotal))
378 .arg(QString::number(bytesReceived)).arg(QString::number(bytesTotal));
380 m_TrayIcon.setToolTip(windowTitle() +
" - "
381 + qApp->applicationDisplayName()
387void CFrmUpdater::slotDownloadFile()
389 qDebug(log) <<
"CFrmUpdater::slotDownloadFile";
391 if(!m_Urls.isEmpty())
393 m_Download = QSharedPointer<RabbitCommon::CDownload>(
395 bool check = connect(m_Download.data(), SIGNAL(sigFinished(
const QString)),
396 this, SLOT(slotDownloadFileFinished(
const QString)));
398 check = connect(m_Download.data(), SIGNAL(sigError(
int,
const QString)),
399 this, SLOT(slotDownloadError(
int,
const QString)));
401 check = connect(m_Download.data(), SIGNAL(sigDownloadProgress(qint64, qint64)),
402 this, SLOT(slotDownloadProgress(qint64, qint64)));
404 m_Download->Start(m_Urls);
409void CFrmUpdater::slotCheckConfigFile()
411 m_TrayIcon.setToolTip(windowTitle() +
" - "
412 + qApp->applicationDisplayName());
413 qDebug(log) <<
"CFrmUpdater::slotCheckConfigFile()";
418 if(nRet <= 0)
return;
421 QString szText(tr(
"There is laster version"));
422 ui->lbState->setText(szText);
423 qInfo(log) << szText;
448 qDebug(log) <<
"CFrmUpdater::CheckRedirectConfigFile()"
449 << m_DownloadFile.fileName();
451 QVector<CONFIG_REDIRECT> conf;
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;
465 for(
auto it = conf.begin(); it != conf.end(); it++) {
467 QString szVersion = it->szVersion;
468 if(szVersion.isEmpty())
470 QString szError = tr(
"Failed:") + tr(
"Configure file content error:")
471 + m_DownloadFile.fileName();
472 ui->lbState->setText(szError);
473 qCritical(log) << szError;
481 QString szMinVersion = it->szMinUpdateVersion;
482 if(!szMinVersion.isEmpty()) {
491 if(redirect.szVersion.isEmpty())
495 foreach (
auto f, redirect.files) {
496 if(!f.szSystem.isEmpty()) {
497 if(QSysInfo::productType() != f.szSystem)
500 if(!f.szArchitecture.isEmpty())
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))
508 if(szArchitecture != f.szArchitecture)
519 if(redirect.files.isEmpty()) {
521 QUrl github(
"https://github.com/KangLin/"
522 + qApp->applicationName() +
"/releases/download/"
523 + redirect.szVersion +
"/update.json");
524 m_Urls.push_back(github);
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);
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);
546 qInfo(log) <<
"Redirect. Version:" << redirect.szVersion << m_Urls;
548 emit sigDownLoadRedire();
588 qDebug(log) <<
"CFrmUpdater::CheckUpdateConfigFile()";
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;
600 if(
CompareVersion(info.version.szVerion, m_szCurrentVersion) <= 0)
603 szError = tr(
"There is laster version");
604 ui->lbState->setText(szError);
605 qInfo(log) << szError;
610 if(info.files.isEmpty()) {
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;
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";
626 QString szArchitecture = QSysInfo::currentCpuArchitecture();
628 foreach (
auto f, info.files) {
629 if(f.szSystem.compare(szSystem, Qt::CaseInsensitive))
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))
649 if(file.szSystem.compare(szSystem, Qt::CaseInsensitive)) {
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;
660 m_Info.version = info.version;
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)
670 qDebug(log) <<
"Force update";
675 ui->cbHomePage->show();
676 ui->cbPrompt->show();
677 ui->pbOK->setText(tr(
"OK(&O)"));
679 if(!CheckPrompt(info.version.szVerion) && this->isHidden())
702 if(!f.open(QFile::ReadOnly))
704 QString szError = tr(
"Open file fail").arg(szFile);
705 qCritical(log) << szError;
710 doc = QJsonDocument::fromJson(f.readAll());
714 QString szError = tr(
"Parse file %1 fail. It isn't configure file")
716 qCritical(log) << szError;
720 QJsonObject objRoot = doc.object();
721 if(!objRoot.contains(
"redirect"))
724 QJsonArray arrRedirect = objRoot[
"redirect"].toArray();
725 for(
auto it = arrRedirect.begin(); it != arrRedirect.end(); it++) {
726 QJsonObject obj = it->toObject();
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;
733 QJsonArray objFiles = obj[
"files"].toArray();
734 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
735 QJsonObject f = it->toObject();
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();
744 QJsonArray urls = f[
"urls"].toArray();
745 foreach(
auto u, urls)
747 file.urls.append(u.toString());
750 objRedirect.files.append(file);
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;
761 conf.append(objRedirect);
822 if(!file.open(QFile::ReadOnly)) {
823 qDebug(log) <<
"The file isn't opened:" << szFile;
828 doc = QJsonDocument::fromJson(file.readAll());
832 qCritical(log) <<
"Parser configure file fail." << szFile;
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();
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
856 if(!obj.contains(
"files")) {
857 qDebug(log) <<
"Configure file isn't contains files array";
861 QJsonArray objFiles = obj[
"files"].toArray();
862 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
863 QJsonObject f = it->toObject();
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();
872 QJsonArray urls = f[
"urls"].toArray();
873 foreach(
auto u, urls)
875 file.urls.append(u.toString());
878 conf.files.append(file);
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;
892void CFrmUpdater::slotDownloadSetupFile()
894 qDebug(log) <<
"CFrmUpdater::slotDownloadSetupFile()";
895 ui->pbOK->setText(tr(
"Hide"));
896 ui->lbState->setText(tr(
"Downloading ......"));
901 m_Urls = m_ConfigFile.urls;
906void CFrmUpdater::slotUpdate()
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);;
916 bool bSuccess =
false;
918 if(!m_DownloadFile.open(QIODevice::ReadOnly))
921 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
922 + m_DownloadFile.fileName();
923 qCritical(log) << szErr;
924 ui->lbState->setText(szErr);
927 QCryptographicHash md5sum(QCryptographicHash::Md5);
928 if(!md5sum.addData(&m_DownloadFile))
931 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
932 + m_DownloadFile.fileName();
933 qCritical(log) << szErr;
934 ui->lbState->setText(szErr);
937 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
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;
952 m_DownloadFile.close();
960 int nRet = Execute(m_DownloadFile.fileName());
962 ui->lbState->setText(tr(
"The installer has started, Please close the application"));
966 QString szHome = m_Info.version.szHome;
967 if((nRet || ui->cbHomePage->isChecked()) && !szHome.isEmpty())
968 if(!procHome.startDetached(szHome))
971 if(!QDesktopServices::openUrl(url))
973 QString szErr = tr(
"Failed:") + tr(
"Open home page fail");
974 ui->lbState->setText(szErr);
987 if(!QDesktopServices::openUrl(url))
989 QString szErr = tr(
"Open home page fail");
990 qCritical(log) << szErr;
994int CFrmUpdater::Execute(
const QString szFile)
1000 int nRet = m_pcbUpdate(szFile);
1017 QFileInfo fi(szFile);
1018 if(!fi.suffix().compare(
"AppImage", Qt::CaseInsensitive)) {
1020 QString szAppImage = QString::fromLocal8Bit(qgetenv(
"APPIMAGE"));
1022 if(!szAppImage.isEmpty()) {
1023 QFile f(fi.filePath());
1024 QFileInfo cf(szAppImage);
1026 szExec = cf.absoluteDir().absolutePath()
1027 + QDir::separator() + fi.fileName();
1028 bRet = f.copy(szExec);
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))
1037 szErr = tr(
"Failed:") + tr(
"Open the folder fail: ")
1038 + cf.absoluteDir().absolutePath();
1039 qCritical(log) << szErr;
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))
1051 szErr = tr(
"Failed:") + tr(
"Open the folder fail: ")
1052 + fi.absoluteDir().absolutePath();
1053 qCritical(log) << szErr;
1057 }
else if(!fi.suffix().compare(
"deb", Qt::CaseInsensitive)
1058 || !fi.suffix().compare(
"rpm", Qt::CaseInsensitive)) {
1060 QStringList lstPara;
1061 lstPara <<
"install" <<
"-y" << szFile;
1062 QString szCmd =
"apt";
1063 if(!fi.suffix().compare(
"rpm", Qt::CaseInsensitive))
1065 bool bRet = RabbitCommon::CTools::ExecuteWithAdministratorPrivilege(szCmd, lstPara,
false);
1067 qCritical(log) <<
"Execute:" << szCmd << lstPara
1068 <<
"fail. nRet:" << nRet;
1070 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
1073 if(!QDesktopServices::openUrl(url))
1075 QString szErr = tr(
"Failed:")
1076 + tr(
"Execute install program error.%1")
1078 ui->lbState->setText(szErr);
1079 qCritical(log) << szErr;
1084 qDebug(log) <<
"Success: Install" << szCmd << lstPara;
1086 }
else if(!fi.suffix().compare(
"gz", Qt::CaseInsensitive)) {
1088 QString szInstall = fi.absolutePath() + QDir::separator() +
"setup.sh";
1090 if(!f.open(QFile::WriteOnly))
1092 QString szErr = tr(
"Failed:")
1093 + tr(
"Open file %1 fail").arg(fi.absolutePath());
1094 ui->lbState->setText(szErr);
1098 QString szCmd = InstallScript(szFile, qApp->applicationName());
1099 f.write(szCmd.toStdString().c_str());
1100 qDebug(log) << szCmd << szInstall;
1104 if(!RabbitCommon::CTools::ExecuteWithAdministratorPrivilege(
1106 QStringList() << szInstall))
1108 QString szErr = tr(
"Failed:") + tr(
"Execute") +
"/bin/bash "
1109 + szInstall +
"fail";
1110 ui->lbState->setText(szErr);
1141 qInfo(log) <<
"Start"
1143 <<
"in a new process, and detaches from it.";
1144 if(!proc.startDetached(szCmd))
1146 qInfo(log) <<
"Start new process fail."
1147 <<
"Use system installer to install"
1150 if(!QDesktopServices::openUrl(url))
1152 QString szErr = tr(
"Failed:")
1153 + tr(
"Execute install program error.%1")
1155 ui->lbState->setText(szErr);
1170QString CFrmUpdater::InstallScript(
const QString szDownLoadFile,
1171 const QString szApplicationName)
1173 QFileInfo fi(szDownLoadFile);
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";
1180 szCmd +=
"cd /opt/" + szApplicationName +
"\n";
1181 szCmd +=
"if [ -f install1.sh ]; then\n";
1182 szCmd +=
" ./install1.sh remove " + szApplicationName +
"\n";
1185 szCmd +=
"cp " + szDownLoadFile +
" ." +
"\n";
1186 szCmd +=
"tar xvfz " + fi.fileName() +
"\n";
1187 szCmd +=
"rm " + fi.fileName() +
"\n";
1190 szCmd +=
"./install1.sh ";
1191 if(m_InstallAutoStartupType)
1192 szCmd +=
"install_autostart";
1196 int nRet = QMessageBox::information(
this, tr(
"Run"),
1197 tr(
"Run after install"),
1198 QMessageBox::Yes|QMessageBox::No,
1200 if(QMessageBox::Yes == nRet)
1204 szCmd +=
" " + szApplicationName +
"\n";
1221 QString sN = newVersion;
1222 QString sC = currentVersion;
1224 if(sN.isEmpty() || sC.isEmpty())
1225 return sN.length() - sC.length();
1227 sN = sN.replace(QRegularExpression(
"[-~_]"),
".");
1228 sC = sC.replace(QRegularExpression(
"[-~_]"),
".");
1230 QStringList szNew = sN.split(
".");
1231 QStringList szCur = sC.split(
".");
1233 int count = qMin(szNew.length(), szCur.length());
1234 qDebug(log) <<
"count:" << count;
1237 return szNew.length() - szCur.length();
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();
1248 return sN.length() - sC.length();
1249 nRet = szNew.at(1).toInt() - szCur.at(1).toInt();
1254 return sN.length() - sC.length();
1255 nRet = szNew.at(2).toInt() - szCur.at(2).toInt();
1260 return sC.length() - sN.length();
1261 return szNew.at(3).toInt() - szCur.at(3).toInt();
1272 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
1273 szTmp = szTmp + QDir::separator() +
"Rabbit"
1274 + QDir::separator() + qApp->applicationName();
1276 QString szFile = szTmp + QDir::separator() + m_ConfigFile.szFileName;
1279 if(!f.open(QIODevice::ReadOnly))
1282 m_DownloadFile.setFileName(szFile);
1284 QCryptographicHash md5sum(QCryptographicHash::Md5);
1285 if(!md5sum.addData(&f))
1290 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
1305void CFrmUpdater::on_pbOK_clicked()
1307 qDebug(log) <<
"CFrmUpdater::on_pbOK_clicked()";
1308 if(!m_pStateDownloadSetupFile->active())
1315void CFrmUpdater::on_pbClose_clicked()
1317 if(m_StateMachine->isRunning())
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)
1330void CFrmUpdater::slotButtonClickd(
int id)
1332 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(), QSettings::IniFormat);
1333 set.setValue(
"Update/RadioButton",
id);
1338 QCommandLineParser parser;
1340 parser.process(qApp->arguments());
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);
1376 foreach (
auto f, info.files) {
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);
1387 foreach (
auto u, f.urls) {
1388 urls.append(u.toString());
1390 file.insert(
"urls", urls);
1395 case CONFIG_TYPE::VERSION:
1396 doc.setObject(version);
1398 case CONFIG_TYPE::FILE:
1399 doc.setObject(files[0].toObject());
1401 case CONFIG_TYPE::VERSION_FILE:
1404 root.insert(
"version", version);
1405 root.insert(
"files", files);
1406 doc.setObject(root);
1413 if(!f.open(QIODevice::WriteOnly))
1415 qCritical(log) <<
"Open file fail:" << f.fileName();
1418 f.write(doc.toJson());
1423int CFrmUpdater::GenerateUpdateXmlFile(
const QString &szFile,
const CONFIG_INFO &info, CONFIG_TYPE &type)
1426 QDomProcessingInstruction ins;
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);
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);
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);
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);
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);
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);
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);
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);
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);
1482 foreach(
auto u, file.urls)
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);
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);
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);
1504 f.setFileName(szFile);
1505 if(!f.open(QIODevice::WriteOnly))
1508 <<
"CFrmUpdater::GenerateUpdateXml file open file fail:"
1512 QTextStream stream(&f);
1513#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1514 stream.setCodec(
"UTF-8");
1516 doc.save(stream, 4);
1523 QCommandLineParser parser;
1525 parser.process(qApp->arguments());
1537 return GenerateUpdateXmlFile(szFile +
".xml", info, type);
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";
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)
1568 if(szAppImage.isEmpty())
1569 f = QFileInfo(qApp->applicationFilePath());
1571 f = QFileInfo(szAppImage);
1572 if(f.suffix().compare(
"AppImage", Qt::CaseInsensitive))
1574 QString szVersion = m_szCurrentVersion;
1575 szVersion.replace(
"v",
"", Qt::CaseInsensitive);
1576 szFileName = qApp->applicationName().toLower()
1577 +
"_" + szVersion +
"_amd64.deb";
1579 szFileName = qApp->applicationName()
1580 +
"_" + m_szCurrentVersion +
".AppImage";
1585 szUrl =
"https://github.com/KangLin/"
1586 + qApp->applicationName()
1587 +
"/releases/download/"
1588 + m_szCurrentVersion +
"/" + szFileName;
1590 parser.addHelpOption();
1591 parser.addVersionOption();
1593 QCommandLineOption oFile(QStringList() <<
"f" <<
"file",
1594 tr(
"Configure file name"),
1595 "Configure file name",
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"),
1609 m_szCurrentVersion);
1610 parser.addOption(oPackageVersion);
1611 QCommandLineOption oTime(QStringList() <<
"t" <<
"time",
1614 QDateTime::currentDateTime().toString(
"yyyy-MM-dd hh:mm:ss"));
1615 parser.addOption(oTime);
1616 QCommandLineOption oInfo(QStringList() <<
"i" <<
"info",
1619 qApp->applicationName() +
" " + m_szCurrentVersion);
1620 parser.addOption(oInfo);
1622 QCommandLineOption oSystem(QStringList() <<
"s" <<
"system",
1623 tr(
"Operating system"),
1626 parser.addOption(oSystem);
1627 QCommandLineOption oArch(QStringList() <<
"a" <<
"arch",
1630 QSysInfo::buildCpuArchitecture());
1631 parser.addOption(oArch);
1632 QCommandLineOption oMd5(QStringList() <<
"c" <<
"md5",
1635 parser.addOption(oMd5);
1636 QCommandLineOption oPackageFile(QStringList() <<
"p" <<
"pf" <<
"package-file",
1637 tr(
"Package file, Is used to calculate md5sum"),
1640 parser.addOption(oPackageFile);
1641 QCommandLineOption oFileName(QStringList() <<
"n" <<
"file-name",
1645 parser.addOption(oFileName);
1646 QCommandLineOption oUrl(QStringList() <<
"u" <<
"urls",
1647 tr(
"Package download urls"),
1650 parser.addOption(oUrl);
1651 QString szHome =
"https://github.com/KangLin/" + qApp->applicationName();
1652 QCommandLineOption oUrlHome(
"home",
1653 tr(
"Project home url"),
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"),
1666 parser.addOption(oForce);
1668 if(!parser.parse(QApplication::arguments())) {
1669 qDebug(log) <<
"parser.parse fail" << parser.errorText()
1670 << qApp->arguments();
1673 szFile = parser.value(oFile);
1674 if(szFile.isEmpty())
1675 qDebug(log) <<
"File is empty";
1677 type =
static_cast<CONFIG_TYPE
>(parser.value(oFileOuputContent).toInt());
1678 qDebug(log) <<
"File content is:" << (int)type;
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;
1689 info.version.bForce =
true;
1692 file.szSystem = parser.value(oSystem);
1693 file.szArchitecture = parser.value(oArch);
1694 file.szMd5sum = parser.value(oMd5);
1695 file.szFileName = parser.value(oFileName);
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())
1705 QCryptographicHash md5sum(QCryptographicHash::Md5);
1706 QFile app(szPackageFile);
1707 if(app.open(QIODevice::ReadOnly))
1709 if(md5sum.addData(&app))
1711 file.szMd5sum = md5sum.result().toHex();
1715 qCritical(log) <<
"Don't open package file:" << szPackageFile;
1720 if(file.szMd5sum.isEmpty())
1721 qWarning(log) <<
"Md5 is empty. please set -c or --md5 or -p";
1729 if(file.szFileName.isEmpty())
1730 file.szFileName = szFileName;
1732 QString szUrls = parser.value(oUrl);
1733 foreach(
auto u, szUrls.split(QRegularExpression(
"[;,]")))
1735 file.urls.push_back(QUrl(u));
1738 info.files.append(file);
1743void CFrmUpdater::showEvent(QShowEvent *event)
1746 if(!m_StateMachine->isRunning())
1747 m_StateMachine->start();
1750void CFrmUpdater::slotShowWindow(QSystemTrayIcon::ActivationReason reason)
1753#if defined(Q_OS_ANDROID)
1761bool CFrmUpdater::CheckPrompt(
const QString &szVersion)
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);
1771 return !ui->cbPrompt->isChecked();
1775void CFrmUpdater::on_cbPrompt_clicked(
bool checked)
1777 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1778 QSettings::IniFormat);
1779 set.setValue(
"Updater/Prompt", checked);
1782void CFrmUpdater::on_cbHomePage_clicked(
bool checked)
1784 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1785 QSettings::IniFormat);
1786 set.setValue(
"Updater/ShowHomePage", checked);
1791 m_InstallAutoStartupType = bAutoStart;
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 ¤tVersion)
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.