2019-09-08 22:27:57 +02:00
|
|
|
#include "Updates.hpp"
|
|
|
|
|
2019-09-15 15:45:04 +02:00
|
|
|
#include "common/Modes.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "common/NetworkRequest.hpp"
|
|
|
|
#include "common/Outcome.hpp"
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
#include "common/QLogging.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "common/Version.hpp"
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
#include "Settings.hpp"
|
2019-09-08 22:27:57 +02:00
|
|
|
#include "singletons/Paths.hpp"
|
|
|
|
#include "util/CombinePath.hpp"
|
|
|
|
#include "util/PostToThread.hpp"
|
|
|
|
|
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QProcess>
|
2019-10-07 19:53:46 +02:00
|
|
|
#include <QRegularExpression>
|
2019-09-08 22:27:57 +02:00
|
|
|
|
|
|
|
namespace chatterino {
|
|
|
|
namespace {
|
|
|
|
QString currentBranch()
|
|
|
|
{
|
|
|
|
return getSettings()->betaUpdates ? "beta" : "stable";
|
|
|
|
}
|
2019-10-07 19:53:46 +02:00
|
|
|
|
|
|
|
/// Checks if the online version is newer or older than the current version.
|
|
|
|
bool isDowngradeOf(const QString &online, const QString ¤t)
|
|
|
|
{
|
|
|
|
static auto matchVersion =
|
|
|
|
QRegularExpression(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?)");
|
|
|
|
|
|
|
|
// Versions are just strings, they don't need to follow a specific
|
|
|
|
// format so we can only assume if one version is newer than another
|
|
|
|
// one.
|
|
|
|
|
|
|
|
// We match x.x.x.x with each version level being optional.
|
|
|
|
|
|
|
|
auto onlineMatch = matchVersion.match(online);
|
|
|
|
auto currentMatch = matchVersion.match(current);
|
|
|
|
|
|
|
|
for (int i = 1; i <= 4; i++)
|
|
|
|
{
|
|
|
|
if (onlineMatch.captured(i).toInt() <
|
|
|
|
currentMatch.captured(i).toInt())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (onlineMatch.captured(i).toInt() >
|
|
|
|
currentMatch.captured(i).toInt())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-08 22:27:57 +02:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
Updates::Updates()
|
|
|
|
: currentVersion_(CHATTERINO_VERSION)
|
|
|
|
, updateGuideLink_("https://chatterino.com")
|
|
|
|
{
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoUpdate) << "init UpdateManager";
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
|
2019-10-07 22:42:34 +02:00
|
|
|
Updates &Updates::instance()
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
// fourtf: don't add this class to the application class
|
|
|
|
static Updates instance;
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString &Updates::getCurrentVersion() const
|
|
|
|
{
|
|
|
|
return currentVersion_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString &Updates::getOnlineVersion() const
|
|
|
|
{
|
|
|
|
return onlineVersion_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Updates::installUpdates()
|
|
|
|
{
|
|
|
|
if (this->status_ != UpdateAvailable)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
QMessageBox *box = new QMessageBox(
|
|
|
|
QMessageBox::Information, "Chatterino Update",
|
|
|
|
"A link will open in your browser. Download and install to update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->exec();
|
|
|
|
QDesktopServices::openUrl(this->updateExe_);
|
|
|
|
#elif defined Q_OS_LINUX
|
|
|
|
QMessageBox *box =
|
|
|
|
new QMessageBox(QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Automatic updates are currently not available on "
|
|
|
|
"linux. Please redownload the app to update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->exec();
|
|
|
|
QDesktopServices::openUrl(this->updateGuideLink_);
|
|
|
|
#elif defined Q_OS_WIN
|
|
|
|
if (getPaths()->isPortable())
|
|
|
|
{
|
|
|
|
QMessageBox *box =
|
|
|
|
new QMessageBox(QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Chatterino is downloading the update "
|
|
|
|
"in the background and will run the "
|
|
|
|
"updater once it is finished.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->show();
|
|
|
|
|
|
|
|
NetworkRequest(this->updatePortable_)
|
|
|
|
.timeout(600000)
|
2019-09-19 19:03:50 +02:00
|
|
|
.onError([this](NetworkResult) {
|
2019-09-08 22:27:57 +02:00
|
|
|
this->setStatus_(DownloadFailed);
|
|
|
|
|
|
|
|
postToThread([] {
|
|
|
|
QMessageBox *box = new QMessageBox(
|
|
|
|
QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Failed while trying to download the update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->show();
|
|
|
|
box->raise();
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.onSuccess([this](auto result) -> Outcome {
|
|
|
|
QByteArray object = result.getData();
|
|
|
|
auto filename =
|
|
|
|
combinePath(getPaths()->miscDirectory, "update.zip");
|
|
|
|
|
|
|
|
QFile file(filename);
|
|
|
|
file.open(QIODevice::Truncate | QIODevice::WriteOnly);
|
|
|
|
|
|
|
|
if (file.write(object) == -1)
|
|
|
|
{
|
|
|
|
this->setStatus_(WriteFileFailed);
|
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
file.flush();
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
QProcess::startDetached(
|
|
|
|
combinePath(QCoreApplication::applicationDirPath(),
|
|
|
|
"updater.1/ChatterinoUpdater.exe"),
|
|
|
|
{filename, "restart"});
|
|
|
|
|
|
|
|
QApplication::exit(0);
|
|
|
|
return Success;
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
this->setStatus_(Downloading);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QMessageBox *box =
|
|
|
|
new QMessageBox(QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Chatterino is downloading the update "
|
|
|
|
"in the background and will run the "
|
|
|
|
"updater once it is finished.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->show();
|
|
|
|
|
|
|
|
NetworkRequest(this->updateExe_)
|
|
|
|
.timeout(600000)
|
2019-09-19 19:03:50 +02:00
|
|
|
.onError([this](NetworkResult) {
|
2019-09-08 22:27:57 +02:00
|
|
|
this->setStatus_(DownloadFailed);
|
|
|
|
|
|
|
|
QMessageBox *box = new QMessageBox(
|
|
|
|
QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Failed to download the update. \n\nTry manually "
|
|
|
|
"downloading the update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->exec();
|
|
|
|
})
|
|
|
|
.onSuccess([this](auto result) -> Outcome {
|
|
|
|
QByteArray object = result.getData();
|
2022-11-10 20:11:40 +01:00
|
|
|
auto filePath =
|
2019-09-08 22:27:57 +02:00
|
|
|
combinePath(getPaths()->miscDirectory, "Update.exe");
|
|
|
|
|
2022-11-10 20:11:40 +01:00
|
|
|
QFile file(filePath);
|
2019-09-08 22:27:57 +02:00
|
|
|
file.open(QIODevice::Truncate | QIODevice::WriteOnly);
|
|
|
|
|
|
|
|
if (file.write(object) == -1)
|
|
|
|
{
|
|
|
|
this->setStatus_(WriteFileFailed);
|
|
|
|
QMessageBox *box = new QMessageBox(
|
|
|
|
QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Failed to save the update file. This could be due to "
|
|
|
|
"window settings or antivirus software.\n\nTry "
|
|
|
|
"manually "
|
|
|
|
"downloading the update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->exec();
|
|
|
|
|
|
|
|
QDesktopServices::openUrl(this->updateExe_);
|
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
file.flush();
|
|
|
|
file.close();
|
|
|
|
|
2022-11-10 20:11:40 +01:00
|
|
|
if (QProcess::startDetached(filePath, {}))
|
2019-09-08 22:27:57 +02:00
|
|
|
{
|
|
|
|
QApplication::exit(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QMessageBox *box = new QMessageBox(
|
|
|
|
QMessageBox::Information, "Chatterino Update",
|
|
|
|
"Failed to execute update binary. This could be due to "
|
|
|
|
"window "
|
|
|
|
"settings or antivirus software.\n\nTry manually "
|
|
|
|
"downloading "
|
|
|
|
"the update.");
|
|
|
|
box->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
box->exec();
|
|
|
|
|
|
|
|
QDesktopServices::openUrl(this->updateExe_);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
this->setStatus_(Downloading);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void Updates::checkForUpdates()
|
|
|
|
{
|
2021-08-21 13:00:01 +02:00
|
|
|
auto version = Version::instance();
|
|
|
|
|
|
|
|
if (!version.isSupportedOS())
|
2020-08-24 12:02:56 +02:00
|
|
|
{
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoUpdate)
|
2020-08-24 12:02:56 +02:00
|
|
|
<< "Update checking disabled because OS doesn't appear to be one "
|
|
|
|
"of Windows, GNU/Linux or macOS.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-24 13:30:21 +02:00
|
|
|
// Disable updates on Flatpak
|
2021-08-21 13:00:01 +02:00
|
|
|
if (version.isFlatpak())
|
2021-07-24 13:30:21 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-08 10:32:55 +01:00
|
|
|
// Disable updates if on nightly
|
2019-10-07 22:42:34 +02:00
|
|
|
if (Modes::instance().isNightly)
|
2019-09-15 15:45:04 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-08 22:27:57 +02:00
|
|
|
QString url =
|
|
|
|
"https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/" +
|
|
|
|
currentBranch();
|
|
|
|
|
|
|
|
NetworkRequest(url)
|
|
|
|
.timeout(60000)
|
|
|
|
.onSuccess([this](auto result) -> Outcome {
|
|
|
|
auto object = result.parseJson();
|
|
|
|
/// Version available on every platform
|
|
|
|
QJsonValue version_val = object.value("version");
|
|
|
|
|
|
|
|
if (!version_val.isString())
|
|
|
|
{
|
|
|
|
this->setStatus_(SearchFailed);
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoUpdate) << "error updating";
|
2019-09-08 22:27:57 +02:00
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined Q_OS_WIN || defined Q_OS_MACOS
|
2020-05-10 13:13:47 +02:00
|
|
|
/// Downloads an installer for the new version
|
2019-09-08 22:27:57 +02:00
|
|
|
QJsonValue updateExe_val = object.value("updateexe");
|
|
|
|
if (!updateExe_val.isString())
|
|
|
|
{
|
|
|
|
this->setStatus_(SearchFailed);
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoUpdate) << "error updating";
|
2019-09-08 22:27:57 +02:00
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
this->updateExe_ = updateExe_val.toString();
|
2020-08-24 09:52:24 +02:00
|
|
|
|
|
|
|
# ifdef Q_OS_WIN
|
2019-09-08 22:27:57 +02:00
|
|
|
/// Windows portable
|
|
|
|
QJsonValue portable_val = object.value("portable_download");
|
|
|
|
if (!portable_val.isString())
|
|
|
|
{
|
|
|
|
this->setStatus_(SearchFailed);
|
2020-11-21 16:20:10 +01:00
|
|
|
qCDebug(chatterinoUpdate) << "error updating";
|
2019-09-08 22:27:57 +02:00
|
|
|
return Failure;
|
|
|
|
}
|
|
|
|
this->updatePortable_ = portable_val.toString();
|
2020-08-24 09:52:24 +02:00
|
|
|
# endif
|
|
|
|
|
|
|
|
#elif defined Q_OS_LINUX
|
2019-09-08 22:27:57 +02:00
|
|
|
QJsonValue updateGuide_val = object.value("updateguide");
|
|
|
|
if (updateGuide_val.isString())
|
|
|
|
{
|
|
|
|
this->updateGuideLink_ = updateGuide_val.toString();
|
|
|
|
}
|
2020-08-24 09:52:24 +02:00
|
|
|
#else
|
2019-09-08 22:27:57 +02:00
|
|
|
return Failure;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/// Current version
|
|
|
|
this->onlineVersion_ = version_val.toString();
|
|
|
|
|
|
|
|
/// Update available :)
|
|
|
|
if (this->currentVersion_ != this->onlineVersion_)
|
|
|
|
{
|
|
|
|
this->setStatus_(UpdateAvailable);
|
2019-10-07 19:53:46 +02:00
|
|
|
this->isDowngrade_ =
|
|
|
|
isDowngradeOf(this->onlineVersion_, this->currentVersion_);
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this->setStatus_(NoUpdateAvailable);
|
|
|
|
}
|
|
|
|
return Failure;
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
this->setStatus_(Searching);
|
|
|
|
}
|
|
|
|
|
|
|
|
Updates::Status Updates::getStatus() const
|
|
|
|
{
|
|
|
|
return this->status_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Updates::shouldShowUpdateButton() const
|
|
|
|
{
|
|
|
|
switch (this->getStatus())
|
|
|
|
{
|
|
|
|
case UpdateAvailable:
|
|
|
|
case SearchFailed:
|
|
|
|
case Downloading:
|
|
|
|
case DownloadFailed:
|
|
|
|
case WriteFileFailed:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Updates::isError() const
|
|
|
|
{
|
|
|
|
switch (this->getStatus())
|
|
|
|
{
|
|
|
|
case SearchFailed:
|
|
|
|
case DownloadFailed:
|
|
|
|
case WriteFileFailed:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-07 19:53:46 +02:00
|
|
|
bool Updates::isDowngrade() const
|
|
|
|
{
|
|
|
|
return this->isDowngrade_;
|
|
|
|
}
|
|
|
|
|
2019-09-08 22:27:57 +02:00
|
|
|
void Updates::setStatus_(Status status)
|
|
|
|
{
|
|
|
|
if (this->status_ != status)
|
|
|
|
{
|
|
|
|
this->status_ = status;
|
2020-11-08 12:02:19 +01:00
|
|
|
postToThread([this, status] {
|
|
|
|
this->statusUpdated.invoke(status);
|
|
|
|
});
|
2019-09-08 22:27:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace chatterino
|