<rect>
<x>0</x>
<y>0</y>
- <width>622</width>
+ <width>642</width>
<height>364</height>
</rect>
</property>
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
- <property name="margin">
- <number>18</number>
+ <property name="leftMargin">
+ <number>32</number>
+ </property>
+ <property name="topMargin">
+ <number>42</number>
+ </property>
+ <property name="rightMargin">
+ <number>32</number>
+ </property>
+ <property name="bottomMargin">
+ <number>32</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
<widget class="QLabel" name="statusLabel">
<property name="font">
<font>
</property>
</spacer>
</item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Latest version available:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="labelVersionLatest">
+ <property name="text">
+ <string>(Unknown)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Currently installed version:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="labelVersionInstalled">
+ <property name="text">
+ <string>(Unknown)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="3">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>15</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>15</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="infoLabel">
+ <property name="text">
+ <string>$(INFORMATION)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QPushButton" name="retryButton">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Retry</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../res/Icons.qrc">
+ <normaloff>:/icons/arrow_refresh.png</normaloff>:/icons/arrow_refresh.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
</layout>
</item>
</layout>
<include location="../res/Images.qrc"/>
<include location="../res/Images.qrc"/>
<include location="../res/Images.qrc"/>
+ <include location="../res/Images.qrc"/>
+ <include location="../res/Images.qrc"/>
+ <include location="../res/Images.qrc"/>
</resources>
<connections>
<connection>
#include "Dialog_Update.h"
+#include "Global.h"
+#include "Resource.h"
+
#include <QClipboard>
#include <QFileDialog>
#include <QTimer>
+#include <QProcess>
+#include <QUuid>
+#include <QDate>
+#include <QRegExp>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QCloseEvent>
#include <Windows.h>
+///////////////////////////////////////////////////////////////////////////////
+
+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:<br><a href=\"%1\">%1</a>").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);
+}