From: lordmulder Date: Sun, 28 Nov 2010 21:18:07 +0000 (+0100) Subject: Actually implement the auto-updater. X-Git-Tag: Release_400~179 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=b59bb2399a83c7dce56f5015d8a4df7e4bc28aa9;p=lamexp%2FLameXP.git Actually implement the auto-updater. --- diff --git a/gui/UpdateDialog.ui b/gui/UpdateDialog.ui index 35b4af31..a0937d3f 100644 --- a/gui/UpdateDialog.ui +++ b/gui/UpdateDialog.ui @@ -6,7 +6,7 @@ 0 0 - 622 + 642 364 @@ -143,28 +143,21 @@ 0 - - 18 + + 32 + + + 42 + + + 32 + + + 32 - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 40 - - - - - @@ -200,6 +193,163 @@ + + + + + + Latest version available: + + + + + + + (Unknown) + + + + + + + Currently installed version: + + + + + + + (Unknown) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + $(INFORMATION) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 100 + 0 + + + + Retry + + + + :/icons/arrow_refresh.png:/icons/arrow_refresh.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -331,6 +481,9 @@ + + + diff --git a/res/Icons.qrc b/res/Icons.qrc index 0f8a5553..ab39e215 100644 --- a/res/Icons.qrc +++ b/res/Icons.qrc @@ -9,6 +9,7 @@ icons/application_xp_terminal.png icons/arrow_down.png icons/arrow_up.png + icons/arrow_refresh.png icons/bin.png icons/bomb.png icons/calendar.png diff --git a/res/Tools.qrc b/res/Tools.qrc index 14875932..0154dfe6 100644 --- a/res/Tools.qrc +++ b/res/Tools.qrc @@ -5,6 +5,7 @@ tools/faad.exe tools/flac.exe tools/gpgv.exe + tools/gpgv.gpg tools/lame.exe tools/MAC.exe tools/mediainfo_icl11.exe diff --git a/res/tools/gpgv.gpg b/res/tools/gpgv.gpg new file mode 100644 index 00000000..8ac5f577 Binary files /dev/null and b/res/tools/gpgv.gpg differ diff --git a/src/Config.h b/src/Config.h index bb9bcd20..c1c8d77c 100644 --- a/src/Config.h +++ b/src/Config.h @@ -25,7 +25,7 @@ #define VER_LAMEXP_MAJOR 4 #define VER_LAMEXP_MINOR_HI 0 #define VER_LAMEXP_MINOR_LO 0 -#define VER_LAMEXP_BUILD 89 +#define VER_LAMEXP_BUILD 95 #define VER_LAMEXP_SUFFIX TechPreview /* diff --git a/src/Dialog_Update.cpp b/src/Dialog_Update.cpp index 0d86a2fd..e9ccf998 100644 --- a/src/Dialog_Update.cpp +++ b/src/Dialog_Update.cpp @@ -21,49 +21,424 @@ #include "Dialog_Update.h" +#include "Global.h" +#include "Resource.h" + #include #include #include +#include +#include +#include +#include +#include +#include +#include #include +/////////////////////////////////////////////////////////////////////////////// + +static const char *section_id = "LameXP"; + +static const char *mirror_url_postfix = "update_beta.ver"; + +static const char *mirrors[] = +{ + "http://mulder.dummwiedeutsch.de/", + "http://mulder.brhack.net/", + "http://free.pages.at/borschdfresser/", + "http://mplayer.savedonthe.net/", + "http://www.tricksoft.de/", + NULL +}; + +/////////////////////////////////////////////////////////////////////////////// + +class UpdateInfo +{ +public: + UpdateInfo(void) + : + m_buildNo(0), + m_buildDate(1900, 1, 1), + m_downloadSite(""), + m_downloadAddress(""), + m_downloadFilename(""), + m_downloadFilecode("") + { + } + + unsigned int m_buildNo; + QDate m_buildDate; + QString m_downloadSite; + QString m_downloadAddress; + QString m_downloadFilename; + QString m_downloadFilecode; +}; + +/////////////////////////////////////////////////////////////////////////////// + UpdateDialog::UpdateDialog(QWidget *parent) : - QDialog(parent) + QDialog(parent), + m_binaryWGet(lamexp_lookup_tool("wget.exe")), + m_binaryGnuPG(lamexp_lookup_tool("gpgv.exe")), + m_binaryUpdater(lamexp_lookup_tool("wupdate.exe")), + m_binaryKeys(lamexp_lookup_tool("gpgv.gpg")), + m_updateInfo(NULL) { + if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryUpdater.isEmpty() || m_binaryKeys.isEmpty()) + { + throw "Tools not initialized correctly!"; + } + //Init the dialog, from the .ui file setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); - setMinimumSize(size()); - setMaximumHeight(height()); //Disable "X" button HMENU hMenu = GetSystemMenu((HWND) winId(), FALSE); EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); - //Init flags - m_clipboardUsed = false; + //Enable button + connect(retryButton, SIGNAL(clicked()), this, SLOT(checkForUpdates())); + connect(installButton, SIGNAL(clicked()), this, SLOT(applyUpdate())); + connect(infoLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString))); } UpdateDialog::~UpdateDialog(void) { - if(m_clipboardUsed) - { - QApplication::clipboard()->clear(); - } + LAMEXP_DELETE(m_updateInfo); } void UpdateDialog::showEvent(QShowEvent *event) { + QDialog::showEvent(event); + statusLabel->setText("Checking for new updates online, please wait..."); - QTimer::singleShot(8000, this, SLOT(updateCompleted())); + labelVersionInstalled->setText(QString("Build %1 (%2)").arg(QString::number(lamexp_version_build()), lamexp_version_date().toString(Qt::ISODate))); + labelVersionLatest->setText("(Unknown)"); + + QTimer::singleShot(0, this, SLOT(updateInit())); installButton->setEnabled(false); closeButton->setEnabled(false); + retryButton->setEnabled(false); + retryButton->hide(); + infoLabel->hide(); + + for(int i = 0; mirrors[i]; i++) + { + progressBar->setMaximum(i+2); + } + + progressBar->setValue(0); +} + +void UpdateDialog::closeEvent(QCloseEvent *event) +{ + if(!closeButton->isEnabled()) event->ignore(); } -void UpdateDialog::updateCompleted(void) +void UpdateDialog::updateInit(void) { - statusLabel->setText("No new updates avialbale. Your version of LameXP is up-to-date."); + setMinimumSize(size()); + setMaximumHeight(height()); + + checkForUpdates(); +} + +void UpdateDialog::checkForUpdates(void) +{ + bool success = false; + m_updateInfo = new UpdateInfo; + + progressBar->setValue(0); + installButton->setEnabled(false); + closeButton->setEnabled(false); + retryButton->setEnabled(false); + if(infoLabel->isVisible()) infoLabel->hide(); + + QApplication::processEvents(); + QApplication::setOverrideCursor(Qt::WaitCursor); + + for(int i = 0; mirrors[i]; i++) + { + progressBar->setValue(i+1); + if(tryUpdateMirror(m_updateInfo, mirrors[i])) + { + success = true; + break; + } + } + + QApplication::restoreOverrideCursor(); + + if(!success) + { + if(!retryButton->isVisible()) retryButton->show(); + closeButton->setEnabled(true); + retryButton->setEnabled(true); + statusLabel->setText("Failed to fetch update information. Check internet connection!"); + progressBar->setValue(progressBar->maximum()); + LAMEXP_DELETE(m_updateInfo); + PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC); + return; + } + + labelVersionLatest->setText(QString("Build %1 (%2)").arg(QString::number(m_updateInfo->m_buildNo), m_updateInfo->m_buildDate.toString(Qt::ISODate))); + infoLabel->show(); + infoLabel->setText(QString("More information available at:
%1").arg(m_updateInfo->m_downloadSite)); + QApplication::processEvents(); + + if(m_updateInfo->m_buildNo > lamexp_version_build()) + { + installButton->setEnabled(true); + statusLabel->setText("A new version of LameXP is available. Update highly recommended!"); + MessageBeep(MB_ICONINFORMATION); + } + else if(m_updateInfo->m_buildNo == lamexp_version_build()) + { + statusLabel->setText("No new updates avialbale. Your version of LameXP is up-to-date."); + MessageBeep(MB_ICONINFORMATION); + } + else + { + statusLabel->setText("Your version appears to be newer than the latest release."); + MessageBeep(MB_ICONEXCLAMATION); + } + closeButton->setEnabled(true); + if(retryButton->isVisible()) retryButton->hide(); + progressBar->setValue(progressBar->maximum()); } +bool UpdateDialog::tryUpdateMirror(UpdateInfo *updateInfo, const QString &url) +{ + bool success = false; + + QUuid uuid = QUuid::createUuid(); + QString outFileVersionInfo = QString("%1/%2.ver").arg(QDir::tempPath(), uuid.toString()); + QString outFileSignature = QString("%1/%2.sig").arg(QDir::tempPath(), uuid.toString()); + + qDebug("\nDownloading update info:"); + bool ok1 = getFile(QString("%1%2").arg(url,mirror_url_postfix), outFileVersionInfo); + + qDebug("\nDownloading signature file:"); + bool ok2 = getFile(QString("%1%2.sig").arg(url,mirror_url_postfix), outFileSignature); + + if(ok1 && ok2) + { + qDebug("\nDownload okay, checking signature:"); + if(checkSignature(outFileVersionInfo, outFileSignature)) + { + qDebug("\nSignature okay, parsing info:"); + success = parseVersionInfo(outFileVersionInfo, updateInfo); + } + else + { + qDebug("\nBad signature, take care!"); + } + } + else + { + qDebug("\nDownload has failed!"); + } + + QFile::remove(outFileVersionInfo); + QFile::remove(outFileSignature); + + return success; +} + +bool UpdateDialog::getFile(const QString &url, const QString &outFile) +{ + QFileInfo output(outFile); + output.setCaching(false); + + if(output.exists()) + { + QFile::remove(output.canonicalFilePath()); + if(output.exists()) + { + return false; + } + } + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.setReadChannel(QProcess::StandardOutput); + + QEventLoop loop; + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); + + process.start(m_binaryWGet, QStringList() << "-O" << output.absoluteFilePath() << url); + + if(!process.waitForStarted()) + { + return false; + } + + while(process.state() == QProcess::Running) + { + loop.exec(); + while(process.canReadLine()) + { + qDebug("WGet: %s", QString::fromLatin1(process.readLine()).simplified().toLatin1().constData()); + } + } + + qDebug("WGet: Exited with code %d", process.exitCode()); + return (process.exitCode() == 0) && output.exists() && output.isFile(); +} + +bool UpdateDialog::checkSignature(const QString &file, const QString &signature) +{ + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.setReadChannel(QProcess::StandardOutput); + + QEventLoop loop; + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); + + process.start(m_binaryGnuPG, QStringList() << "--homedir" << lamexp_temp_folder() << "--keyring" << QDir::toNativeSeparators(m_binaryKeys) << QDir::toNativeSeparators(signature) << QDir::toNativeSeparators(file)); + + if(!process.waitForStarted()) + { + return false; + } + + while(process.state() == QProcess::Running) + { + loop.exec(); + while(process.canReadLine()) + { + qDebug("GnuPG: %s", QString::fromLatin1(process.readLine()).simplified().toLatin1().constData()); + } + } + + qDebug("GnuPG: Exited with code %d", process.exitCode()); + return (process.exitCode() == 0); +} + +bool UpdateDialog::parseVersionInfo(const QString &file, UpdateInfo *updateInfo) +{ + + QRegExp value("^(\\w+)=(.+)$"); + QRegExp section("^\\[(.+)\\]$"); + + QFile data(file); + if(!data.open(QIODevice::ReadOnly)) + { + qWarning("Cannot open update info file for reading!"); + return false; + } + + bool inSection = false; + + while(!data.atEnd()) + { + QString line = QString::fromLatin1(data.readLine()).trimmed(); + if(section.indexIn(line) >= 0) + { + qDebug("Section: '%s'", section.cap(1).toLatin1().constData()); + inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0); + continue; + } + if(inSection && value.indexIn(line) >= 0) + { + qDebug("Value: '%s' ==> '%s'", value.cap(1).toLatin1().constData(), value.cap(2).toLatin1().constData()); + if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0) + { + bool ok = false; + unsigned int temp = value.cap(2).toUInt(&ok); + if(ok) updateInfo->m_buildNo = temp; + } + else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0) + { + QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); + if(temp.isValid()) updateInfo->m_buildDate = temp; + } + else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadSite = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadAddress = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilename = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilecode = value.cap(2).trimmed(); + } + } + } + + bool complete = true; + + if(!(updateInfo->m_buildNo > 0)) complete = false; + if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false; + if(updateInfo->m_downloadSite.isEmpty()) complete = false; + if(updateInfo->m_downloadAddress.isEmpty()) complete = false; + if(updateInfo->m_downloadFilename.isEmpty()) complete = false; + if(updateInfo->m_downloadFilecode.isEmpty()) complete = false; + + return complete; +} + +void UpdateDialog::linkActivated(const QString &link) +{ + QDesktopServices::openUrl(QUrl(link)); +} + +void UpdateDialog::applyUpdate(void) +{ + installButton->setEnabled(false); + closeButton->setEnabled(false); + retryButton->setEnabled(false); + + if(m_updateInfo) + { + statusLabel->setText("Update is being downloaded, please be patient..."); + QApplication::processEvents(); + + QProcess process; + QStringList args; + QEventLoop loop; + + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + + args << QString("/Location=%1").arg(m_updateInfo->m_downloadAddress); + args << QString("/Filename=%1").arg(m_updateInfo->m_downloadFilename); + args << QString("/TicketID=%1").arg(m_updateInfo->m_downloadFilecode); + args << QString("/ToFolder=%1").arg(QDir::toNativeSeparators(QApplication::applicationDirPath())); + args << QString("/AppTitle=LameXP (Build #%1)").arg(QString::number(m_updateInfo->m_buildNo)); + + QApplication::setOverrideCursor(Qt::WaitCursor); + process.start(m_binaryUpdater, args); + loop.exec(); + QApplication::restoreOverrideCursor(); + + if(process.exitCode() == 0) + { + statusLabel->setText("Update ready to install. Applicaion will quit..."); + QApplication::quit(); + } + else + { + statusLabel->setText("Update failed. Please try again or download manually!"); + } + } + + installButton->setEnabled(true); + closeButton->setEnabled(true); +} diff --git a/src/Dialog_Update.h b/src/Dialog_Update.h index 5958594a..7b200d80 100644 --- a/src/Dialog_Update.h +++ b/src/Dialog_Update.h @@ -25,6 +25,8 @@ #include +class UpdateInfo; + class UpdateDialog : public QDialog, private Ui::UpdateDialog { Q_OBJECT @@ -34,12 +36,25 @@ public: ~UpdateDialog(void); private slots: - void updateCompleted(void); + void updateInit(void); + void checkForUpdates(void); + void linkActivated(const QString &link); + void applyUpdate(void); protected: void showEvent(QShowEvent *event); - + void closeEvent(QCloseEvent *event); private: - bool m_clipboardUsed; + bool tryUpdateMirror(UpdateInfo *updateInfo, const QString &url); + bool getFile(const QString &url, const QString &outFile); + bool checkSignature(const QString &file, const QString &signature); + bool parseVersionInfo(const QString &file, UpdateInfo *updateInfo); + + UpdateInfo *m_updateInfo; + + const QString m_binaryWGet; + const QString m_binaryGnuPG; + const QString m_binaryUpdater; + const QString m_binaryKeys; }; diff --git a/src/Thread_Initialization.cpp b/src/Thread_Initialization.cpp index edfd306c..1d6bfe0f 100644 --- a/src/Thread_Initialization.cpp +++ b/src/Thread_Initialization.cpp @@ -44,6 +44,7 @@ static const struct lamexp_tool_t g_lamexp_tools[] = {"09e5a07555a24b8c9d6af880b81eb8ed75be16fd", "faad.exe"}, {"070bf98f78e572a97e4703ef5720c682567a6a56", "flac.exe"}, {"cf379081035ae6bfb6f7bc22f13bfb7ac6302ac5", "gpgv.exe"}, + {"d837bf6ee4dab557d8b02d46c75a24e58980fffa", "gpgv.gpg"}, {"143fc001a2f6c56fe1b9e6f8a2eb2b53b9e1e504", "lame.exe"}, {"775b260b3f64101beaeb317b74746f9bccdab842", "MAC.exe"}, {"e8719fbfd7b690b3e518489f7aae3915305711c2", "mediainfo_icl11.exe"},