OSDN Git Service

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