5#include "RabbitCommonDir.h"
6#include "RabbitCommonTools.h"
7#include "ui_FrmUpdater.h"
9#include <QStandardPaths>
11#include <QJsonDocument>
14#include <QDomDocument>
20#include <QDesktopServices>
21#include <QInputDialog>
25#include <QLoggingCategory>
26#include <QRegularExpression>
28static Q_LOGGING_CATEGORY(log,
"RabbitCommon.Updater")
33 m_InstallAutoStartupType(false),
36 m_pStateDownloadSetupFile(
nullptr)
39 setAttribute(Qt::WA_DeleteOnClose);
41 ui->lbNewArch->hide();
42 ui->lbNewVersion->hide();
43 ui->progressBar->hide();
44 ui->cbHomePage->hide();
47 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
48 QSettings::IniFormat);
49 ui->cbPrompt->setChecked(set.value(
"Updater/Prompt",
false).toBool());
50 ui->cbHomePage->setChecked(set.value(
"Updater/ShowHomePage",
true).toBool());
52 check = connect(&m_TrayIcon,
53 SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
55 SLOT(slotShowWindow(QSystemTrayIcon::ActivationReason)));
57 m_TrayIcon.setIcon(this->windowIcon());
58 m_TrayIcon.setToolTip(windowTitle() +
" - "
59 + qApp->applicationDisplayName());
61 int id = set.value(
"Update/RadioButton", -2).toInt();
62 m_ButtonGroup.addButton(ui->rbEveryTime);
63 m_ButtonGroup.addButton(ui->rbEveryDate);
64 m_ButtonGroup.addButton(ui->rbEveryWeek);
65 m_ButtonGroup.addButton(ui->rbEveryMonth);
66 m_ButtonGroup.button(
id)->setChecked(
true);
67 check = connect(&m_ButtonGroup,
68 #
if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
69 SIGNAL(idClicked(
int)),
71 SIGNAL(buttonClicked(
int)),
73 this, SLOT(slotButtonClickd(
int)));
77 ui->lbCurrentArch->setText(tr(
"Current archecture: %1")
78 .arg(QSysInfo::currentCpuArchitecture()));
80 QString szVerion = qApp->applicationVersion();
81#ifdef RabbitCommon_VERSION
82 if(szVerion.isEmpty())
83 szVerion = RabbitCommon_VERSION;
89 if(QSslSocket::supportsSsl())
92#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
93 szMsg =
"Build Version: " + QSslSocket::sslLibraryBuildVersionString();
95 szMsg +=
"; Installed Version: " + QSslSocket::sslLibraryVersionString();
96 qInfo(log) <<
"QSslSocket support ssl:" << szMsg;
99#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
100 szMsg =
"BuildVersion: " + QSslSocket::sslLibraryBuildVersionString();
103 "QSslSocket is not support ssl. The system is not install the OPENSSL dynamic library[" << szMsg <<
"]."
104 " Please install OPENSSL dynamic library [" << szMsg <<
"]";
113 QUrl github(
"https://github.com/KangLin/"
114 + qApp->applicationName() +
"/raw/master/Update/update.json");
115 QUrl gitlab(
"https://gitlab.com/kl222/"
116 + qApp->applicationName() +
"/-/raw/master/Update/update.json");
117 QUrl gitee(
"https://gitee.com/kl222/"
118 + qApp->applicationName() +
"/raw/master/Update/update.json");
119 QUrl sourceforge(
"https://sourceforge.net/p/"
120 + qApp->applicationName() +
"/ci/master/tree/Update/update.json?format=raw");
122 m_Urls << github << gitee << sourceforge << gitlab;
126 qDebug(log) <<
"Urls:" << m_Urls;
130CFrmUpdater::~CFrmUpdater()
132 qDebug(log) <<
"CFrmUpdater::~CFrmUpdater()";
133 m_DownloadFile.close();
192 qDebug(log) <<
"Init State Machine";
193 QFinalState *sFinal =
new QFinalState();
194 QState *sCheck =
new QState();
195 QState *s =
new QState();
196 QState *sDownloadConfigFile =
new QState(s);
197 QState *sCheckConfigFile =
new QState(s);
198 QState *sDownloadSetupFile =
new QState(s);
199 QState *sUpdate =
new QState(s);
201 bool check = connect(sFinal, SIGNAL(entered()),
202 this, SLOT(slotStateFinished()));
205 sCheck->addTransition(
this, SIGNAL(sigError()), sFinal);
206 sCheck->addTransition(
this, SIGNAL(sigFinished()), s);
207 check = connect(sCheck, SIGNAL(entered()),
this, SLOT(slotCheck()));
210 s->addTransition(
this, SIGNAL(sigError()), sFinal);
211 s->addTransition(
this, SIGNAL(sigFinished()), sFinal);
213 s->setInitialState(sDownloadConfigFile);
214 sDownloadConfigFile->assignProperty(ui->lbState,
"text", tr(
"Being Download config file"));
215 sDownloadConfigFile->addTransition(
this, SIGNAL(sigFinished()), sCheckConfigFile);
216 check = connect(sDownloadConfigFile, SIGNAL(entered()),
217 this, SLOT(slotDownloadFile()));
220 sCheckConfigFile->addTransition(
this, SIGNAL(sigDownLoadRedire()), sDownloadConfigFile);
221 sCheckConfigFile->addTransition(
this, SIGNAL(sigFinished()), sDownloadSetupFile);
222 sCheckConfigFile->addTransition(ui->pbOK, SIGNAL(clicked()), sDownloadSetupFile);
223 sCheckConfigFile->assignProperty(ui->pbOK,
"text", tr(
"OK(&O)"));
224 check = connect(sCheckConfigFile, SIGNAL(entered()),
this, SLOT(slotCheckConfigFile()));
227 m_pStateDownloadSetupFile = sDownloadSetupFile;
228 sDownloadSetupFile->addTransition(
this, SIGNAL(sigFinished()), sUpdate);
229 sDownloadSetupFile->assignProperty(ui->lbState,
"text", tr(
"Being download update file"));
230 check = connect(sDownloadSetupFile, SIGNAL(entered()),
this, SLOT(slotDownloadSetupFile()));
233 sUpdate->assignProperty(ui->lbState,
"text", tr(
"Being install update"));
234 check = connect(sUpdate, SIGNAL(entered()),
this, SLOT(slotUpdate()));
237 m_StateMachine.addState(sCheck);
238 m_StateMachine.addState(s);
239 m_StateMachine.addState(sFinal);
240 m_StateMachine.setInitialState(sCheck);
241 m_StateMachine.start();
247 QString title = szTitle;
248 if(szTitle.isEmpty())
249 title = qApp->applicationDisplayName();
250 ui->lbTitle->setText(title);
252 QPixmap pixmpa = QPixmap::fromImage(icon);
254 pixmpa.load(
":/icon/RabbitCommon/App",
"PNG");
255 ui->lbTitleIcon->setPixmap(pixmpa);
259int CFrmUpdater::SetVersion(
const QString &szVersion)
261 m_szCurrentVersion = szVersion;
262 ui->lbCurrentVersion->setText(tr(
"Current version: %1")
263 .arg(m_szCurrentVersion));
267void CFrmUpdater::slotStateFinished()
269 qDebug(log) <<
"slotStateFinished()";
274void CFrmUpdater::slotCheck()
276 qDebug(log) <<
"CFrmUpdater::slotCheck()";
277 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
278 QSettings::IniFormat);
279 QDateTime d = set.value(
"Update/DateTime").toDateTime();
280 set.setValue(
"Update/DateTime", QDateTime::currentDateTime());
288 if(ui->rbEveryDate->isChecked())
290 else if(ui->rbEveryWeek->isChecked())
292 else if(ui->rbEveryMonth->isChecked())
295 if(n <= d.daysTo(QDateTime::currentDateTime()))
302void CFrmUpdater::slotDownloadError(
int nErr,
const QString szError)
304 ui->progressBar->hide();
305 ui->progressBar->setRange(0, 100);;
307 szMsg = tr(
"Failed:") + tr(
"Download file is Failed.");
308 if(!szError.isEmpty())
309 szMsg +=
"(" + szError +
")";
310 ui->lbState->setText(szMsg);
311 qCritical(log) << szMsg << nErr;
315void CFrmUpdater::slotDownloadFileFinished(
const QString szFile)
317 qDebug(log) <<
"slotDownloadFileFinished:" << szFile;
318 ui->progressBar->hide();
319 ui->progressBar->setRange(0, 100);;
320 if(m_DownloadFile.isOpen())
321 m_DownloadFile.close();
324 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
325 szTmp = szTmp + QDir::separator() +
"Rabbit"
326 + QDir::separator() + qApp->applicationName();
328 QString szFileName(m_ConfigFile.szFileName);
329 if(szFileName.isEmpty())
331 szFileName = szFile.mid(szFile.lastIndexOf(
"/"));
333 if(szFileName.left(1) !=
"/" && szFileName.left(1) !=
"\\")
334 szFileName = QDir::separator() + szFileName;
335 QString f = szTmp + szFileName;
339 if(QFile::copy(szFile, f))
341 if(QFile::rename(szFile, f))
344 m_DownloadFile.setFileName(f);
345 qInfo(log) <<
"Download finished: rename"
346 << szFile <<
"to" << f;
348 qCritical(log) <<
"Download finished. rename fail from"
349 << szFile <<
"to" << f;
350 m_DownloadFile.setFileName(szFile);
355void CFrmUpdater::slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
357 if(ui->progressBar->isHidden())
359 ui->progressBar->show();
360 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
362 if(ui->progressBar->maximum() != bytesTotal)
363 ui->progressBar->setRange(0,
static_cast<int>(bytesTotal));
365 ui->progressBar->setValue(
static_cast<int>(bytesReceived));
367 QString szInfo = tr(
"Downloading %1% [%2/%3]")
368 .arg(QString::number(bytesReceived * 100 / bytesTotal))
369 .arg(QString::number(bytesReceived)).arg(QString::number(bytesTotal));
371 m_TrayIcon.setToolTip(windowTitle() +
" - "
372 + qApp->applicationDisplayName()
378void CFrmUpdater::slotDownloadFile()
380 qDebug(log) <<
"CFrmUpdater::slotDownloadFile";
382 if(!m_Urls.isEmpty())
384 m_Download = QSharedPointer<RabbitCommon::CDownload>(
386 bool check = connect(m_Download.data(), SIGNAL(sigFinished(
const QString)),
387 this, SLOT(slotDownloadFileFinished(
const QString)));
389 check = connect(m_Download.data(), SIGNAL(sigError(
int,
const QString)),
390 this, SLOT(slotDownloadError(
int,
const QString)));
392 check = connect(m_Download.data(), SIGNAL(sigDownloadProgress(qint64, qint64)),
393 this, SLOT(slotDownloadProgress(qint64, qint64)));
395 m_Download->Start(m_Urls);
400void CFrmUpdater::slotCheckConfigFile()
402 m_TrayIcon.setToolTip(windowTitle() +
" - "
403 + qApp->applicationDisplayName());
404 qDebug(log) <<
"CFrmUpdater::slotCheckConfigFile()";
409 if(nRet <= 0)
return;
412 QString szText(tr(
"There is laster version"));
413 ui->lbState->setText(szText);
414 qInfo(log) << szText;
439 qDebug(log) <<
"CFrmUpdater::CheckRedirectConfigFile()"
440 << m_DownloadFile.fileName();
442 QVector<CONFIG_REDIRECT> conf;
446 QString szError = tr(
"Failed:") + tr(
"%2 process the file: %1")
447 .arg(m_DownloadFile.fileName()).arg(nRet);
448 ui->lbState->setText(szError);
449 qCritical(log) << szError;
456 for(
auto it = conf.begin(); it != conf.end(); it++) {
458 QString szVersion = it->szVersion;
459 if(szVersion.isEmpty())
461 QString szError = tr(
"Failed:") + tr(
"Configure file content error:")
462 + m_DownloadFile.fileName();
463 ui->lbState->setText(szError);
464 qCritical(log) << szError;
472 QString szMinVersion = it->szMinUpdateVersion;
473 if(!szMinVersion.isEmpty()) {
482 if(redirect.szVersion.isEmpty())
486 foreach (
auto f, redirect.files) {
487 if(!f.szSystem.isEmpty()) {
488 if(QSysInfo::productType() != f.szSystem)
491 if(!f.szArchitecture.isEmpty())
493 QString szArchitecture = QSysInfo::currentCpuArchitecture();
494#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
495 if(!szArchitecture.compare(
"i386", Qt::CaseInsensitive)
496 && !f.szArchitecture.compare(
"x86_64", Qt::CaseInsensitive))
499 if(szArchitecture != f.szArchitecture)
510 if(redirect.files.isEmpty()) {
512 QUrl github(
"https://github.com/KangLin/"
513 + qApp->applicationName() +
"/releases/download/"
514 + redirect.szVersion +
"/update.json");
515 m_Urls.push_back(github);
516 QUrl sourceforge(
"https://sourceforge.net/projects/"
517 + qApp->applicationName() +
"/files/"
518 + redirect.szVersion +
"/update.json?viasf=1");
519 m_Urls.push_back(sourceforge);
523 szError = tr(
"Failed:")
524 + tr(
"Don't find the urls in configure file:")
525 + m_DownloadFile.fileName()
526 +
"; " + tr(
"Current version:") + m_szCurrentVersion
527 +
"; " + tr(
"version:") + redirect.szVersion
528 +
"; " + tr(
"min update version:")
529 + redirect.szMinUpdateVersion;
530 qCritical(log) << szError;
531 ui->lbState->setText(szError);
536 qInfo(log) <<
"Redirect. Version:" << redirect.szVersion << m_Urls;
538 emit sigDownLoadRedire();
578 qDebug(log) <<
"CFrmUpdater::CheckUpdateConfigFile()";
582 QString szError = tr(
"Failed:") + tr(
"%2 process the file: %1")
583 .arg(m_DownloadFile.fileName()).arg(nRet);
584 ui->lbState->setText(szError);
585 qCritical(log) << szError;
590 if(
CompareVersion(info.version.szVerion, m_szCurrentVersion) <= 0)
593 szError = tr(
"There is laster version");
594 ui->lbState->setText(szError);
595 qInfo(log) << szError;
600 if(info.files.isEmpty()) {
602 szError = tr(
"Failed:") + tr(
"There is not files in the configure file ")
603 + m_DownloadFile.fileName();
604 ui->lbState->setText(szError);
605 qCritical(log) << szError;
610 QString szArchitecture = QSysInfo::currentCpuArchitecture();
612 foreach (
auto f, info.files) {
613 if(f.szSystem.compare(QSysInfo::productType(), Qt::CaseInsensitive))
616#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
617 if(!szArchitecture.compare(
"i386", Qt::CaseInsensitive)
618 && !f.szArchitecture.compare(
"x86_64", Qt::CaseInsensitive))
621 if(szArchitecture != f.szArchitecture)
628 if(file.szSystem.compare(QSysInfo::productType(), Qt::CaseInsensitive)) {
630 szErr = tr(
"Failed:")
631 + tr(
"The system or architecture is not exist in the configure file ")
632 + m_DownloadFile.fileName();
633 ui->lbState->setText(szErr);
634 qCritical(log) << szErr;
639 m_Info.version = info.version;
642 ui->lbNewVersion->setText(tr(
"New version: %1").arg(info.version.szVerion));
643 ui->lbNewVersion->show();
644 ui->lbNewArch->setText(tr(
"New architecture: %1").arg(file.szArchitecture));
645 ui->lbNewArch->show();
646 ui->lbState->setText(tr(
"There is a new version, is it updated?"));
647 if(info.version.bForce)
649 qDebug(log) <<
"Force update";
654 ui->cbHomePage->show();
655 ui->cbPrompt->show();
656 ui->pbOK->setText(tr(
"OK(&O)"));
658 if(!CheckPrompt(info.version.szVerion) && this->isHidden())
681 if(!f.open(QFile::ReadOnly))
683 QString szError = tr(
"Open file fail").arg(szFile);
684 qCritical(log) << szError;
689 doc = QJsonDocument::fromJson(f.readAll());
693 QString szError = tr(
"Parse file %1 fail. It isn't configure file")
695 qCritical(log) << szError;
699 QJsonObject objRoot = doc.object();
700 if(!objRoot.contains(
"redirect"))
703 QJsonArray arrRedirect = objRoot[
"redirect"].toArray();
704 for(
auto it = arrRedirect.begin(); it != arrRedirect.end(); it++) {
705 QJsonObject obj = it->toObject();
707 objRedirect.szVersion = obj[
"version"].toString();
708 objRedirect.szMinUpdateVersion = obj[
"min_update_version"].toString();
709 qDebug(log) <<
"version:" << objRedirect.szVersion
710 <<
"min_update_version:" << objRedirect.szMinUpdateVersion;
712 QJsonArray objFiles = obj[
"files"].toArray();
713 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
714 QJsonObject f = it->toObject();
716 file.szSystem = f[
"os"].toString();
717 file.szSystemMinVersion = f[
"os_min_version"].toString();
718 file.szArchitecture = f[
"arch"].toString();
719 file.szArchitectureMinVersion = f[
"arch_min_version"].toString();
720 file.szMd5sum = f[
"md5"].toString();
721 file.szFileName = f[
"name"].toString();
723 QJsonArray urls = f[
"urls"].toArray();
724 foreach(
auto u, urls)
726 file.urls.append(u.toString());
729 objRedirect.files.append(file);
731 qDebug(log) <<
"OS:" << file.szSystem
732 <<
"os_min_version:" << file.szSystemMinVersion
733 <<
"arch:" << file.szArchitecture
734 <<
"arch_min_version:" << file.szArchitectureMinVersion
735 <<
"md5:" << file.szMd5sum
736 <<
"name:" << file.szFileName
737 <<
"urls:" << file.urls;
740 conf.append(objRedirect);
801 if(!file.open(QFile::ReadOnly)) {
802 qDebug(log) <<
"The file isn't opened:" << szFile;
807 doc = QJsonDocument::fromJson(file.readAll());
811 qCritical(log) <<
"Parser configure file fail." << szFile;
815 QJsonObject obj = doc.object();
816 if(obj.contains(
"version")) {
817 QJsonObject objVersion = obj[
"version"].toObject();
818 conf.version.szVerion = objVersion[
"version"].toString();
819 conf.version.szMinUpdateVersion = objVersion[
"min_update_version"].toString();
820 conf.version.szTime = objVersion[
"time"].toString();
821 conf.version.szInfomation = objVersion[
"information"].toString();
822 conf.version.szHome = objVersion[
"home"].toString();
823 conf.version.bForce = objVersion[
"force"].toBool();
825 qDebug(log) <<
"Current version:" << m_szCurrentVersion
826 <<
"version:" << conf.version.szVerion
827 <<
"minUpdateVersion:" << conf.version.szMinUpdateVersion
828 <<
"time:" << conf.version.szTime
829 <<
"information:" << conf.version.szInfomation
830 <<
"home:" << conf.version.szHome
831 <<
"bForce:" << conf.version.bForce
835 if(!obj.contains(
"files")) {
836 qDebug(log) <<
"Configure file isn't contains files array";
840 QJsonArray objFiles = obj[
"files"].toArray();
841 for(
auto it = objFiles.begin(); it != objFiles.end(); it++) {
842 QJsonObject f = it->toObject();
844 file.szSystem = f[
"os"].toString();
845 file.szSystemMinVersion = f[
"os_min_version"].toString();
846 file.szArchitecture = f[
"arch"].toString();
847 file.szArchitectureMinVersion = f[
"arch_min_version"].toString();
848 file.szMd5sum = f[
"md5"].toString();
849 file.szFileName = f[
"name"].toString();
851 QJsonArray urls = f[
"urls"].toArray();
852 foreach(
auto u, urls)
854 file.urls.append(u.toString());
857 conf.files.append(file);
859 qDebug(log) <<
"OS:" << file.szSystem
860 <<
"os_min_version:" << file.szSystemMinVersion
861 <<
"arch:" << file.szArchitecture
862 <<
"arch_min_version:" << file.szArchitectureMinVersion
863 <<
"md5:" << file.szMd5sum
864 <<
"name:" << file.szFileName
865 <<
"urls:" << file.urls;
871void CFrmUpdater::slotDownloadSetupFile()
873 qDebug(log) <<
"CFrmUpdater::slotDownloadSetupFile()";
874 ui->pbOK->setText(tr(
"Hide"));
875 ui->lbState->setText(tr(
"Download ......"));
880 m_Urls = m_ConfigFile.urls;
885void CFrmUpdater::slotUpdate()
887 m_TrayIcon.setToolTip(windowTitle() +
" - "
888 + qApp->applicationDisplayName());
889 ui->lbState->setText(tr(
"Being install update ......"));
890 ui->progressBar->hide();
891 ui->progressBar->setRange(0, 100);;
895 bool bSuccess =
false;
897 if(!m_DownloadFile.open(QIODevice::ReadOnly))
900 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
901 + m_DownloadFile.fileName();
902 qCritical(log) << szErr;
903 ui->lbState->setText(szErr);
906 QCryptographicHash md5sum(QCryptographicHash::Md5);
907 if(!md5sum.addData(&m_DownloadFile))
910 szErr = tr(
"Failed:") + tr(
"Don't open download file ")
911 + m_DownloadFile.fileName();
912 qCritical(log) << szErr;
913 ui->lbState->setText(szErr);
916 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
919 szFail = tr(
"Failed:") + tr(
"Md5sum is different. ")
920 +
"\n" + tr(
"Download file md5sum: ")
921 + md5sum.result().toHex()
922 +
"\n" + tr(
"md5sum in Update.xml:")
923 + m_ConfigFile.szMd5sum;
924 ui->lbState->setText(szFail);
925 qCritical(log) << szFail;
931 m_DownloadFile.close();
952 QFileInfo fi(m_DownloadFile.fileName());
953 if(fi.suffix().compare(
"gz", Qt::CaseInsensitive))
956 szCmd = m_DownloadFile.fileName();
958 qInfo(log) <<
"Start"
960 <<
"in a new process, and detaches from it.";
961 if(!proc.startDetached(szCmd))
963 qInfo(log) <<
"Start new process fail."
964 <<
"Use system installer to install"
965 << m_DownloadFile.fileName();
966 QUrl url(m_DownloadFile.fileName());
967 if(!QDesktopServices::openUrl(url))
969 QString szErr = tr(
"Failed:")
970 + tr(
"Execute install program error.%1")
971 .arg(m_DownloadFile.fileName());
972 ui->lbState->setText(szErr);
977 QString szInstall = fi.absolutePath() + QDir::separator() +
"setup.sh";
979 if(!f.open(QFile::WriteOnly))
981 QString szErr = tr(
"Failed:")
982 + tr(
"Open file %1 fail").arg(fi.absolutePath());
983 ui->lbState->setText(szErr);
986 QString szCmd = InstallScript(m_DownloadFile.fileName(),
987 qApp->applicationName());
988 f.write(szCmd.toStdString().c_str());
989 qDebug(log) << szCmd << szInstall;
994 QStringList() << szInstall))
996 QString szErr = tr(
"Failed:") + tr(
"Execute") +
"/bin/bash "
997 + szInstall +
"fail";
998 ui->lbState->setText(szErr);
1023 ui->lbState->setText(tr(
"The installer has started, Please close the application"));
1033 QString szHome = m_Info.version.szHome;
1034 if((!bSuccess || ui->cbHomePage->isChecked()) && !szHome.isEmpty())
1035 if(!procHome.startDetached(szHome))
1038 if(!QDesktopServices::openUrl(url))
1040 QString szErr = tr(
"Failed:") + tr(
"Open home page fail");
1041 ui->lbState->setText(szErr);
1054 if(!QDesktopServices::openUrl(url))
1056 QString szErr = tr(
"Open home page fail");
1057 qCritical(log) << szErr;
1061QString CFrmUpdater::InstallScript(
const QString szDownLoadFile,
1062 const QString szApplicationName)
1064 QFileInfo fi(szDownLoadFile);
1066 szCmd =
"#!/bin/bash\n";
1067 szCmd +=
"set -e\n";
1068 szCmd +=
"if [ ! -d /opt/" + szApplicationName +
" ]; then\n";
1069 szCmd +=
" mkdir -p /opt/" + szApplicationName +
"\n";
1071 szCmd +=
"cd /opt/" + szApplicationName +
"\n";
1072 szCmd +=
"if [ -f install1.sh ]; then\n";
1073 szCmd +=
" ./install1.sh remove " + szApplicationName +
"\n";
1076 szCmd +=
"cp " + szDownLoadFile +
" ." +
"\n";
1077 szCmd +=
"tar xvfz " + fi.fileName() +
"\n";
1078 szCmd +=
"rm " + fi.fileName() +
"\n";
1081 szCmd +=
"./install1.sh ";
1082 if(m_InstallAutoStartupType)
1083 szCmd +=
"install_autostart";
1087 int nRet = QMessageBox::information(
this, tr(
"Run"),
1088 tr(
"Run after install"),
1089 QMessageBox::Yes|QMessageBox::No,
1091 if(QMessageBox::Yes == nRet)
1095 szCmd +=
" " + szApplicationName +
"\n";
1111 QString sN = newVersion;
1112 QString sC = currentVersion;
1114 if(sN.isEmpty() || sC.isEmpty())
1115 return sN.length() - sC.length();
1117 sN = sN.replace(
"-",
".");
1118 sC = sC.replace(
"-",
".");
1120 QStringList szNew = sN.split(
".");
1121 QStringList szCur = sC.split(
".");
1123 int count = qMin(szNew.length(), szCur.length());
1124 qDebug(log) <<
"count:" << count;
1127 return szNew.length() - szCur.length();
1129 QString firstNew = szNew.at(0);
1130 QString firstCur = szCur.at(0);
1131 firstNew = firstNew.remove(QChar(
'v'), Qt::CaseInsensitive);
1132 firstCur = firstCur.remove(QChar(
'v'), Qt::CaseInsensitive);
1133 nRet = firstNew.toInt() - firstCur.toInt();
1138 return sN.length() - sC.length();
1139 nRet = szNew.at(1).toInt() - szCur.at(1).toInt();
1144 return sN.length() - sC.length();
1145 return szNew.at(2).toInt() - szCur.at(2).toInt();
1156 = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
1157 szTmp = szTmp + QDir::separator() +
"Rabbit"
1158 + QDir::separator() + qApp->applicationName();
1160 QString szFile = szTmp + QDir::separator() + m_ConfigFile.szFileName;
1163 if(!f.open(QIODevice::ReadOnly))
1166 m_DownloadFile.setFileName(szFile);
1168 QCryptographicHash md5sum(QCryptographicHash::Md5);
1169 if(!md5sum.addData(&f))
1174 if(md5sum.result().toHex() != m_ConfigFile.szMd5sum)
1189void CFrmUpdater::on_pbOK_clicked()
1191 qDebug(log) <<
"CFrmUpdater::on_pbOK_clicked()";
1192 if(!m_pStateDownloadSetupFile->active())
1199void CFrmUpdater::on_pbClose_clicked()
1201 if(m_StateMachine.isRunning())
1203 QMessageBox::StandardButton ret = QMessageBox::warning(
this, tr(
"Close"),
1204 tr(
"Is updating, be sure to close?"), QMessageBox::Yes|QMessageBox::No);
1205 if(QMessageBox::No == ret)
1214void CFrmUpdater::slotButtonClickd(
int id)
1216 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(), QSettings::IniFormat);
1217 set.setValue(
"Update/RadioButton",
id);
1222 QCommandLineParser parser;
1224 parser.process(qApp->arguments());
1251 QJsonObject version;
1252 version.insert(
"version", info.version.szVerion);
1253 version.insert(
"min_update_version", info.version.szMinUpdateVersion);
1254 version.insert(
"time", info.version.szTime);
1255 version.insert(
"information", info.version.szInfomation);
1256 version.insert(
"home", info.version.szHome);
1257 version.insert(
"force", info.version.bForce);
1260 foreach (
auto f, info.files) {
1262 file.insert(
"os", f.szSystem);
1263 if(!f.szSystemMinVersion.isEmpty())
1264 file.insert(
"os_min_version", f.szSystemMinVersion);
1265 file.insert(
"arch", f.szArchitecture);
1266 if(!f.szArchitectureMinVersion.isEmpty())
1267 file.insert(
"arch_min_version", f.szArchitectureMinVersion);
1268 file.insert(
"md5", f.szMd5sum);
1269 file.insert(
"name", f.szFileName);
1271 foreach (
auto u, f.urls) {
1272 urls.append(u.toString());
1274 file.insert(
"urls", urls);
1279 case CONFIG_TYPE::VERSION:
1280 doc.setObject(version);
1282 case CONFIG_TYPE::FILE:
1283 doc.setObject(files[0].toObject());
1285 case CONFIG_TYPE::VERSION_FILE:
1288 root.insert(
"version", version);
1289 root.insert(
"files", files);
1290 doc.setObject(root);
1297 if(!f.open(QIODevice::WriteOnly))
1299 qCritical(log) <<
"Open file fail:" << f.fileName();
1302 f.write(doc.toJson());
1307int CFrmUpdater::GenerateUpdateXmlFile(
const QString &szFile,
const CONFIG_INFO &info, CONFIG_TYPE &type)
1310 QDomProcessingInstruction ins;
1312 ins = doc.createProcessingInstruction(
"xml",
"version=\'1.0\' encoding=\'UTF-8\'");
1313 doc.appendChild(ins);
1314 QDomElement root = doc.createElement(
"UPDATE");
1315 doc.appendChild(root);
1317 QDomText version = doc.createTextNode(
"VERSION");
1318 version.setData(info.version.szVerion);
1319 QDomElement eVersion = doc.createElement(
"VERSION");
1320 eVersion.appendChild(version);
1321 root.appendChild(eVersion);
1323 QDomText time = doc.createTextNode(
"TIME");
1324 time.setData(info.version.szTime);
1325 QDomElement eTime = doc.createElement(
"TIME");
1326 eTime.appendChild(time);
1327 root.appendChild(eTime);
1329 QDomText i = doc.createTextNode(
"INFO");
1330 i.setData(info.version.szInfomation);
1331 QDomElement eInfo = doc.createElement(
"INFO");
1332 eInfo.appendChild(i);
1333 root.appendChild(eInfo);
1335 QDomText force = doc.createTextNode(
"FORCE");
1336 force.setData(QString::number(info.version.bForce));
1337 QDomElement eForce = doc.createElement(
"FORCE");
1338 eForce.appendChild(force);
1339 root.appendChild(eForce);
1341 CONFIG_FILE file = info.files[0];
1342 QDomText system = doc.createTextNode(
"SYSTEM");
1343 system.setData(file.szSystem);
1344 QDomElement eSystem = doc.createElement(
"SYSTEM");
1345 eSystem.appendChild(system);
1346 root.appendChild(eSystem);
1348 QDomText arch = doc.createTextNode(
"ARCHITECTURE");
1349 arch.setData(file.szArchitecture);
1350 QDomElement architecture = doc.createElement(
"ARCHITECTURE");
1351 architecture.appendChild(arch);
1352 root.appendChild(architecture);
1354 QDomText md5 = doc.createTextNode(
"MD5SUM");
1355 md5.setData(file.szMd5sum);
1356 QDomElement eMd5 = doc.createElement(
"MD5SUM");
1357 eMd5.appendChild(md5);
1358 root.appendChild(eMd5);
1360 QDomText fileName = doc.createTextNode(
"FILENAME");
1361 fileName.setData(file.szFileName);
1362 QDomElement eFileName = doc.createElement(
"FILENAME");
1363 eFileName.appendChild(fileName);
1364 root.appendChild(eFileName);
1366 foreach(
auto u, file.urls)
1368 QDomText url = doc.createTextNode(
"URL");
1369 url.setData(u.toString());
1370 QDomElement eUrl = doc.createElement(
"URL");
1371 eUrl.appendChild(url);
1372 root.appendChild(eUrl);
1375 QDomText urlHome = doc.createTextNode(
"HOME");
1376 urlHome.setData(info.version.szHome);
1377 QDomElement eUrlHome = doc.createElement(
"HOME");
1378 eUrlHome.appendChild(urlHome);
1379 root.appendChild(eUrlHome);
1381 QDomText min = doc.createTextNode(
"MIN_UPDATE_VERSION");
1382 min.setData(info.version.szMinUpdateVersion);
1383 QDomElement eMin = doc.createElement(
"MIN_UPDATE_VERSION");
1384 eMin.appendChild(min);
1385 root.appendChild(eMin);
1388 f.setFileName(szFile);
1389 if(!f.open(QIODevice::WriteOnly))
1392 <<
"CFrmUpdater::GenerateUpdateXml file open file fail:"
1396 QTextStream stream(&f);
1397#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1398 stream.setCodec(
"UTF-8");
1400 doc.save(stream, 4);
1407 QCommandLineParser parser;
1409 parser.process(qApp->arguments());
1421 return GenerateUpdateXmlFile(szFile +
".xml", info, type);
1439#if defined (Q_OS_WIN)
1440 szFileName = qApp->applicationName() +
"_" + m_szCurrentVersion +
"_Setup" +
".exe";
1441#elif defined(Q_OS_ANDROID)
1442 szFileName = qApp->applicationName().toLower() +
"_" + m_szCurrentVersion +
".apk";
1443#elif defined(Q_OS_LINUX)
1444 QFileInfo f(qApp->applicationFilePath());
1445 if(f.suffix().compare(
"AppImage", Qt::CaseInsensitive))
1447 QString szVersion = m_szCurrentVersion;
1448 szVersion.replace(
"v",
"", Qt::CaseInsensitive);
1449 szFileName = qApp->applicationName().toLower()
1450 +
"_" + szVersion +
"_amd64.deb";
1452 szFileName = qApp->applicationName()
1453 +
"_" + m_szCurrentVersion +
".tar.gz";
1458 szUrl =
"https://github.com/KangLin/"
1459 + qApp->applicationName()
1460 +
"/releases/download/"
1461 + m_szCurrentVersion +
"/" + szFileName;
1463 parser.addHelpOption();
1464 parser.addVersionOption();
1466 QCommandLineOption oFile(QStringList() <<
"f" <<
"file",
1467 tr(
"Configure file name"),
1468 "Configure file name",
1470 parser.addOption(oFile);
1471 QCommandLineOption oFileOuputContent(QStringList() <<
"foc" <<
"file-output-content",
1472 tr(
"Configure file output content:") +
"\n"
1473 + QString::number(
static_cast<int>(CONFIG_TYPE::VERSION)) + tr(
": content is version") +
"\n"
1474 + QString::number(
static_cast<int>(CONFIG_TYPE::FILE)) + tr(
": content is file") +
"\n"
1475 + QString::number(
static_cast<int>(CONFIG_TYPE::VERSION_FILE)) + tr(
": content is version and file"),
1476 "Configure file output content",
1477 QString::number(
static_cast<int>(CONFIG_TYPE::VERSION_FILE)));
1478 parser.addOption(oFileOuputContent);
1479 QCommandLineOption oPackageVersion(QStringList() <<
"pv" <<
"package-version",
1480 tr(
"Package version"),
1482 m_szCurrentVersion);
1483 parser.addOption(oPackageVersion);
1484 QCommandLineOption oTime(QStringList() <<
"t" <<
"time",
1487 QDateTime::currentDateTime().toString(
"yyyy-MM-dd hh:mm:ss"));
1488 parser.addOption(oTime);
1489 QCommandLineOption oInfo(QStringList() <<
"i" <<
"info",
1492 qApp->applicationName() +
" " + m_szCurrentVersion);
1493 parser.addOption(oInfo);
1495 QCommandLineOption oSystem(QStringList() <<
"s" <<
"system",
1496 tr(
"Operating system"),
1498 QSysInfo::productType());
1499 parser.addOption(oSystem);
1500 QCommandLineOption oArch(QStringList() <<
"a" <<
"arch",
1503 QSysInfo::buildCpuArchitecture());
1504 parser.addOption(oArch);
1505 QCommandLineOption oMd5(QStringList() <<
"c" <<
"md5",
1508 parser.addOption(oMd5);
1509 QCommandLineOption oPackageFile(QStringList() <<
"p" <<
"pf" <<
"package-file",
1510 tr(
"Package file, Is used to calculate md5sum"),
1513 parser.addOption(oPackageFile);
1514 QCommandLineOption oFileName(QStringList() <<
"n" <<
"file-name",
1518 parser.addOption(oFileName);
1519 QCommandLineOption oUrl(QStringList() <<
"u" <<
"urls",
1520 tr(
"Package download urls"),
1523 parser.addOption(oUrl);
1524 QString szHome =
"https://github.com/KangLin/" + qApp->applicationName();
1525 QCommandLineOption oUrlHome(
"home",
1526 tr(
"Project home url"),
1529 parser.addOption(oUrlHome);
1530 QCommandLineOption oMin(QStringList() <<
"m" <<
"min" <<
"min-update-version",
1531 tr(
"Min update version"),
1532 "Min update version",
1533 m_szCurrentVersion);
1534 parser.addOption(oMin);
1535 QCommandLineOption oForce(QStringList() <<
"force",
1536 tr(
"Set force flag"),
1539 parser.addOption(oForce);
1541 if(!parser.parse(QApplication::arguments())) {
1542 qDebug(log) <<
"parser.parse fail" << parser.errorText()
1543 << qApp->arguments();
1546 szFile = parser.value(oFile);
1547 if(szFile.isEmpty())
1548 qDebug(log) <<
"File is empty";
1550 type =
static_cast<CONFIG_TYPE
>(parser.value(oFileOuputContent).toInt());
1551 qDebug(log) <<
"File content is:" << (int)type;
1553 info.version.szVerion = parser.value(oPackageVersion);
1554 info.version.szMinUpdateVersion = parser.value(oMin);
1555 info.version.szTime = parser.value(oTime);
1556 info.version.szInfomation = parser.value(oInfo);
1557 info.version.szHome = parser.value(oUrlHome);
1558 QString szForce = parser.value(oForce).trimmed();
1559 if(szForce.compare(
"true", Qt::CaseInsensitive))
1560 info.version.bForce =
false;
1562 info.version.bForce =
true;
1565 file.szSystem = parser.value(oSystem);
1566 file.szArchitecture = parser.value(oArch);
1567 file.szMd5sum = parser.value(oMd5);
1568 file.szFileName = parser.value(oFileName);
1570 QString szPackageFile = parser.value(oPackageFile);
1571 if(!szPackageFile.isEmpty()) {
1572 QFileInfo fi(szPackageFile);
1573 if(file.szFileName.isEmpty())
1574 file.szFileName = fi.fileName();
1575 if(file.szMd5sum.isEmpty())
1578 QCryptographicHash md5sum(QCryptographicHash::Md5);
1579 QFile app(szPackageFile);
1580 if(app.open(QIODevice::ReadOnly))
1582 if(md5sum.addData(&app))
1584 file.szMd5sum = md5sum.result().toHex();
1588 qCritical(log) <<
"Don't open package file:" << szPackageFile;
1593 if(file.szMd5sum.isEmpty())
1594 qWarning(log) <<
"Md5 is empty. please set -c or --md5 or -p";
1602 if(file.szFileName.isEmpty())
1603 file.szFileName = szFileName;
1605 QString szUrls = parser.value(oUrl);
1606 foreach(
auto u, szUrls.split(QRegularExpression(
"[;,]")))
1608 file.urls.push_back(QUrl(u));
1611 info.files.append(file);
1616void CFrmUpdater::showEvent(QShowEvent *event)
1619 if(!m_StateMachine.isRunning())
1620 m_StateMachine.start();
1623void CFrmUpdater::slotShowWindow(QSystemTrayIcon::ActivationReason reason)
1626#if defined(Q_OS_ANDROID)
1634bool CFrmUpdater::CheckPrompt(
const QString &szVersion)
1636 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1637 QSettings::IniFormat);
1638 QString version = set.value(
"Updater/Version", m_szCurrentVersion).toString();
1639 set.setValue(
"Updater/Version", szVersion);
1644 return !ui->cbPrompt->isChecked();
1648void CFrmUpdater::on_cbPrompt_clicked(
bool checked)
1650 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1651 QSettings::IniFormat);
1652 set.setValue(
"Updater/Prompt", checked);
1655void CFrmUpdater::on_cbHomePage_clicked(
bool checked)
1657 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
1658 QSettings::IniFormat);
1659 set.setValue(
"Updater/ShowHomePage", checked);
1664 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.