1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2010 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "Dialog_Update.h"
28 #include <QFileDialog>
34 #include <QDesktopServices>
36 #include <QCloseEvent>
40 ///////////////////////////////////////////////////////////////////////////////
42 static const char *section_id = "LameXP";
44 static const char *mirror_url_postfix = "update_beta.ver";
46 static const char *mirrors[] =
48 "http://mulder.dummwiedeutsch.de/",
49 "http://mulder.brhack.net/",
50 "http://free.pages.at/borschdfresser/",
51 "http://mplayer.savedonthe.net/",
52 "http://www.tricksoft.de/",
56 ///////////////////////////////////////////////////////////////////////////////
64 m_buildDate(1900, 1, 1),
66 m_downloadAddress(""),
67 m_downloadFilename(""),
68 m_downloadFilecode("")
72 unsigned int m_buildNo;
74 QString m_downloadSite;
75 QString m_downloadAddress;
76 QString m_downloadFilename;
77 QString m_downloadFilecode;
80 ///////////////////////////////////////////////////////////////////////////////
82 UpdateDialog::UpdateDialog(QWidget *parent)
85 m_binaryWGet(lamexp_lookup_tool("wget.exe")),
86 m_binaryGnuPG(lamexp_lookup_tool("gpgv.exe")),
87 m_binaryUpdater(lamexp_lookup_tool("wupdate.exe")),
88 m_binaryKeys(lamexp_lookup_tool("gpgv.gpg")),
91 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryUpdater.isEmpty() || m_binaryKeys.isEmpty())
93 throw "Tools not initialized correctly!";
96 //Init the dialog, from the .ui file
98 setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint);
101 HMENU hMenu = GetSystemMenu((HWND) winId(), FALSE);
102 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
105 connect(retryButton, SIGNAL(clicked()), this, SLOT(checkForUpdates()));
106 connect(installButton, SIGNAL(clicked()), this, SLOT(applyUpdate()));
107 connect(infoLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)));
110 UpdateDialog::~UpdateDialog(void)
112 LAMEXP_DELETE(m_updateInfo);
115 void UpdateDialog::showEvent(QShowEvent *event)
117 QDialog::showEvent(event);
119 statusLabel->setText("Checking for new updates online, please wait...");
120 labelVersionInstalled->setText(QString("Build %1 (%2)").arg(QString::number(lamexp_version_build()), lamexp_version_date().toString(Qt::ISODate)));
121 labelVersionLatest->setText("(Unknown)");
123 QTimer::singleShot(0, this, SLOT(updateInit()));
124 installButton->setEnabled(false);
125 closeButton->setEnabled(false);
126 retryButton->setEnabled(false);
130 for(int i = 0; mirrors[i]; i++)
132 progressBar->setMaximum(i+2);
135 progressBar->setValue(0);
138 void UpdateDialog::closeEvent(QCloseEvent *event)
140 if(!closeButton->isEnabled()) event->ignore();
143 void UpdateDialog::updateInit(void)
145 setMinimumSize(size());
146 setMaximumHeight(height());
151 void UpdateDialog::checkForUpdates(void)
153 bool success = false;
154 m_updateInfo = new UpdateInfo;
156 progressBar->setValue(0);
157 installButton->setEnabled(false);
158 closeButton->setEnabled(false);
159 retryButton->setEnabled(false);
160 if(infoLabel->isVisible()) infoLabel->hide();
162 QApplication::processEvents();
163 QApplication::setOverrideCursor(Qt::WaitCursor);
165 for(int i = 0; mirrors[i]; i++)
167 progressBar->setValue(i+1);
168 if(tryUpdateMirror(m_updateInfo, mirrors[i]))
175 QApplication::restoreOverrideCursor();
179 if(!retryButton->isVisible()) retryButton->show();
180 closeButton->setEnabled(true);
181 retryButton->setEnabled(true);
182 statusLabel->setText("Failed to fetch update information. Check internet connection!");
183 progressBar->setValue(progressBar->maximum());
184 LAMEXP_DELETE(m_updateInfo);
185 PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
189 labelVersionLatest->setText(QString("Build %1 (%2)").arg(QString::number(m_updateInfo->m_buildNo), m_updateInfo->m_buildDate.toString(Qt::ISODate)));
191 infoLabel->setText(QString("More information available at:<br><a href=\"%1\">%1</a>").arg(m_updateInfo->m_downloadSite));
192 QApplication::processEvents();
194 if(m_updateInfo->m_buildNo > lamexp_version_build())
196 installButton->setEnabled(true);
197 statusLabel->setText("A new version of LameXP is available. Update highly recommended!");
198 MessageBeep(MB_ICONINFORMATION);
200 else if(m_updateInfo->m_buildNo == lamexp_version_build())
202 statusLabel->setText("No new updates avialbale. Your version of LameXP is up-to-date.");
203 MessageBeep(MB_ICONINFORMATION);
207 statusLabel->setText("Your version appears to be newer than the latest release.");
208 MessageBeep(MB_ICONEXCLAMATION);
211 closeButton->setEnabled(true);
212 if(retryButton->isVisible()) retryButton->hide();
213 progressBar->setValue(progressBar->maximum());
216 bool UpdateDialog::tryUpdateMirror(UpdateInfo *updateInfo, const QString &url)
218 bool success = false;
220 QUuid uuid = QUuid::createUuid();
221 QString outFileVersionInfo = QString("%1/%2.ver").arg(QDir::tempPath(), uuid.toString());
222 QString outFileSignature = QString("%1/%2.sig").arg(QDir::tempPath(), uuid.toString());
224 qDebug("\nDownloading update info:");
225 bool ok1 = getFile(QString("%1%2").arg(url,mirror_url_postfix), outFileVersionInfo);
227 qDebug("\nDownloading signature file:");
228 bool ok2 = getFile(QString("%1%2.sig").arg(url,mirror_url_postfix), outFileSignature);
232 qDebug("\nDownload okay, checking signature:");
233 if(checkSignature(outFileVersionInfo, outFileSignature))
235 qDebug("\nSignature okay, parsing info:");
236 success = parseVersionInfo(outFileVersionInfo, updateInfo);
240 qDebug("\nBad signature, take care!");
245 qDebug("\nDownload has failed!");
248 QFile::remove(outFileVersionInfo);
249 QFile::remove(outFileSignature);
254 bool UpdateDialog::getFile(const QString &url, const QString &outFile)
256 QFileInfo output(outFile);
257 output.setCaching(false);
261 QFile::remove(output.canonicalFilePath());
269 process.setProcessChannelMode(QProcess::MergedChannels);
270 process.setReadChannel(QProcess::StandardOutput);
273 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
274 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
275 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
277 process.start(m_binaryWGet, QStringList() << "-O" << output.absoluteFilePath() << url);
279 if(!process.waitForStarted())
284 while(process.state() == QProcess::Running)
287 while(process.canReadLine())
289 qDebug("WGet: %s", QString::fromLatin1(process.readLine()).simplified().toLatin1().constData());
293 qDebug("WGet: Exited with code %d", process.exitCode());
294 return (process.exitCode() == 0) && output.exists() && output.isFile();
297 bool UpdateDialog::checkSignature(const QString &file, const QString &signature)
300 process.setProcessChannelMode(QProcess::MergedChannels);
301 process.setReadChannel(QProcess::StandardOutput);
304 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
305 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
306 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
308 process.start(m_binaryGnuPG, QStringList() << "--homedir" << lamexp_temp_folder() << "--keyring" << QDir::toNativeSeparators(m_binaryKeys) << QDir::toNativeSeparators(signature) << QDir::toNativeSeparators(file));
310 if(!process.waitForStarted())
315 while(process.state() == QProcess::Running)
318 while(process.canReadLine())
320 qDebug("GnuPG: %s", QString::fromLatin1(process.readLine()).simplified().toLatin1().constData());
324 qDebug("GnuPG: Exited with code %d", process.exitCode());
325 return (process.exitCode() == 0);
328 bool UpdateDialog::parseVersionInfo(const QString &file, UpdateInfo *updateInfo)
331 QRegExp value("^(\\w+)=(.+)$");
332 QRegExp section("^\\[(.+)\\]$");
335 if(!data.open(QIODevice::ReadOnly))
337 qWarning("Cannot open update info file for reading!");
341 bool inSection = false;
345 QString line = QString::fromLatin1(data.readLine()).trimmed();
346 if(section.indexIn(line) >= 0)
348 qDebug("Section: '%s'", section.cap(1).toLatin1().constData());
349 inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0);
352 if(inSection && value.indexIn(line) >= 0)
354 qDebug("Value: '%s' ==> '%s'", value.cap(1).toLatin1().constData(), value.cap(2).toLatin1().constData());
355 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
358 unsigned int temp = value.cap(2).toUInt(&ok);
359 if(ok) updateInfo->m_buildNo = temp;
361 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
363 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
364 if(temp.isValid()) updateInfo->m_buildDate = temp;
366 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
368 updateInfo->m_downloadSite = value.cap(2).trimmed();
370 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
372 updateInfo->m_downloadAddress = value.cap(2).trimmed();
374 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
376 updateInfo->m_downloadFilename = value.cap(2).trimmed();
378 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
380 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
385 bool complete = true;
387 if(!(updateInfo->m_buildNo > 0)) complete = false;
388 if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false;
389 if(updateInfo->m_downloadSite.isEmpty()) complete = false;
390 if(updateInfo->m_downloadAddress.isEmpty()) complete = false;
391 if(updateInfo->m_downloadFilename.isEmpty()) complete = false;
392 if(updateInfo->m_downloadFilecode.isEmpty()) complete = false;
397 void UpdateDialog::linkActivated(const QString &link)
399 QDesktopServices::openUrl(QUrl(link));
402 void UpdateDialog::applyUpdate(void)
404 installButton->setEnabled(false);
405 closeButton->setEnabled(false);
406 retryButton->setEnabled(false);
410 statusLabel->setText("Update is being downloaded, please be patient...");
411 QApplication::processEvents();
417 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
418 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
420 args << QString("/Location=%1").arg(m_updateInfo->m_downloadAddress);
421 args << QString("/Filename=%1").arg(m_updateInfo->m_downloadFilename);
422 args << QString("/TicketID=%1").arg(m_updateInfo->m_downloadFilecode);
423 args << QString("/ToFolder=%1").arg(QDir::toNativeSeparators(QApplication::applicationDirPath()));
424 args << QString("/AppTitle=LameXP (Build #%1)").arg(QString::number(m_updateInfo->m_buildNo));
426 QApplication::setOverrideCursor(Qt::WaitCursor);
427 process.start(m_binaryUpdater, args);
429 QApplication::restoreOverrideCursor();
431 if(process.exitCode() == 0)
433 statusLabel->setText("Update ready to install. Applicaion will quit...");
434 QApplication::quit();
438 statusLabel->setText("Update failed. Please try again or download manually!");
442 installButton->setEnabled(true);
443 closeButton->setEnabled(true);