mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-01-07 07:37:24 +03:00
parent
b0fe6e6c59
commit
3ebdb50457
2 changed files with 70 additions and 13 deletions
|
@ -56,6 +56,11 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QScopeGuard>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/sessionstatus.h"
|
#include "base/bittorrent/sessionstatus.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
@ -113,7 +118,11 @@ namespace
|
||||||
|
|
||||||
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
|
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
const QString PYTHON_INSTALLER_URL = u"https://www.python.org/ftp/python/3.13.0/python-3.13.0-amd64.exe"_s;
|
const QString PYTHON_INSTALLER_URL = u"https://www.python.org/ftp/python/3.13.0/python-3.13.0-amd64.exe"_s;
|
||||||
|
const QByteArray PYTHON_INSTALLER_MD5 = QByteArrayLiteral("f5e5d48ba86586d4bef67bcb3790d339");
|
||||||
|
const QByteArray PYTHON_INSTALLER_SHA3_512 = QByteArrayLiteral("28ed23b82451efa5ec87e5dd18d7dacb9bc4d0a3643047091e5a687439f7e03a1c6e60ec64ee1210a0acaf2e5012504ff342ff27e5db108db05407e62aeff2f1");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, const QString &titleSuffix)
|
MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, const QString &titleSuffix)
|
||||||
|
@ -1899,33 +1908,79 @@ void MainWindow::installPython()
|
||||||
, this, &MainWindow::pythonDownloadFinished);
|
, this, &MainWindow::pythonDownloadFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::verifyPythonInstaller(const Path &installerPath) const
|
||||||
|
{
|
||||||
|
// Verify installer hash
|
||||||
|
// Python.org only provides MD5 hash but MD5 is already broken and doesn't guarantee file is not tampered.
|
||||||
|
// Therefore, MD5 is only included to prove that the hash is still the same with upstream and we rely on
|
||||||
|
// SHA3-512 for the main check.
|
||||||
|
|
||||||
|
QFile file {installerPath.data()};
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
LogMsg((tr("Failed to open Python installer. File: \"%1\".").arg(installerPath.toString())), Log::WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCryptographicHash md5Hash {QCryptographicHash::Md5};
|
||||||
|
md5Hash.addData(&file);
|
||||||
|
if (const QByteArray hashHex = md5Hash.result().toHex(); hashHex != PYTHON_INSTALLER_MD5)
|
||||||
|
{
|
||||||
|
LogMsg((tr("Failed MD5 hash check for Python installer. File: \"%1\". Result hash: \"%2\". Expected hash: \"%3\".")
|
||||||
|
.arg(installerPath.toString(), QString::fromLatin1(hashHex), QString::fromLatin1(PYTHON_INSTALLER_MD5)))
|
||||||
|
, Log::WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.seek(0);
|
||||||
|
|
||||||
|
QCryptographicHash sha3Hash {QCryptographicHash::Sha3_512};
|
||||||
|
sha3Hash.addData(&file);
|
||||||
|
if (const QByteArray hashHex = sha3Hash.result().toHex(); hashHex != PYTHON_INSTALLER_SHA3_512)
|
||||||
|
{
|
||||||
|
LogMsg((tr("Failed SHA3-512 hash check for Python installer. File: \"%1\". Result hash: \"%2\". Expected hash: \"%3\".")
|
||||||
|
.arg(installerPath.toString(), QString::fromLatin1(hashHex), QString::fromLatin1(PYTHON_INSTALLER_SHA3_512)))
|
||||||
|
, Log::WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
||||||
{
|
{
|
||||||
const auto restoreWidgetsState = [this]
|
auto restoreWidgetsGuard = qScopeGuard([this]
|
||||||
{
|
{
|
||||||
m_ui->actionSearchWidget->setEnabled(true);
|
m_ui->actionSearchWidget->setEnabled(true);
|
||||||
m_ui->actionSearchWidget->setToolTip({});
|
m_ui->actionSearchWidget->setToolTip({});
|
||||||
setCursor(Qt::ArrowCursor);
|
setCursor(Qt::ArrowCursor);
|
||||||
};
|
});
|
||||||
|
|
||||||
if (result.status != Net::DownloadStatus::Success)
|
if (result.status != Net::DownloadStatus::Success)
|
||||||
{
|
{
|
||||||
restoreWidgetsState();
|
|
||||||
QMessageBox::warning(
|
QMessageBox::warning(
|
||||||
this, tr("Download error")
|
this, tr("Download error")
|
||||||
, tr("Python setup could not be downloaded, reason: %1.\nPlease install it manually.")
|
, tr("Python installer could not be downloaded. Error: %1.\nPlease install it manually.")
|
||||||
.arg(result.errorString));
|
.arg(result.errorString));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path exePath = result.filePath + u".exe";
|
const Path exePath = result.filePath + u".exe";
|
||||||
Utils::Fs::renameFile(result.filePath, exePath);
|
if (!Utils::Fs::renameFile(result.filePath, exePath))
|
||||||
|
{
|
||||||
|
LogMsg(tr("Rename Python installer failed. Source: \"%1\". Destination: \"%2\".")
|
||||||
|
.arg(result.filePath.toString(), exePath.toString())
|
||||||
|
, Log::WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifyPythonInstaller(exePath))
|
||||||
|
return;
|
||||||
|
|
||||||
// launch installer
|
// launch installer
|
||||||
auto *installer = new QProcess(this);
|
auto *installer = new QProcess(this);
|
||||||
installer->connect(installer, &QProcess::finished, this, [this, exePath, installer, restoreWidgetsState](const int exitCode, const QProcess::ExitStatus exitStatus)
|
installer->connect(installer, &QProcess::finished, this, [this, exePath, installer, restoreWidgetsGuard = std::move(restoreWidgetsGuard)](const int exitCode, const QProcess::ExitStatus exitStatus)
|
||||||
{
|
{
|
||||||
restoreWidgetsState();
|
|
||||||
installer->deleteLater();
|
installer->deleteLater();
|
||||||
|
|
||||||
if ((exitStatus == QProcess::NormalExit) && (exitCode == 0))
|
if ((exitStatus == QProcess::NormalExit) && (exitCode == 0))
|
||||||
|
|
|
@ -133,9 +133,6 @@ private slots:
|
||||||
void optionsSaved();
|
void optionsSaved();
|
||||||
void toggleAlternativeSpeeds();
|
void toggleAlternativeSpeeds();
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
void pythonDownloadFinished(const Net::DownloadResult &result);
|
|
||||||
#endif
|
|
||||||
void addToolbarContextMenu();
|
void addToolbarContextMenu();
|
||||||
void manageCookies();
|
void manageCookies();
|
||||||
|
|
||||||
|
@ -184,12 +181,12 @@ private slots:
|
||||||
#else
|
#else
|
||||||
void toggleVisibility();
|
void toggleVisibility();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
void pythonDownloadFinished(const Net::DownloadResult &result);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void populateDesktopIntegrationMenu();
|
void populateDesktopIntegrationMenu();
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
void installPython();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void closeEvent(QCloseEvent *) override;
|
void closeEvent(QCloseEvent *) override;
|
||||||
void showEvent(QShowEvent *) override;
|
void showEvent(QShowEvent *) override;
|
||||||
|
@ -204,6 +201,11 @@ private:
|
||||||
void refreshWindowTitle();
|
void refreshWindowTitle();
|
||||||
void refreshTrayIconTooltip();
|
void refreshTrayIconTooltip();
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
void installPython();
|
||||||
|
bool verifyPythonInstaller(const Path &installerPath) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
Ui::MainWindow *m_ui = nullptr;
|
Ui::MainWindow *m_ui = nullptr;
|
||||||
|
|
||||||
QString m_windowTitle;
|
QString m_windowTitle;
|
||||||
|
|
Loading…
Reference in a new issue