OSDN Git Service

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