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)
43 setAttribute(Qt::WA_DeleteOnClose);
45 ui->lbNewArch->hide();
46 ui->lbNewVersion->hide();
47 ui->progressBar->hide();
48 ui->cbHomePage->hide();
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());
56 check = connect(&m_TrayIcon,
57 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
59 SLOT(slotShowWindow(QSystemTrayIcon::ActivationReason)));
61 m_TrayIcon.setIcon(this->windowIcon());
62 m_TrayIcon.setToolTip(windowTitle() +
" - "
63 + qApp->applicationDisplayName());
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)),
75 SIGNAL(buttonClicked(
int)),
77 this, SLOT(slotButtonClickd(
int)));
81 ui->lbCurrentArch->setText(tr(
"Current archecture: %1")
82 .arg(QSysInfo::currentCpuArchitecture()));
84 QString szVerion = qApp->applicationVersion();
85#ifdef RabbitCommon_VERSION
86 if(szVerion.isEmpty())
87 szVerion = RabbitCommon_VERSION;
93 if(QSslSocket::supportsSsl())
96#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
97 szMsg =
"Build Version: " + QSslSocket::sslLibraryBuildVersionString();
99 szMsg +=
"; Installed Version: " + QSslSocket::sslLibraryVersionString();
100 qInfo(log) <<
"QSslSocket support ssl:" << szMsg;
103#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
104 szMsg =
"BuildVersion: " + QSslSocket::sslLibraryBuildVersionString();
107 "QSslSocket is not support ssl. The system is not install the OPENSSL dynamic library[" << szMsg <<
"]."
108 " Please install OPENSSL dynamic library [" << szMsg <<
"]";
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");
126 m_Urls << github << gitee << sourceforge << gitlab;
130 qDebug(log) <<
"Urls:" << m_Urls;
134CFrmUpdater::~CFrmUpdater()
136 qDebug(log) <<
"CFrmUpdater::~CFrmUpdater()";
137 m_DownloadFile.close();
141 m_StateMachine->deleteLater();
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);
207 bool check = connect(sFinal, SIGNAL(entered()),
208 this, SLOT(slotStateFinished()));
211 sCheck->addTransition(
this, SIGNAL(sigError()), sFinal);
212 sCheck->addTransition(
this, SIGNAL(sigFinished()), s);
213 check = connect(sCheck, SIGNAL(entered()),
this, SLOT(slotCheck()));
216 s->addTransition(
this, SIGNAL(sigError()), sFinal);
217 s->addTransition(
this, SIGNAL(sigFinished()), sFinal);
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()));
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()));
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()));
239 sUpdate->assignProperty(ui->lbState,
"text", tr(
"Being install update"));
240 check = connect(sUpdate, SIGNAL(entered()),
this, SLOT(slotUpdate()));
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();
255 QString title = szTitle;
256 if(szTitle.isEmpty())
257 title = qApp->applicationDisplayName();
258 ui->lbTitle->setText(title);
260 QPixmap pixmpa = QPixmap::fromImage(icon);
262 pixmpa.load(
":/icon/RabbitCommon/App",
"PNG");
263 ui->lbTitleIcon->setPixmap(pixmpa);
267int CFrmUpdater::SetVersion(
const QString &szVersion)
269 m_szCurrentVersion = szVersion;
270 ui->lbCurrentVersion->setText(tr(
"Current version: %1")
271 .arg(m_szCurrentVersion));
275void CFrmUpdater::slotStateFinished()
277 qDebug(log) <<
"slotStateFinished()";
282void CFrmUpdater::slotCheck()
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());
296 if(ui->rbEveryDate->isChecked())
298 else if(ui->rbEveryWeek->isChecked())
300 else if(ui->rbEveryMonth->isChecked())
303 if(n <= d.daysTo(QDateTime::currentDateTime()))
310void CFrmUpdater::slotDownloadError(
int nErr,
const QString szError)
312 ui->progressBar->hide();
313 ui->progressBar->setRange(0, 100);;
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;
323void CFrmUpdater::slotDownloadFileFinished(
const QString szFile)
325 qDebug(log) <<
"slotDownloadFileFinished:" << szFile;
326 ui->progressBar->hide();
327 ui->progressBar->setRange(0, 100);;
328 if(m_DownloadFile.isOpen())
329 m_DownloadFile.close();
332 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
333 szTmp = szTmp + QDir::separator() +
"Rabbit"
334 + QDir::separator() + qApp->applicationName();
336 QString szFileName(m_ConfigFile.szFileName);
337 if(szFileName.isEmpty())
339 szFileName = szFile.mid(szFile.lastIndexOf(
"/"));
341 if(szFileName.left(1) !=
"/" && szFileName.left(1) !=
"\\")
342 szFileName = QDir::separator() + szFileName;
343 QString f = szTmp + szFileName;
347 if(QFile::copy(szFile, f))
349 if(QFile::rename(szFile, f))
352 m_DownloadFile.setFileName(f);
353 qInfo(log) <<
"Download finished: rename"
354 << szFile <<
"to" << f;
356 qCritical(log) <<
"Download finished. rename fail from"
357 << szFile <<
"to" << f;
358 m_DownloadFile.setFileName(szFile);
363void CFrmUpdater::slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
365 if(ui->progressBar->isHidden())
367 ui->progressBar->show();
368 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
370 if(ui->progressBar->maximum() != bytesTotal)
371 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
373 ui->progressBar->setValue(
static_cast<int>(bytesReceived));
375 QString szInfo = tr(
"Downloading %1% [%2/%3]")
376 .arg(QString::number(bytesReceived * 100 / bytesTotal))
377 .arg(QString::number(bytesReceived)).arg(QString::number(bytesTotal));
379 m_TrayIcon.setToolTip(windowTitle() +
" - "
380 + qApp->applicationDisplayName()
386void CFrmUpdater::slotDownloadFile()
388 qDebug(log) <<
"CFrmUpdater::slotDownloadFile";
390 if(!m_Urls.isEmpty())
392 m_Download = QSharedPointer<RabbitCommon::CDownload>(
394 bool check = connect(m_Download.data(), SIGNAL(sigFinished(
const QString)),
395 this, SLOT(slotDownloadFileFinished(
const QString)));
397 check = connect(m_Download.data(), SIGNAL(sigError(
int,
const QString)),
398 this, SLOT(slotDownloadError(
int,
const QString)));
400 check = connect(m_Download.data(), SIGNAL(sigDownloadProgress(qint64, qint64)),
401 this, SLOT(slotDownloadProgress(qint64, qint64)));
403 m_Download->Start(m_Urls);
408void CFrmUpdater::slotCheckConfigFile()
410 m_TrayIcon.setToolTip(windowTitle() +
" - "
411 + qApp->applicationDisplayName());
412 qDebug(log) <<
"CFrmUpdater::slotCheckConfigFile()";
417 if(nRet <= 0)
return;
420 QString szText(tr(
"There is laster version"));
421 ui->lbState->setText(szText);
422 qInfo(log) << szText;
447 qDebug(log) <<
"CFrmUpdater::CheckRedirectConfigFile()"
448 << m_DownloadFile.fileName();
450 QVector<CONFIG_REDIRECT> conf;
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;
464 for(
auto it = conf.begin(); it != conf.end(); it++) {
466 QString szVersion = it->szVersion;
467 if(szVersion.isEmpty())
469 QString szError = tr(
"Failed:") + tr(
"Configure file content error:")
470 + m_DownloadFile.fileName();
471 ui->lbState->setText(szError);
472 qCritical(log) << szError;
480 QString szMinVersion = it->szMinUpdateVersion;
481 if(!szMinVersion.isEmpty()) {
490 if(redirect.szVersion.isEmpty())
494 foreach (
auto f, redirect.files) {
495 if(!f.szSystem.isEmpty()) {
496 if(QSysInfo::productType() != f.szSystem)
499 if(!f.szArchitecture.isEmpty())
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))
507 if(szArchitecture != f.szArchitecture)
518 if(redirect.files.isEmpty()) {
520 QUrl github(
"https://github.com/KangLin/"
521 + qApp->applicationName() +
"/releases/download/"
522 + redirect.szVersion +
"/update.json");
523 m_Urls.push_back(github);
525 QUrl sourceforge(
"https://sourceforge.net/projects/"
526 + qApp->applicationName() +
"/"
527 + redirect.szVersion +
"/update.json?viasf=1");
528 m_Urls.push_back(sourceforge);
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);
545 qInfo(log) <<
"Redirect. Version:" << redirect.szVersion << m_Urls;
547 emit sigDownLoadRedire();
587 qDebug(log) <<
"CFrmUpdater::CheckUpdateConfigFile()";
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;
599 if(
CompareVersion(info.version.szVerion, m_szCurrentVersion) <= 0)
602 szError = tr(
"There is laster version");
603 ui->lbState->setText(szError);
604 qInfo(log) << szError;
609 if(info.files.isEmpty()) {
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;
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";
625 QString szArchitecture = QSysInfo::currentCpuArchitecture();
627 foreach (
auto f, info.files) {
628 if(f.szSystem.compare(szSystem, Qt::CaseInsensitive))
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))
648 if(file.szSystem.compare(szSystem, Qt::CaseInsensitive)) {
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;
659 m_Info.version = info.version;
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)
669 qDebug(log) <<
"Force update";
674 ui->cbHomePage->show();
675 ui->cbPrompt->show();
676 ui->pbOK->setText(tr(
"OK(&O)"));
678 if(!CheckPrompt(info.version.szVerion) && this->isHidden())
701 if(!f.open(QFile::ReadOnly))
703 QString szError = tr(
"Open file fail").arg(szFile);
704 qCritical(log) << szError;
709 doc = QJsonDocument::fromJson(f.readAll());
713 QString szError = tr(
"Parse file %1 fail. It isn't configure file")
715 qCritical(log) << szError;
719 QJsonObject objRoot = doc.object();
720 if(!objRoot.contains(
"redirect"))
723 QJsonArray arrRedirect = objRoot[
"redirect"].toArray();
724 for(
auto it = arrRedirect.begin(); it != arrRedirect.end(); it++) {
725 QJsonObject obj = it->toObject();
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;
732 QJsonArray objFiles = obj[
"files"].toArray();
733 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
734 QJsonObject f = it->toObject();
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();
743 QJsonArray urls = f[
"urls"].toArray();
744 foreach(
auto u, urls)
746 file.urls.append(u.toString());
749 objRedirect.files.append(file);
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;
760 conf.append(objRedirect);
821 if(!file.open(QFile::ReadOnly)) {
822 qDebug(log) <<
"The file isn't opened:" << szFile;
827 doc = QJsonDocument::fromJson(file.readAll());
831 qCritical(log) <<
"Parser configure file fail." << szFile;
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();
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
855 if(!obj.contains(
"files")) {
856 qDebug(log) <<
"Configure file isn't contains files array";
860 QJsonArray objFiles = obj[
"files"].toArray();
861 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
862 QJsonObject f = it->toObject();
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();
871 QJsonArray urls = f[
"urls"].toArray();
872 foreach(
auto u, urls)
874 file.urls.append(u.toString());
877 conf.files.append(file);
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;
891void CFrmUpdater::slotDownloadSetupFile()
893 qDebug(log) <<
"CFrmUpdater::slotDownloadSetupFile()";
894 ui->pbOK->setText(tr(
"Hide"));
895 ui->lbState->setText(tr(
"Download ......"));
900 m_Urls = m_ConfigFile.urls;
905void CFrmUpdater::slotUpdate()
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);;
915 bool bSuccess =
false;
917 if(!m_DownloadFile.open(QIODevice::ReadOnly))
920 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
921 + m_DownloadFile.fileName();
922 qCritical(log) << szErr;
923 ui->lbState->setText(szErr);
926 QCryptographicHash md5sum(QCryptographicHash::Md5);
927 if(!md5sum.addData(&m_DownloadFile))
930 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
931 + m_DownloadFile.fileName();
932 qCritical(log) << szErr;
933 ui->lbState->setText(szErr);
936 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
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;
951 m_DownloadFile.close();
972 QFileInfo fi(m_DownloadFile.fileName());
973 if(!fi.suffix().compare(
"AppImage", Qt::CaseInsensitive)) {
975 QString szAppImage = QString::fromLocal8Bit(qgetenv(
"APPIMAGE"));
977 if(!szAppImage.isEmpty()) {
978 QFile f(fi.filePath());
979 QFileInfo cf(szAppImage);
981 szExec = cf.absoluteDir().absolutePath()
982 + QDir::separator() + fi.fileName();
983 bRet = f.copy(szExec);
985 QString szMsg(tr(
"Please exec:") + szExec);
986 ui->lbState->setText(szMsg);
988 QUrl url = QUrl::fromLocalFile(cf.absoluteDir().absolutePath());
989 if(!QDesktopServices::openUrl(url))
992 szErr = tr(
"Failed:") + tr(
"Open the folder fail:")
993 + cf.absoluteDir().absolutePath();
994 qCritical(log) << szErr;
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))
1006 szErr = tr(
"Failed:") + tr(
"Open the folder fail:")
1007 + fi.absoluteDir().absolutePath();
1008 qCritical(log) << szErr;
1012 }
else if(fi.suffix().compare(
"gz", Qt::CaseInsensitive)) {
1014 szCmd = m_DownloadFile.fileName();
1016 qInfo(log) <<
"Start"
1018 <<
"in a new process, and detaches from it.";
1019 if(!proc.startDetached(szCmd))
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))
1027 QString szErr = tr(
"Failed:")
1028 + tr(
"Execute install program error.%1")
1029 .arg(m_DownloadFile.fileName());
1030 ui->lbState->setText(szErr);
1035 QString szInstall = fi.absolutePath() + QDir::separator() +
"setup.sh";
1037 if(!f.open(QFile::WriteOnly))
1039 QString szErr = tr(
"Failed:")
1040 + tr(
"Open file %1 fail").arg(fi.absolutePath());
1041 ui->lbState->setText(szErr);
1044 QString szCmd = InstallScript(m_DownloadFile.fileName(),
1045 qApp->applicationName());
1046 f.write(szCmd.toStdString().c_str());
1047 qDebug(log) << szCmd << szInstall;
1052 QStringList() << szInstall))
1054 QString szErr = tr(
"Failed:") + tr(
"Execute") +
"/bin/bash "
1055 + szInstall +
"fail";
1056 ui->lbState->setText(szErr);
1081 ui->lbState->setText(tr(
"The installer has started, Please close the application"));
1091 QString szHome = m_Info.version.szHome;
1092 if((!bSuccess || ui->cbHomePage->isChecked()) && !szHome.isEmpty())
1093 if(!procHome.startDetached(szHome))
1096 if(!QDesktopServices::openUrl(url))
1098 QString szErr = tr(
"Failed:") + tr(
"Open home page fail");
1099 ui->lbState->setText(szErr);
1112 if(!QDesktopServices::openUrl(url))
1114 QString szErr = tr(
"Open home page fail");
1115 qCritical(log) << szErr;
1119QString CFrmUpdater::InstallScript(
const QString szDownLoadFile,
1120 const QString szApplicationName)
1122 QFileInfo fi(szDownLoadFile);
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";
1129 szCmd +=
"cd /opt/" + szApplicationName +
"\n";
1130 szCmd +=
"if [ -f install1.sh ]; then\n";
1131 szCmd +=
" ./install1.sh remove " + szApplicationName +
"\n";
1134 szCmd +=
"cp " + szDownLoadFile +
" ." +
"\n";
1135 szCmd +=
"tar xvfz " + fi.fileName() +
"\n";
1136 szCmd +=
"rm " + fi.fileName() +
"\n";
1139 szCmd +=
"./install1.sh ";
1140 if(m_InstallAutoStartupType)
1141 szCmd +=
"install_autostart";
1145 int nRet = QMessageBox::information(
this, tr(
"Run"),
1146 tr(
"Run after install"),
1147 QMessageBox::Yes|QMessageBox::No,
1149 if(QMessageBox::Yes == nRet)
1153 szCmd +=
" " + szApplicationName +
"\n";
1169 QString sN = newVersion;
1170 QString sC = currentVersion;
1172 if(sN.isEmpty() || sC.isEmpty())
1173 return sN.length() - sC.length();
1175 sN = sN.replace(
"-",
".");
1176 sC = sC.replace(
"-",
".");
1178 QStringList szNew = sN.split(
".");
1179 QStringList szCur = sC.split(
".");
1181 int count = qMin(szNew.length(), szCur.length());
1182 qDebug(log) <<
"count:" << count;
1185 return szNew.length() - szCur.length();
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();
1196 return sN.length() - sC.length();
1197 nRet = szNew.at(1).toInt() - szCur.at(1).toInt();
1202 return sN.length() - sC.length();
1203 return szNew.at(2).toInt() - szCur.at(2).toInt();
1214 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
1215 szTmp = szTmp + QDir::separator() +
"Rabbit"
1216 + QDir::separator() + qApp->applicationName();
1218 QString szFile = szTmp + QDir::separator() + m_ConfigFile.szFileName;
1221 if(!f.open(QIODevice::ReadOnly))
1224 m_DownloadFile.setFileName(szFile);
1226 QCryptographicHash md5sum(QCryptographicHash::Md5);
1227 if(!md5sum.addData(&f))
1232 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
1247void CFrmUpdater::on_pbOK_clicked()
1249 qDebug(log) <<
"CFrmUpdater::on_pbOK_clicked()";
1250 if(!m_pStateDownloadSetupFile->active())
1257void CFrmUpdater::on_pbClose_clicked()
1259 if(m_StateMachine->isRunning())
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)
1272void CFrmUpdater::slotButtonClickd(
int id)
1274 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(), QSettings::IniFormat);
1275 set.setValue(
"Update/RadioButton",
id);
1280 QCommandLineParser parser;
1282 parser.process(qApp->arguments());
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);
1318 foreach (
auto f, info.files) {
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);
1329 foreach (
auto u, f.urls) {
1330 urls.append(u.toString());
1332 file.insert(
"urls", urls);
1337 case CONFIG_TYPE::VERSION:
1338 doc.setObject(version);
1340 case CONFIG_TYPE::FILE:
1341 doc.setObject(files[0].toObject());
1343 case CONFIG_TYPE::VERSION_FILE:
1346 root.insert(
"version", version);
1347 root.insert(
"files", files);
1348 doc.setObject(root);
1355 if(!f.open(QIODevice::WriteOnly))
1357 qCritical(log) <<
"Open file fail:" << f.fileName();
1360 f.write(doc.toJson());
1365int CFrmUpdater::GenerateUpdateXmlFile(
const QString &szFile,
const CONFIG_INFO &info, CONFIG_TYPE &type)
1368 QDomProcessingInstruction ins;
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);
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);
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);
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);
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);
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);
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);
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);
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);
1424 foreach(
auto u, file.urls)
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);
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);
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);
1446 f.setFileName(szFile);
1447 if(!f.open(QIODevice::WriteOnly))
1450 <<
"CFrmUpdater::GenerateUpdateXml file open file fail:"
1454 QTextStream stream(&f);
1455#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1456 stream.setCodec(
"UTF-8");
1458 doc.save(stream, 4);
1465 QCommandLineParser parser;
1467 parser.process(qApp->arguments());
1479 return GenerateUpdateXmlFile(szFile +
".xml", info, type);
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";
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)
1510 if(szAppImage.isEmpty())
1511 f = QFileInfo(qApp->applicationFilePath());
1513 f = QFileInfo(szAppImage);
1514 if(f.suffix().compare(
"AppImage", Qt::CaseInsensitive))
1516 QString szVersion = m_szCurrentVersion;
1517 szVersion.replace(
"v",
"", Qt::CaseInsensitive);
1518 szFileName = qApp->applicationName().toLower()
1519 +
"_" + szVersion +
"_amd64.deb";
1521 szFileName = qApp->applicationName()
1522 +
"_" + m_szCurrentVersion +
".AppImage";
1527 szUrl =
"https://github.com/KangLin/"
1528 + qApp->applicationName()
1529 +
"/releases/download/"
1530 + m_szCurrentVersion +
"/" + szFileName;
1532 parser.addHelpOption();
1533 parser.addVersionOption();
1535 QCommandLineOption oFile(QStringList() <<
"f" <<
"file",
1536 tr(
"Configure file name"),
1537 "Configure file name",
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"),
1551 m_szCurrentVersion);
1552 parser.addOption(oPackageVersion);
1553 QCommandLineOption oTime(QStringList() <<
"t" <<
"time",
1556 QDateTime::currentDateTime().toString(
"yyyy-MM-dd hh:mm:ss"));
1557 parser.addOption(oTime);
1558 QCommandLineOption oInfo(QStringList() <<
"i" <<
"info",
1561 qApp->applicationName() +
" " + m_szCurrentVersion);
1562 parser.addOption(oInfo);
1564 QCommandLineOption oSystem(QStringList() <<
"s" <<
"system",
1565 tr(
"Operating system"),
1568 parser.addOption(oSystem);
1569 QCommandLineOption oArch(QStringList() <<
"a" <<
"arch",
1572 QSysInfo::buildCpuArchitecture());
1573 parser.addOption(oArch);
1574 QCommandLineOption oMd5(QStringList() <<
"c" <<
"md5",
1577 parser.addOption(oMd5);
1578 QCommandLineOption oPackageFile(QStringList() <<
"p" <<
"pf" <<
"package-file",
1579 tr(
"Package file, Is used to calculate md5sum"),
1582 parser.addOption(oPackageFile);
1583 QCommandLineOption oFileName(QStringList() <<
"n" <<
"file-name",
1587 parser.addOption(oFileName);
1588 QCommandLineOption oUrl(QStringList() <<
"u" <<
"urls",
1589 tr(
"Package download urls"),
1592 parser.addOption(oUrl);
1593 QString szHome =
"https://github.com/KangLin/" + qApp->applicationName();
1594 QCommandLineOption oUrlHome(
"home",
1595 tr(
"Project home url"),
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"),
1608 parser.addOption(oForce);
1610 if(!parser.parse(QApplication::arguments())) {
1611 qDebug(log) <<
"parser.parse fail" << parser.errorText()
1612 << qApp->arguments();
1615 szFile = parser.value(oFile);
1616 if(szFile.isEmpty())
1617 qDebug(log) <<
"File is empty";
1619 type =
static_cast<CONFIG_TYPE
>(parser.value(oFileOuputContent).toInt());
1620 qDebug(log) <<
"File content is:" << (int)type;
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;
1631 info.version.bForce =
true;
1634 file.szSystem = parser.value(oSystem);
1635 file.szArchitecture = parser.value(oArch);
1636 file.szMd5sum = parser.value(oMd5);
1637 file.szFileName = parser.value(oFileName);
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())
1647 QCryptographicHash md5sum(QCryptographicHash::Md5);
1648 QFile app(szPackageFile);
1649 if(app.open(QIODevice::ReadOnly))
1651 if(md5sum.addData(&app))
1653 file.szMd5sum = md5sum.result().toHex();
1657 qCritical(log) <<
"Don't open package file:" << szPackageFile;
1662 if(file.szMd5sum.isEmpty())
1663 qWarning(log) <<
"Md5 is empty. please set -c or --md5 or -p";
1671 if(file.szFileName.isEmpty())
1672 file.szFileName = szFileName;
1674 QString szUrls = parser.value(oUrl);
1675 foreach(
auto u, szUrls.split(QRegularExpression(
"[;,]")))
1677 file.urls.push_back(QUrl(u));
1680 info.files.append(file);
1685void CFrmUpdater::showEvent(QShowEvent *event)
1688 if(!m_StateMachine->isRunning())
1689 m_StateMachine->start();
1692void CFrmUpdater::slotShowWindow(QSystemTrayIcon::ActivationReason reason)
1695#if defined(Q_OS_ANDROID)
1703bool CFrmUpdater::CheckPrompt(
const QString &szVersion)
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);
1713 return !ui->cbPrompt->isChecked();
1717void CFrmUpdater::on_cbPrompt_clicked(
bool checked)
1719 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1720 QSettings::IniFormat);
1721 set.setValue(
"Updater/Prompt", checked);
1724void CFrmUpdater::on_cbHomePage_clicked(
bool checked)
1726 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1727 QSettings::IniFormat);
1728 set.setValue(
"Updater/ShowHomePage", checked);
1733 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 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.