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,27 +143,20 @@
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::updateCompleted(void)
+void UpdateDialog::closeEvent(QCloseEvent *event)
{
- statusLabel->setText("No new updates avialbale. Your version of LameXP is up-to-date.");
+ if(!closeButton->isEnabled()) event->ignore();
+}
+
+void UpdateDialog::updateInit(void)
+{
+ 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"},