215 lines
7.3 KiB
C++
215 lines
7.3 KiB
C++
// Copyright 2007-2023 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "VersionCheck.h"
|
|
|
|
#include "MainWindow.h"
|
|
#include "Utils.h"
|
|
#include "WebFetch.h"
|
|
#include "Global.h"
|
|
|
|
#ifdef Q_OS_WIN
|
|
# include "win.h"
|
|
#endif
|
|
|
|
#include <QtCore/QUrlQuery>
|
|
#include <QtWidgets/QMessageBox>
|
|
#include <QtXml/QDomDocument>
|
|
|
|
#include <QtConcurrent/QtConcurrent>
|
|
|
|
#ifdef Q_OS_WIN
|
|
# include <shellapi.h>
|
|
# include <softpub.h>
|
|
#endif
|
|
|
|
VersionCheck::VersionCheck(bool autocheck, QObject *p, bool focus) : QObject(p), m_preparationWatcher() {
|
|
connect(&m_preparationWatcher, &QFutureWatcher< void >::finished, this, &VersionCheck::performRequest);
|
|
|
|
QFuture< void > future = QtConcurrent::run([this, autocheck, focus]() {
|
|
m_requestURL.setPath(focus ? QLatin1String("/v1/banner") : QLatin1String("/v1/version-check"));
|
|
|
|
QList< QPair< QString, QString > > queryItems;
|
|
queryItems << qMakePair(QString::fromLatin1("ver"),
|
|
QString::fromLatin1(QUrl::toPercentEncoding(Version::getRelease())));
|
|
#if defined(Q_OS_WIN)
|
|
# if defined(Q_OS_WIN64)
|
|
queryItems << qMakePair(QString::fromLatin1("os"), QString::fromLatin1("WinX64"));
|
|
# else
|
|
queryItems << qMakePair(QString::fromLatin1("os"), QString::fromLatin1("Win32"));
|
|
# endif
|
|
#elif defined(Q_OS_MAC)
|
|
# if defined(USE_MAC_UNIVERSAL)
|
|
queryItems << qMakePair(QString::fromLatin1("os"), QString::fromLatin1("MacOSX-Universal"));
|
|
# else
|
|
queryItems << qMakePair(QString::fromLatin1("os"), QString::fromLatin1("MacOSX"));
|
|
# endif
|
|
#else
|
|
queryItems << qMakePair(QString::fromLatin1("os"), QString::fromLatin1("Unix"));
|
|
#endif
|
|
if (!Global::get().s.bUsage)
|
|
queryItems << qMakePair(QString::fromLatin1("nousage"), QString::fromLatin1("1"));
|
|
if (autocheck)
|
|
queryItems << qMakePair(QString::fromLatin1("auto"), QString::fromLatin1("1"));
|
|
|
|
queryItems << qMakePair(QString::fromLatin1("locale"), Global::get().s.qsLanguage.isEmpty()
|
|
? QLocale::system().name()
|
|
: Global::get().s.qsLanguage);
|
|
|
|
QFile f(qApp->applicationFilePath());
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
qWarning("VersionCheck: Failed to open binary");
|
|
} else {
|
|
QByteArray a = f.readAll();
|
|
if (a.size() < 1) {
|
|
qWarning("VersionCheck: suspiciously small binary");
|
|
} else {
|
|
QCryptographicHash qch(QCryptographicHash::Sha1);
|
|
qch.addData(a);
|
|
queryItems << qMakePair(QString::fromLatin1("sha1"), QString::fromLatin1(qch.result().toHex()));
|
|
}
|
|
}
|
|
|
|
QUrlQuery query;
|
|
query.setQueryItems(queryItems);
|
|
m_requestURL.setQuery(query);
|
|
});
|
|
|
|
m_preparationWatcher.setFuture(future);
|
|
}
|
|
|
|
void VersionCheck::performRequest() {
|
|
WebFetch::fetch(QLatin1String("update"), m_requestURL, this, SLOT(fetched(QByteArray, QUrl)));
|
|
}
|
|
|
|
void VersionCheck::fetched(QByteArray a, QUrl url) {
|
|
if (!a.isNull()) {
|
|
if (!a.isEmpty()) {
|
|
#ifdef SNAPSHOT_BUILD
|
|
if (url.path() == QLatin1String("/v1/banner")) {
|
|
Global::get().mw->msgBox(QString::fromUtf8(a));
|
|
} else if (url.path() == QLatin1String("/v1/version-check")) {
|
|
# ifndef Q_OS_WIN
|
|
Global::get().mw->msgBox(QString::fromUtf8(a));
|
|
# else
|
|
QDomDocument qdd;
|
|
qdd.setContent(a);
|
|
|
|
QDomElement elem = qdd.firstChildElement(QLatin1String("p"));
|
|
elem = elem.firstChildElement(QLatin1String("a"));
|
|
|
|
QUrl fetch = QUrl(elem.attribute(QLatin1String("href")));
|
|
fetch.setHost(QString());
|
|
fetch.setScheme(QString());
|
|
if (!fetch.isValid()) {
|
|
Global::get().mw->msgBox(QString::fromUtf8(a));
|
|
} else {
|
|
QString filename = Global::get().qdBasePath.absoluteFilePath(QLatin1String("Snapshots/")
|
|
+ QFileInfo(fetch.path()).fileName());
|
|
|
|
QFile qf(filename);
|
|
if (qf.exists()) {
|
|
std::wstring native = QDir::toNativeSeparators(filename).toStdWString();
|
|
|
|
WINTRUST_FILE_INFO file;
|
|
ZeroMemory(&file, sizeof(file));
|
|
file.cbStruct = sizeof(file);
|
|
file.pcwszFilePath = native.c_str();
|
|
|
|
WINTRUST_DATA data;
|
|
ZeroMemory(&data, sizeof(data));
|
|
data.cbStruct = sizeof(data);
|
|
data.dwUIChoice = WTD_UI_NONE;
|
|
data.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
data.dwUnionChoice = WTD_CHOICE_FILE;
|
|
data.pFile = &file;
|
|
data.dwProvFlags = WTD_SAFER_FLAG | WTD_USE_DEFAULT_OSVER_CHECK;
|
|
data.dwUIContext = WTD_UICONTEXT_INSTALL;
|
|
|
|
static GUID guid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
|
|
|
LONG ts = WinVerifyTrust(0, &guid, &data);
|
|
|
|
if (ts == 0) {
|
|
if (QMessageBox::question(
|
|
Global::get().mw, tr("Upgrade Mumble"),
|
|
tr("A new version of Mumble has been detected and automatically downloaded. It is "
|
|
"recommended that you either upgrade to this version, or downgrade to the "
|
|
"latest stable release. Do you want to launch the installer now?"),
|
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
|
|
== QMessageBox::Yes) {
|
|
SHELLEXECUTEINFOW execinfo;
|
|
std::wstring filenative = filename.toStdWString();
|
|
std::wstring dirnative = QDir::toNativeSeparators(QDir::tempPath()).toStdWString();
|
|
ZeroMemory(&execinfo, sizeof(execinfo));
|
|
execinfo.cbSize = sizeof(execinfo);
|
|
execinfo.lpFile = filenative.c_str();
|
|
execinfo.lpDirectory = dirnative.c_str();
|
|
execinfo.nShow = SW_NORMAL;
|
|
|
|
if (ShellExecuteExW(&execinfo)) {
|
|
Global::get().mw->forceQuit = true;
|
|
qApp->closeAllWindows();
|
|
} else {
|
|
Global::get().mw->msgBox(tr("Failed to launch snapshot installer."));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
Global::get().mw->msgBox(
|
|
tr("Corrupt download of new version detected. Automatically removed."));
|
|
qf.remove();
|
|
}
|
|
|
|
// Delete all but the N most recent snapshots
|
|
size_t numberOfSnapshotsToKeep = 1;
|
|
|
|
QDir snapdir(Global::get().qdBasePath.absolutePath() + QLatin1String("/Snapshots/"), QString(),
|
|
QDir::Name, QDir::Files);
|
|
|
|
foreach (const QFileInfo fileInfo,
|
|
snapdir.entryInfoList(QStringList(), QDir::NoFilter, QDir::Time)) {
|
|
if (numberOfSnapshotsToKeep) {
|
|
--numberOfSnapshotsToKeep;
|
|
continue;
|
|
}
|
|
|
|
qWarning() << "Purging old snapshot" << fileInfo.fileName();
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
file.remove();
|
|
}
|
|
} else {
|
|
Global::get().mw->msgBox(tr("Downloading new snapshot from %1 to %2")
|
|
.arg(fetch.toString().toHtmlEscaped(), filename.toHtmlEscaped()));
|
|
WebFetch::fetch(QLatin1String("dl"), fetch, this, SLOT(fetched(QByteArray, QUrl)));
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
QString filename = Global::get().qdBasePath.absoluteFilePath(QLatin1String("Snapshots/")
|
|
+ QFileInfo(url.path()).fileName());
|
|
|
|
QFile qf(filename);
|
|
if (qf.open(QIODevice::WriteOnly)) {
|
|
qf.write(a);
|
|
qf.close();
|
|
new VersionCheck(true, Global::get().mw);
|
|
} else {
|
|
Global::get().mw->msgBox(tr("Failed to write new version to disk."));
|
|
}
|
|
# endif
|
|
}
|
|
#else
|
|
Q_UNUSED(url);
|
|
Global::get().mw->msgBox(QString::fromUtf8(a));
|
|
#endif
|
|
}
|
|
} else {
|
|
Global::get().mw->msgBox(tr("Mumble failed to retrieve version information from the central server."));
|
|
}
|
|
|
|
deleteLater();
|
|
}
|