OSDN Git Service

Updated Monkey's Audio binary to v4.11 (2013-01-20), including STDERR flush fix.
[lamexp/LameXP.git] / src / Dialog_Update.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Dialog_Update.h"
23
24 //UIC includes
25 #include "..\tmp\UIC_UpdateDialog.h"
26
27 //LameXP includes
28 #include "Global.h"
29 #include "Resource.h"
30 #include "Dialog_LogView.h"
31 #include "Model_Settings.h"
32 #include "WinSevenTaskbar.h"
33
34 //Qt includes
35 #include <QClipboard>
36 #include <QFileDialog>
37 #include <QTimer>
38 #include <QProcess>
39 #include <QDate>
40 #include <QRegExp>
41 #include <QDesktopServices>
42 #include <QUrl>
43 #include <QCloseEvent>
44 #include <QMovie>
45 #include <QtConcurrentRun>
46
47 //Win32 includes
48 #include <MMSystem.h>
49 #include <WinInet.h>
50
51 ///////////////////////////////////////////////////////////////////////////////
52
53 static const char *header_id = "!Update";
54 static const char *section_id = "LameXP";
55
56 static const char *mirror_url_postfix[] = 
57 {
58         "update.ver",
59         "update_beta.ver",
60         NULL
61 };
62
63 static const char *update_mirrors_prim[] =
64 {
65         "http://muldersoft.com/",
66         "http://mulder.brhack.net/",
67         "http://mulder.bplaced.net/",
68         "http://mulder.cwsurf.de/",
69         "http://mulder.6te.net/",
70         "http://mulder.webuda.com/",
71         "http://lamexp.sourceforge.net/",
72         "http://lordmulder.github.com/LameXP/",
73         "http://lord_mulder.bitbucket.org/",
74         "http://www.tricksoft.de/",
75         NULL
76 };
77
78 static const char *update_mirrors_back[] =
79 {
80         "http://mplayer.savedonthe.net/",
81         "http://www.rarewares.org/",
82         "http://lord_mulder.doom9.net/",
83         NULL
84 };
85
86 static const char *known_hosts[] =              //Taken form: http://www.alexa.com/topsites !!!
87 {
88         "http://www.163.com/",
89         "http://www.360buy.com/",
90         "http://www.ac3filter.net/",
91         "http://www.amazon.com/",
92         "http://www.aol.com/",
93         "http://www.apache.org/",
94         "http://www.apple.com/",
95         "http://www.adobe.com/",
96         "http://www.avidemux.org/",
97         "http://www.babylon.com/",
98         "http://www.baidu.com/",
99         "http://www.bbc.co.uk/",
100         "http://www.bing.com/",
101         "http://www.cnet.com/",
102         "http://cnzz.com/",
103         "http://codecs.com/",
104         "http://qt.digia.com/",
105         "http://www.ebay.com/",
106         "http://www.equation.com/",
107         "http://fc2.com/",
108         "http://www.ffmpeg.org/",
109         "http://www.flickr.com/",
110         "http://www.gitorious.org/",
111         "http://git-scm.com/",
112         "http://www.gnome.org/",
113         "http://www.gnu.org/",
114         "http://go.com/",
115         "http://code.google.com/",
116         "http://www.heise.de/",
117         "http://www.huffingtonpost.co.uk/",
118         "http://www.iana.org/",
119         "http://www.imdb.com/",
120         "http://www.imgburn.com/",
121         "http://imgur.com/",
122         "http://mirrors.kernel.org/",
123         "http://www.libav.org/",
124         "http://www.linkedin.com/",
125         "http://www.livedoor.com/",
126         "http://www.livejournal.com/",
127         "http://mail.ru/",
128         "http://www.mediafire.com/",
129         "http://www.mozilla.org/en-US/",
130         "http://mplayerhq.hu/",
131         "http://www.msn.com/?st=1",
132         "http://oss.netfarm.it/",
133         "http://www.nytimes.com/",
134         "http://www.opera.com/",
135         "http://www.portablefreeware.com/",
136         "http://qt-project.org/",
137         "http://www.quakelive.com/",
138         "http://www.seamonkey-project.org/",
139         "http://www.sina.com.cn/",
140         "http://www.sohu.com/",
141         "http://www.soso.com/",
142         "http://sourceforge.net/",
143         "http://www.spiegel.de/",
144         "http://tdm-gcc.tdragon.net/",
145         "http://www.tdrsmusic.com/",
146         "http://www.ubuntu.com/",
147         "http://blog.twitter.com/",
148         "http://www.uol.com.br/",
149         "http://www.videohelp.com/",
150         "http://www.videolan.org/",
151         "http://www.weibo.com/",
152         "http://www.wikipedia.org/",
153         "http://www.winamp.com/",
154         "http://wordpress.com/",
155         "http://us.yahoo.com/",
156         "http://www.yandex.ru/",
157         "http://www.youtube.com/",
158         "http://www.zedo.com/",
159         "http://ffmpeg.zeranoe.com/",
160         NULL
161 };
162
163 static const int MIN_CONNSCORE = 8;
164 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
165 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111106 IceCat/7.0.1";
166
167 static BOOL getInternetConnectedState(void)
168 {
169         DWORD lpdwFlags = NULL;
170         BOOL result = InternetGetConnectedState(&lpdwFlags, NULL);
171         return result;
172 }
173
174 static BOOL CALLBACK focusUpdaterWindow(HWND hwnd, LPARAM lParam)
175 {
176         DWORD processId = *reinterpret_cast<WORD*>(lParam);
177         DWORD windowProcessId = NULL;
178         GetWindowThreadProcessId(hwnd, &windowProcessId);
179         if(windowProcessId == processId)
180         {
181                 SwitchToThisWindow(hwnd, TRUE);
182                 SetForegroundWindow(hwnd);
183                 return FALSE;
184         }
185
186         return TRUE;
187 }
188
189 ///////////////////////////////////////////////////////////////////////////////
190
191 class UpdateInfo
192 {
193 public:
194         UpdateInfo(void) { resetInfo(); }
195         
196         void resetInfo(void)
197         {
198                 m_buildNo = 0;
199                 m_buildDate.setDate(1900, 1, 1);
200                 m_downloadSite.clear();
201                 m_downloadAddress.clear();
202                 m_downloadFilename.clear();
203                 m_downloadFilecode.clear();
204         }
205
206         unsigned int m_buildNo;
207         QDate m_buildDate;
208         QString m_downloadSite;
209         QString m_downloadAddress;
210         QString m_downloadFilename;
211         QString m_downloadFilecode;
212 };
213
214 ///////////////////////////////////////////////////////////////////////////////
215
216 UpdateDialog::UpdateDialog(SettingsModel *settings, QWidget *parent)
217 :
218         QDialog(parent),
219         ui(new Ui::UpdateDialog),
220         m_binaryWGet(lamexp_lookup_tool("wget.exe")),
221         m_binaryGnuPG(lamexp_lookup_tool("gpgv.exe")),
222         m_binaryUpdater(lamexp_lookup_tool("wupdate.exe")),
223         m_binaryKeys(lamexp_lookup_tool("gpgv.gpg")),
224         m_updateInfo(NULL),
225         m_settings(settings),
226         m_logFile(new QStringList()),
227         m_betaUpdates(settings ? (settings->autoUpdateCheckBeta() || lamexp_version_demo()) : lamexp_version_demo()),
228         m_success(false),
229         m_firstShow(true),
230         m_updateReadyToInstall(false),
231         m_updaterProcess(NULL)
232 {
233         if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryUpdater.isEmpty() || m_binaryKeys.isEmpty())
234         {
235                 throw "Tools not initialized correctly!";
236         }
237         
238         //Init the dialog, from the .ui file
239         ui->setupUi(this);
240         setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint);
241
242         //Disable "X" button
243         HMENU hMenu = GetSystemMenu((HWND) winId(), FALSE);
244         EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
245
246         //Init animation
247         m_animator = new QMovie(":/images/Loading3.gif");
248         ui->labelAnimationCenter->setMovie(m_animator);
249         m_animator->start();
250
251         //Indicate beta updates
252         if(m_betaUpdates)
253         {
254                 setWindowTitle(windowTitle().append(" [Beta]"));
255         }
256         
257         //Enable button
258         connect(ui->retryButton, SIGNAL(clicked()), this, SLOT(checkForUpdates()));
259         connect(ui->installButton, SIGNAL(clicked()), this, SLOT(applyUpdate()));
260         connect(ui->infoLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)));
261         connect(ui->logButton, SIGNAL(clicked()), this, SLOT(logButtonClicked()));
262
263         //Enable progress bar
264         connect(ui->progressBar, SIGNAL(valueChanged(int)), this, SLOT(progressBarValueChanged(int)));
265 }
266
267 UpdateDialog::~UpdateDialog(void)
268 {
269         if(m_animator) m_animator->stop();
270         
271         LAMEXP_DELETE(m_updateInfo);
272         LAMEXP_DELETE(m_logFile);
273         LAMEXP_DELETE(m_animator);
274
275         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
276         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
277
278         LAMEXP_DELETE(ui);
279 }
280
281 void UpdateDialog::showEvent(QShowEvent *event)
282 {
283         QDialog::showEvent(event);
284         
285         if(m_firstShow)
286         {
287                 ui->labelVersionInstalled->setText(QString("%1 %2 (%3)").arg(tr("Build"), QString::number(lamexp_version_build()), lamexp_version_date().toString(Qt::ISODate)));
288                 ui->labelVersionLatest->setText(QString("(%1)").arg(tr("Unknown")));
289
290                 ui->installButton->setEnabled(false);
291                 ui->closeButton->setEnabled(false);
292                 ui->retryButton->setEnabled(false);
293                 ui->logButton->setEnabled(false);
294                 ui->retryButton->hide();
295                 ui->logButton->hide();
296                 ui->infoLabel->hide();
297                 ui->hintLabel->hide();
298                 ui->hintIcon->hide();
299                 ui->frameAnimation->hide();
300         
301                 int counter = MIN_CONNSCORE + 2;
302                 for(int i = 0; update_mirrors_prim[i]; i++) counter++;
303                 for(int i = 0; update_mirrors_back[i]; i++) counter++;
304
305                 ui->progressBar->setMaximum(counter);
306                 ui->progressBar->setValue(0);
307
308                 m_updaterProcess = NULL;
309
310                 QTimer::singleShot(0, this, SLOT(updateInit()));
311                 m_firstShow = false;
312         }
313 }
314
315 void UpdateDialog::closeEvent(QCloseEvent *event)
316 {
317         if(!ui->closeButton->isEnabled())
318         {
319                 event->ignore();
320         }
321         else
322         {
323                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
324                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
325         }
326 }
327
328 void UpdateDialog::keyPressEvent(QKeyEvent *e)
329 {
330         if(e->key() == Qt::Key_F11)
331         {
332                 if(ui->closeButton->isEnabled()) logButtonClicked();
333         }
334         else if((e->key() == Qt::Key_F12) && e->modifiers().testFlag(Qt::ControlModifier))
335         {
336                 if(ui->closeButton->isEnabled())
337                 {
338                         testKnownWebSites();
339                         logButtonClicked();
340                 }
341         }
342         else
343         {
344                 QDialog::keyPressEvent(e);
345         }
346 }
347
348 bool UpdateDialog::event(QEvent *e)
349 {
350         if((e->type() == QEvent::ActivationChange) && (m_updaterProcess != NULL))
351         {
352                 EnumWindows(focusUpdaterWindow, reinterpret_cast<LPARAM>(&m_updaterProcess));
353         }
354         return QDialog::event(e);
355 }
356
357 bool UpdateDialog::winEvent(MSG *message, long *result)
358 {
359         return WinSevenTaskbar::handleWinEvent(message, result);
360 }
361
362 void UpdateDialog::updateInit(void)
363 {
364         setMinimumSize(size());
365         setMaximumHeight(height());
366         QApplication::processEvents();
367         checkForUpdates();
368 }
369
370 void UpdateDialog::checkForUpdates(void)
371 {
372         bool success = false;
373         int connectionScore = 0;
374
375         // ----- Initialization ----- //
376
377         m_updateInfo = new UpdateInfo;
378
379         ui->progressBar->setValue(0);
380         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNormalState);
381         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/transmit_blue.png"));
382         ui->installButton->setEnabled(false);
383         ui->closeButton->setEnabled(false);
384         ui->retryButton->setEnabled(false);
385         ui->logButton->setEnabled(false);
386         if(ui->infoLabel->isVisible()) ui->infoLabel->hide();
387         if(ui->hintLabel->isVisible()) ui->hintLabel->hide();
388         if(ui->hintIcon->isVisible()) ui->hintIcon->hide();
389         ui->frameAnimation->show();
390
391         QApplication::processEvents();
392         QApplication::setOverrideCursor(Qt::WaitCursor);
393
394         // ----- Test Internet Connection ----- //
395
396         ui->statusLabel->setText(tr("Testing your internet connection, please wait..."));
397
398         m_logFile->clear();
399         m_logFile->append("Checking internet connection...");
400         
401         QFuture<BOOL> connectedState = QtConcurrent::run(getInternetConnectedState);
402         while(!connectedState.isFinished())
403         {
404                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
405         }
406
407         if(!connectedState.result())
408         {
409                 m_logFile->append(QStringList() << "" << "Operating system reports that the computer is currently offline !!!");
410                 if(!ui->retryButton->isVisible()) ui->retryButton->show();
411                 if(!ui->logButton->isVisible()) ui->logButton->show();
412                 ui->closeButton->setEnabled(true);
413                 ui->retryButton->setEnabled(true);
414                 ui->logButton->setEnabled(true);
415                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
416                 ui->statusLabel->setText(tr("It appears that the computer currently is offline!"));
417                 ui->progressBar->setValue(ui->progressBar->maximum());
418                 ui->hintIcon->setPixmap(QIcon(":/icons/network_error.png").pixmap(16,16));
419                 ui->hintLabel->setText(tr("Please make sure your computer is connected to the internet and try again."));
420                 ui->hintIcon->show();
421                 ui->hintLabel->show();
422                 LAMEXP_DELETE(m_updateInfo);
423                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
424                 QApplication::restoreOverrideCursor();
425                 ui->progressBar->setValue(ui->progressBar->maximum());
426                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
427                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
428                 return;
429         }
430         
431         ui->progressBar->setValue(1);
432         QApplication::processEvents();
433
434         // ----- Test Known Hosts Connectivity ----- //
435
436         QStringList hostList;
437         for(int i = 0; known_hosts[i]; i++)
438         {
439                 hostList << QString::fromLatin1(known_hosts[i]);
440         }
441
442         lamexp_seed_rand();
443         while(!hostList.isEmpty())
444         {
445                 QString currentHost = hostList.takeAt(lamexp_rand() % hostList.count());
446                 if(connectionScore < MIN_CONNSCORE)
447                 {
448                         m_logFile->append(QStringList() << "" << "Testing host:" << currentHost << "");
449                         QString outFile = QString("%1/%2.htm").arg(lamexp_temp_folder2(), lamexp_rand_str());
450                         bool httpOk = false;
451                         if(getFile(currentHost, outFile, 0, &httpOk))
452                         {
453                                 connectionScore++;
454                                 ui->progressBar->setValue(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
455                                 QApplication::processEvents();
456                                 Sleep(64);
457                         }
458                         if(httpOk)
459                         {
460                                 connectionScore++;
461                                 ui->progressBar->setValue(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
462                                 QApplication::processEvents();
463                                 Sleep(64);
464                         }
465                         QFile::remove(outFile);
466                 }
467         }
468
469         if(connectionScore < MIN_CONNSCORE)
470         {
471                 if(!ui->retryButton->isVisible()) ui->retryButton->show();
472                 if(!ui->logButton->isVisible()) ui->logButton->show();
473                 ui->closeButton->setEnabled(true);
474                 ui->retryButton->setEnabled(true);
475                 ui->logButton->setEnabled(true);
476                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
477                 ui->statusLabel->setText(tr("Network connectivity test has failed!"));
478                 ui->progressBar->setValue(ui->progressBar->maximum());
479                 ui->hintIcon->setPixmap(QIcon(":/icons/network_error.png").pixmap(16,16));
480                 ui->hintLabel->setText(tr("Please make sure your internet connection is working properly and try again."));
481                 ui->hintIcon->show();
482                 ui->hintLabel->show();
483                 LAMEXP_DELETE(m_updateInfo);
484                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
485                 QApplication::restoreOverrideCursor();
486                 ui->progressBar->setValue(ui->progressBar->maximum());
487                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
488                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
489                 return;
490         }
491
492         // ----- Build Mirror List ----- //
493
494         ui->statusLabel->setText(tr("Checking for new updates online, please wait..."));
495         m_logFile->append(QStringList() << "" << "----" << "" << "Checking for updates online...");
496
497         QStringList mirrorList;
498         for(int index = 0; update_mirrors_prim[index]; index++)
499         {
500                 mirrorList << QString::fromLatin1(update_mirrors_prim[index]);
501         }
502
503         lamexp_seed_rand();
504         if(const int len = mirrorList.count())
505         {
506                 const int rounds = len * 1097;
507                 for(int i = 0; i < rounds; i++)
508                 {
509                         mirrorList.swap(i % len, lamexp_rand() % len);
510                 }
511         }
512
513         for(int index = 0; update_mirrors_back[index]; index++)
514         {
515                 mirrorList << QString::fromLatin1(update_mirrors_back[index]);
516         }
517         
518         // ----- Fetch Update Info From Server ----- //
519
520         while(!mirrorList.isEmpty())
521         {
522                 QString currentMirror = mirrorList.takeFirst();
523                 ui->progressBar->setValue(ui->progressBar->value() + 1);
524                 if(!success)
525                 {
526                         if(tryUpdateMirror(m_updateInfo, currentMirror))
527                         {
528                                 success = true;
529                         }
530                 }
531                 else
532                 {
533                         QApplication::processEvents();
534                         Sleep(64);
535                 }
536         }
537         
538         QApplication::restoreOverrideCursor();
539         ui->progressBar->setValue(ui->progressBar->maximum());
540         
541         if(!success)
542         {
543                 if(!ui->retryButton->isVisible()) ui->retryButton->show();
544                 if(!ui->logButton->isVisible()) ui->logButton->show();
545                 ui->closeButton->setEnabled(true);
546                 ui->retryButton->setEnabled(true);
547                 ui->logButton->setEnabled(true);
548                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
549                 ui->statusLabel->setText(tr("Failed to fetch update information from server!"));
550                 ui->progressBar->setValue(ui->progressBar->maximum());
551                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
552                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
553                 ui->hintIcon->setPixmap(QIcon(":/icons/server_error.png").pixmap(16,16));
554                 ui->hintLabel->setText(tr("Sorry, the update server might be busy at this time. Plase try again later."));
555                 ui->hintIcon->show();
556                 ui->hintLabel->show();
557                 LAMEXP_DELETE(m_updateInfo);
558                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
559                 return;
560         }
561
562         // ----- Download New Program Version ----- //
563         
564         ui->labelVersionLatest->setText(QString("%1 %2 (%3)").arg(tr("Build"), QString::number(m_updateInfo->m_buildNo), m_updateInfo->m_buildDate.toString(Qt::ISODate)));
565         ui->infoLabel->show();
566         ui->infoLabel->setText(QString("%1<br><a href=\"%2\">%2</a>").arg(tr("More information available at:"), m_updateInfo->m_downloadSite));
567         QApplication::processEvents();
568         
569         if(m_updateInfo->m_buildNo > lamexp_version_build())
570         {
571                 ui->installButton->setEnabled(true);
572                 ui->statusLabel->setText(tr("A new version of LameXP is available!"));
573                 ui->hintIcon->setPixmap(QIcon(":/icons/shield_exclamation.png").pixmap(16,16));
574                 ui->hintLabel->setText(tr("We highly recommend all users to install this update as soon as possible."));
575                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
576                 ui->hintIcon->show();
577                 ui->hintLabel->show();
578                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/shield_exclamation.png"));
579                 MessageBeep(MB_ICONINFORMATION);
580         }
581         else if(m_updateInfo->m_buildNo == lamexp_version_build())
582         {
583                 ui->statusLabel->setText(tr("No new updates available at this time."));
584                 ui->hintIcon->setPixmap(QIcon(":/icons/shield_green.png").pixmap(16,16));
585                 ui->hintLabel->setText(tr("Your version of LameXP is still up-to-date. Please check for updates regularly!"));
586                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
587                 ui->hintIcon->show();
588                 ui->hintLabel->show();
589                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/shield_green.png"));
590                 MessageBeep(MB_ICONINFORMATION);
591         }
592         else
593         {
594                 ui->statusLabel->setText(tr("Your version appears to be newer than the latest release."));
595                 ui->hintIcon->setPixmap(QIcon(":/icons/shield_error.png").pixmap(16,16));
596                 ui->hintLabel->setText(tr("This usually indicates your are currently using a pre-release version of LameXP."));
597                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
598                 ui->hintIcon->show();
599                 ui->hintLabel->show();
600                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/shield_error.png"));
601                 MessageBeep(MB_ICONEXCLAMATION);
602         }
603
604         ui->closeButton->setEnabled(true);
605         if(ui->retryButton->isVisible()) ui->retryButton->hide();
606         if(ui->logButton->isVisible()) ui->logButton->hide();
607         if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
608
609         m_success = true;
610 }
611
612 bool UpdateDialog::tryUpdateMirror(UpdateInfo *updateInfo, const QString &url)
613 {
614         bool success = false;
615         m_logFile->append(QStringList() << "" << "Trying mirror:" << url);
616
617         QString randPart = lamexp_rand_str();
618         QString outFileVersionInfo = QString("%1/%2.ver").arg(lamexp_temp_folder2(), randPart);
619         QString outFileSignature = QString("%1/%2.sig").arg(lamexp_temp_folder2(), randPart);
620
621         m_logFile->append(QStringList() << "" << "Downloading update info:");
622         bool ok1 = getFile(QString("%1%2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVersionInfo);
623
624         m_logFile->append(QStringList() << "" << "Downloading signature:");
625         bool ok2 = getFile(QString("%1%2.sig").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSignature);
626
627         if(ok1 && ok2)
628         {
629                 m_logFile->append(QStringList() << "" << "Download okay, checking signature:");
630                 if(checkSignature(outFileVersionInfo, outFileSignature))
631                 {
632                         m_logFile->append(QStringList() << "" << "Signature okay, parsing info:");
633                         success = parseVersionInfo(outFileVersionInfo, updateInfo);
634                 }
635                 else
636                 {
637                         m_logFile->append(QStringList() << "" << "Bad signature, take care!");
638                 }
639         }
640         else
641         {
642                 m_logFile->append(QStringList() << "" << "Download has failed!");
643         }
644
645         QFile::remove(outFileVersionInfo);
646         QFile::remove(outFileSignature);
647         
648         return success;
649 }
650
651 bool UpdateDialog::getFile(const QString &url, const QString &outFile, unsigned int maxRedir, bool *httpOk)
652 {
653         QFileInfo output(outFile);
654         output.setCaching(false);
655         if(httpOk) *httpOk = false;
656
657         if(output.exists())
658         {
659                 QFile::remove(output.canonicalFilePath());
660                 if(output.exists())
661                 {
662                         return false;
663                 }
664         }
665
666         QProcess process;
667         process.setProcessChannelMode(QProcess::MergedChannels);
668         process.setReadChannel(QProcess::StandardOutput);
669         process.setWorkingDirectory(output.absolutePath());
670
671         QStringList args;
672         args << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir);
673         args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
674         args << "-O" << output.fileName() << url;
675
676         QEventLoop loop;
677         connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
678         connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
679         connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
680
681         QTimer timer;
682         timer.setSingleShot(true);
683         timer.setInterval(25000);
684         connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
685
686         const QRegExp httpResponseOK("200 OK$");
687         
688         process.start(m_binaryWGet, args);
689         
690         if(!process.waitForStarted())
691         {
692                 return false;
693         }
694
695         timer.start();
696
697         while(process.state() == QProcess::Running)
698         {
699                 loop.exec();
700                 bool bTimeOut = (!timer.isActive());
701                 while(process.canReadLine())
702                 {
703                         QString line = QString::fromLatin1(process.readLine()).simplified();
704                         if(line.contains(httpResponseOK))
705                         {
706                                 line.append(" [OK]");
707                                 if(httpOk) *httpOk = true;
708                         }
709                         m_logFile->append(line);
710                 }
711                 if(bTimeOut)
712                 {
713                         qWarning("WGet process timed out <-- killing!");
714                         process.kill();
715                         process.waitForFinished();
716                         m_logFile->append("!!! TIMEOUT !!!");
717                         return false;
718                 }
719         }
720         
721         timer.stop();
722         timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
723
724         m_logFile->append(QString().sprintf("Exited with code %d", process.exitCode()));
725         return (process.exitCode() == 0) && output.exists() && output.isFile();
726 }
727
728 bool UpdateDialog::checkSignature(const QString &file, const QString &signature)
729 {
730         if(QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
731         {
732                 qWarning("CheckSignature: File and signature should be in same folder!");
733                 return false;
734         }
735         if(QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
736         {
737                 qWarning("CheckSignature: File and keyring should be in same folder!");
738                 return false;
739         }
740
741         QProcess process;
742         process.setProcessChannelMode(QProcess::MergedChannels);
743         process.setReadChannel(QProcess::StandardOutput);
744         process.setWorkingDirectory(QFileInfo(file).absolutePath());
745
746         QEventLoop loop;
747         connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
748         connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
749         connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
750         
751         process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(m_binaryKeys).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
752
753         if(!process.waitForStarted())
754         {
755                 return false;
756         }
757
758         while(process.state() == QProcess::Running)
759         {
760                 loop.exec();
761                 while(process.canReadLine())
762                 {
763                         m_logFile->append(QString::fromLatin1(process.readLine()).simplified());
764                 }
765         }
766         
767         m_logFile->append(QString().sprintf("Exited with code %d", process.exitCode()));
768         return (process.exitCode() == 0);
769 }
770
771 bool UpdateDialog::parseVersionInfo(const QString &file, UpdateInfo *updateInfo)
772 {
773         QRegExp value("^(\\w+)=(.+)$");
774         QRegExp section("^\\[(.+)\\]$");
775
776         QDate updateInfoDate;
777         updateInfo->resetInfo();
778
779         QFile data(file);
780         if(!data.open(QIODevice::ReadOnly))
781         {
782                 qWarning("Cannot open update info file for reading!");
783                 return false;
784         }
785         
786         bool inHeader = false;
787         bool inSection = false;
788         
789         while(!data.atEnd())
790         {
791                 QString line = QString::fromLatin1(data.readLine()).trimmed();
792                 if(section.indexIn(line) >= 0)
793                 {
794                         m_logFile->append(QString("Sec: [%1]").arg(section.cap(1)));
795                         inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0);
796                         inHeader = (section.cap(1).compare(header_id, Qt::CaseInsensitive) == 0);
797                         continue;
798                 }
799                 if(inSection && (value.indexIn(line) >= 0))
800                 {
801                         m_logFile->append(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
802                         if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
803                         {
804                                 bool ok = false;
805                                 unsigned int temp = value.cap(2).toUInt(&ok);
806                                 if(ok) updateInfo->m_buildNo = temp;
807                         }
808                         else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
809                         {
810                                 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
811                                 if(temp.isValid()) updateInfo->m_buildDate = temp;
812                         }
813                         else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
814                         {
815                                 updateInfo->m_downloadSite = value.cap(2).trimmed();
816                         }
817                         else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
818                         {
819                                 updateInfo->m_downloadAddress = value.cap(2).trimmed();
820                         }
821                         else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
822                         {
823                                 updateInfo->m_downloadFilename = value.cap(2).trimmed();
824                         }
825                         else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
826                         {
827                                 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
828                         }
829                 }
830                 if(inHeader && (value.indexIn(line) >= 0))
831                 {
832                         m_logFile->append(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
833                         if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
834                         {
835                                 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
836                                 if(temp.isValid()) updateInfoDate = temp;
837                         }
838                 }
839         }
840         
841         if(!updateInfoDate.isValid())
842         {
843                 updateInfo->resetInfo();
844                 m_logFile->append("WARNING: Version info timestamp is missing!");
845                 return false;
846         }
847         else if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < lamexp_current_date_safe())
848         {
849                 updateInfo->resetInfo();
850                 m_logFile->append(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
851                 return false;
852         }
853         else if(lamexp_current_date_safe() < updateInfoDate)
854         {
855                 m_logFile->append("Version info is from the future, take care!");
856                 qWarning("Version info is from the future, take care!");
857         }
858
859         bool complete = true;
860
861         if(!(updateInfo->m_buildNo > 0)) complete = false;
862         if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false;
863         if(updateInfo->m_downloadSite.isEmpty()) complete = false;
864         if(updateInfo->m_downloadAddress.isEmpty()) complete = false;
865         if(updateInfo->m_downloadFilename.isEmpty()) complete = false;
866         if(updateInfo->m_downloadFilecode.isEmpty()) complete = false;
867         
868         if(!complete)
869         {
870                 m_logFile->append("WARNING: Version info is incomplete!");
871         }
872
873         return complete;
874 }
875
876 void UpdateDialog::linkActivated(const QString &link)
877 {
878         QDesktopServices::openUrl(QUrl(link));
879 }
880
881 void UpdateDialog::applyUpdate(void)
882 {
883         ui->installButton->setEnabled(false);
884         ui->closeButton->setEnabled(false);
885         ui->retryButton->setEnabled(false);
886
887         if(m_updateInfo)
888         {
889                 ui->statusLabel->setText(tr("Update is being downloaded, please be patient..."));
890                 ui->frameAnimation->show();
891                 if(ui->hintLabel->isVisible()) ui->hintLabel->hide();
892                 if(ui->hintIcon->isVisible()) ui->hintIcon->hide();
893                 int oldMax = ui->progressBar->maximum();
894                 int oldMin = ui->progressBar->minimum();
895                 ui->progressBar->setRange(0, 0);
896                 QApplication::processEvents();
897                 
898                 QProcess process;
899                 QStringList args;
900                 QEventLoop loop;
901
902                 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
903                 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
904
905                 args << QString("/Location=%1").arg(m_updateInfo->m_downloadAddress);
906                 args << QString("/Filename=%1").arg(m_updateInfo->m_downloadFilename);
907                 args << QString("/TicketID=%1").arg(m_updateInfo->m_downloadFilecode);
908                 args << QString("/ToFolder=%1").arg(QDir::toNativeSeparators(QDir(QApplication::applicationDirPath()).canonicalPath())); 
909                 args << QString("/ToExFile=%1.exe").arg(QFileInfo(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath()).completeBaseName());
910                 args << QString("/AppTitle=LameXP (Build #%1)").arg(QString::number(m_updateInfo->m_buildNo));
911
912                 QApplication::setOverrideCursor(Qt::WaitCursor);
913                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarIndeterminateState);
914                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/transmit_blue.png"));
915
916                 process.start(m_binaryUpdater, args);
917                 bool updateStarted = process.waitForStarted();
918                 if(updateStarted)
919                 {
920                         m_updaterProcess = process.pid()->dwProcessId;
921                         loop.exec();
922                 }
923                 m_updaterProcess = NULL;
924                 QApplication::restoreOverrideCursor();
925
926                 ui->hintLabel->show();
927                 ui->hintIcon->show();
928                 ui->progressBar->setRange(oldMin, oldMax);
929                 ui->progressBar->setValue(oldMax);
930                 ui->frameAnimation->hide();
931
932                 if(updateStarted && (process.exitCode() == 0))
933                 {
934                         ui->statusLabel->setText(tr("Update ready to install. Applicaion will quit..."));
935                         m_updateReadyToInstall = true;
936                         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
937                         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
938                         accept();
939                 }
940                 else
941                 {
942                         ui->statusLabel->setText(tr("Update failed. Please try again or download manually!"));
943                         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
944                         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
945                         WinSevenTaskbar::setTaskbarProgress(this->parentWidget(), 100, 100);
946                 }
947         }
948
949         ui->installButton->setEnabled(true);
950         ui->closeButton->setEnabled(true);
951 }
952
953 void UpdateDialog::logButtonClicked(void)
954 {
955         LogViewDialog *logView = new LogViewDialog(this);
956         logView->exec(*m_logFile);
957         LAMEXP_DELETE(logView);
958 }
959
960 void UpdateDialog::progressBarValueChanged(int value)
961 {
962         WinSevenTaskbar::setTaskbarProgress(this->parentWidget(), value, ui->progressBar->maximum());
963 }
964
965 void UpdateDialog::testKnownWebSites(void)
966 {
967         int connectionScore = 0;
968
969         // ----- Initialization ----- //
970
971         ui->progressBar->setValue(0);
972         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNormalState);
973         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/transmit_blue.png"));
974         ui->installButton->setEnabled(false);
975         ui->closeButton->setEnabled(false);
976         ui->retryButton->setEnabled(false);
977         ui->logButton->setEnabled(false);
978         if(ui->infoLabel->isVisible()) ui->infoLabel->hide();
979         if(ui->hintLabel->isVisible()) ui->hintLabel->hide();
980         if(ui->hintIcon->isVisible()) ui->hintIcon->hide();
981         ui->frameAnimation->show();
982
983         QApplication::processEvents();
984         QApplication::setOverrideCursor(Qt::WaitCursor);
985
986         // ----- Test Internet Connection ----- //
987
988         ui->statusLabel->setText("Testing all known hosts, this may take a few minutes...");
989
990         m_logFile->clear();
991         m_logFile->append("Checking internet connection...");
992         
993         QFuture<BOOL> connectedState = QtConcurrent::run(getInternetConnectedState);
994         while(!connectedState.isFinished())
995         {
996                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
997         }
998
999         if(!connectedState.result())
1000         {
1001                 m_logFile->append(QStringList() << "" << "Operating system reports that the computer is currently offline !!!");
1002                 if(!ui->retryButton->isVisible()) ui->retryButton->show();
1003                 if(!ui->logButton->isVisible()) ui->logButton->show();
1004                 ui->closeButton->setEnabled(true);
1005                 ui->retryButton->setEnabled(true);
1006                 ui->logButton->setEnabled(true);
1007                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
1008                 ui->statusLabel->setText(tr("It appears that the computer currently is offline!"));
1009                 ui->progressBar->setValue(ui->progressBar->maximum());
1010                 ui->hintIcon->setPixmap(QIcon(":/icons/network_error.png").pixmap(16,16));
1011                 ui->hintLabel->setText(tr("Please make sure your computer is connected to the internet and try again."));
1012                 ui->hintIcon->show();
1013                 ui->hintLabel->show();
1014                 LAMEXP_DELETE(m_updateInfo);
1015                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
1016                 QApplication::restoreOverrideCursor();
1017                 ui->progressBar->setValue(ui->progressBar->maximum());
1018                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
1019                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
1020                 return;
1021         }
1022         
1023         // ----- Test Known Hosts Connectivity ----- //
1024
1025         QStringList hostList;
1026         for(int i = 0; known_hosts[i]; i++)
1027         {
1028                 hostList << QString::fromLatin1(known_hosts[i]);
1029         }
1030
1031         qDebug("[Known Hosts]");
1032
1033         int hostCount = hostList.count();
1034         while(!hostList.isEmpty())
1035         {
1036                 QString currentHost = hostList.takeFirst();
1037                 ui->progressBar->setValue(qRound((static_cast<double>(ui->progressBar->maximum() - 1) / static_cast<double>(hostCount)) * static_cast<double>(connectionScore)) + 1);
1038                 qDebug("Testing: %s", currentHost.toLatin1().constData());
1039                 m_logFile->append(QStringList() << "" << "Testing host:" << currentHost << "");
1040                 QString outFile = QString("%1/%2.htm").arg(lamexp_temp_folder2(), lamexp_rand_str());
1041                 bool httpOk = false;
1042                 if(getFile(currentHost, outFile, 0, &httpOk))
1043                 {
1044                         connectionScore++;
1045                 }
1046                 else
1047                 {
1048                         if(httpOk)
1049                         {
1050                                 qWarning("\nConnectivity test was SLOW on the following site:\n%s\n", currentHost.toLatin1().constData());
1051                                 connectionScore++;
1052                         }
1053                         else
1054                         {
1055                                 qWarning("\nConnectivity test FAILED on the following site:\n%s\n", currentHost.toLatin1().constData());
1056                         }
1057                 }
1058                 QFile::remove(outFile);
1059         }
1060
1061         if(connectionScore < hostCount)
1062         {
1063                 if(!ui->retryButton->isVisible()) ui->retryButton->show();
1064                 if(!ui->logButton->isVisible()) ui->logButton->show();
1065                 ui->closeButton->setEnabled(true);
1066                 ui->retryButton->setEnabled(true);
1067                 ui->logButton->setEnabled(true);
1068                 if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
1069                 ui->statusLabel->setText("At least one host could not be reached!");
1070                 ui->progressBar->setValue(ui->progressBar->maximum());
1071                 ui->hintIcon->setPixmap(QIcon(":/icons/network_error.png").pixmap(16,16));
1072                 ui->hintLabel->setText("Please make sure your internet connection is working properly and try again.");
1073                 ui->hintIcon->show();
1074                 ui->hintLabel->show();
1075                 LAMEXP_DELETE(m_updateInfo);
1076                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_ERROR), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);
1077                 QApplication::restoreOverrideCursor();
1078                 ui->progressBar->setValue(ui->progressBar->maximum());
1079                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
1080                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
1081                 return;
1082         }
1083
1084         // ----- Done ----- //
1085         
1086         QApplication::restoreOverrideCursor();
1087         ui->progressBar->setValue(ui->progressBar->maximum());
1088
1089         ui->statusLabel->setText("Test completed.");
1090         ui->hintIcon->setPixmap(QIcon(":/icons/shield_green.png").pixmap(16,16));
1091         ui->hintLabel->setText("Congratulations, the test has completed.");
1092         if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
1093         ui->hintIcon->show();
1094         ui->hintLabel->show();
1095         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/shield_green.png"));
1096         MessageBeep(MB_ICONINFORMATION);
1097
1098         ui->closeButton->setEnabled(true);
1099         if(ui->retryButton->isVisible()) ui->retryButton->hide();
1100         if(ui->logButton->isVisible()) ui->logButton->hide();
1101         if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
1102 }