OSDN Git Service

Fixed number of tools (only relevant for DEBUG builds) + fixed an out-of-bounds array...
[lamexp/LameXP.git] / src / Dialog_Update.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Dialog_Update.h"
24
25 //UIC includes
26 #include "..\tmp\UIC_UpdateDialog.h"
27
28 //LameXP includes
29 #include "Global.h"
30 #include "Thread_CheckUpdate.h"
31 #include "Dialog_LogView.h"
32 #include "Model_Settings.h"
33 #include "WinSevenTaskbar.h"
34
35 //Qt includes
36 #include <QClipboard>
37 #include <QFileDialog>
38 #include <QTimer>
39 #include <QProcess>
40 #include <QDesktopServices>
41 #include <QUrl>
42 #include <QCloseEvent>
43 #include <QMovie>
44 #include <QMessageBox>
45
46 ///////////////////////////////////////////////////////////////////////////////
47
48 /*
49 template <class T>
50 T DO_ASYNC(T (*functionPointer)())
51 {
52         QFutureWatcher<T> watcher; QEventLoop loop;
53         QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
54         watcher.setFuture(QtConcurrent::run(functionPointer));
55         loop.exec(QEventLoop::ExcludeUserInputEvents);
56         return watcher.result();
57 }
58 */
59
60 #define SHOW_HINT(TEXT, ICON) do \
61 { \
62         ui->hintLabel->setText((TEXT)); \
63         ui->hintIcon->setPixmap(QIcon((ICON)).pixmap(16,16)); \
64         ui->hintIcon->show(); \
65         ui->hintLabel->show(); \
66 } \
67 while(0)
68
69 #define UPDATE_TASKBAR(STATE, ICON) do \
70 { \
71         WinSevenTaskbar::setTaskbarState(this->parentWidget(), (STATE)); \
72         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon((ICON))); \
73 } \
74 while(0)
75
76 ///////////////////////////////////////////////////////////////////////////////
77
78 UpdateDialog::UpdateDialog(SettingsModel *settings, QWidget *parent)
79 :
80         QDialog(parent),
81         ui(new Ui::UpdateDialog),
82         m_thread(NULL),
83         m_settings(settings),
84         m_logFile(new QStringList()),
85         m_betaUpdates(settings ? (settings->autoUpdateCheckBeta() || lamexp_version_demo()) : lamexp_version_demo()),
86         m_success(false),
87         m_firstShow(true),
88         m_updateReadyToInstall(false),
89         m_updaterProcess(NULL),
90         m_binaryUpdater(lamexp_lookup_tool("wupdate.exe")),
91         m_binaryWGet(lamexp_lookup_tool("wget.exe")),
92         m_binaryGnuPG(lamexp_lookup_tool("gpgv.exe")),
93         m_binaryKeys(lamexp_lookup_tool("gpgv.gpg"))
94 {
95         if(m_binaryUpdater.isEmpty())
96         {
97                 THROW("Tools not initialized correctly!");
98         }
99
100         //Init the dialog, from the .ui file
101         ui->setupUi(this);
102         setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint);
103
104         //Disable "X" button
105         lamexp_enable_close_button(this, false);
106
107         //Init animation
108         m_animator = new QMovie(":/images/Loading3.gif");
109         ui->labelAnimationCenter->setMovie(m_animator);
110         m_animator->start();
111
112         //Indicate beta updates
113         if(m_betaUpdates)
114         {
115                 setWindowTitle(windowTitle().append(" [Beta]"));
116         }
117         
118         //Enable button
119         connect(ui->retryButton, SIGNAL(clicked()), this, SLOT(checkForUpdates()));
120         connect(ui->installButton, SIGNAL(clicked()), this, SLOT(applyUpdate()));
121         connect(ui->infoLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)));
122         connect(ui->logButton, SIGNAL(clicked()), this, SLOT(logButtonClicked()));
123
124         //Enable progress bar
125         connect(ui->progressBar, SIGNAL(valueChanged(int)), this, SLOT(progressBarValueChanged(int)));
126 }
127
128 UpdateDialog::~UpdateDialog(void)
129 {
130         if(m_animator)
131         {
132                 m_animator->stop();
133         }
134
135         if(m_thread)
136         {
137                 if(!m_thread->wait(1000))
138                 {
139                         m_thread->terminate();
140                         m_thread->wait();
141                 }
142         }
143
144         LAMEXP_DELETE(m_thread);
145         LAMEXP_DELETE(m_logFile);
146         LAMEXP_DELETE(m_animator);
147
148         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
149         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
150
151         LAMEXP_DELETE(ui);
152 }
153
154 void UpdateDialog::showEvent(QShowEvent *event)
155 {
156         QDialog::showEvent(event);
157         
158         if(m_firstShow)
159         {
160                 if(!m_thread)
161                 {
162                         m_thread = new UpdateCheckThread(m_binaryWGet, m_binaryGnuPG, m_binaryKeys, m_betaUpdates);
163                         connect(m_thread, SIGNAL(statusChanged(int)), this, SLOT(threadStatusChanged(int)));
164                         connect(m_thread, SIGNAL(progressChanged(int)), this, SLOT(threadProgressChanged(int)));
165                         connect(m_thread, SIGNAL(messageLogged(QString)), this, SLOT(threadMessageLogged(QString)));
166                         connect(m_thread, SIGNAL(finished()), this, SLOT(threadFinished()));
167                         connect(m_thread, SIGNAL(terminated()), this, SLOT(threadFinished()));
168                 }
169
170                 threadStatusChanged(m_thread->getUpdateStatus());
171                 ui->labelVersionInstalled->setText(QString("%1 %2 (%3)").arg(tr("Build"), QString::number(lamexp_version_build()), lamexp_version_date().toString(Qt::ISODate)));
172                 ui->labelVersionLatest->setText(QString("(%1)").arg(tr("Unknown")));
173
174                 ui->installButton->setEnabled(false);
175                 ui->closeButton->setEnabled(false);
176                 ui->retryButton->setEnabled(false);
177                 ui->logButton->setEnabled(false);
178                 ui->retryButton->hide();
179                 ui->logButton->hide();
180                 ui->infoLabel->hide();
181                 ui->hintLabel->hide();
182                 ui->hintIcon->hide();
183                 ui->frameAnimation->hide();
184         
185                 ui->progressBar->setMaximum(m_thread->getMaximumProgress());
186                 ui->progressBar->setValue(0);
187
188                 m_updaterProcess = NULL;
189
190                 QTimer::singleShot(0, this, SLOT(updateInit()));
191                 m_firstShow = false;
192         }
193 }
194
195 void UpdateDialog::closeEvent(QCloseEvent *event)
196 {
197         if(!ui->closeButton->isEnabled())
198         {
199                 event->ignore();
200         }
201         else
202         {
203                 WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
204                 WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
205         }
206 }
207
208 void UpdateDialog::keyPressEvent(QKeyEvent *e)
209 {
210         if(e->key() == Qt::Key_F11)
211         {
212                 if(ui->closeButton->isEnabled()) logButtonClicked();
213         }
214         else if((e->key() == Qt::Key_F12) && e->modifiers().testFlag(Qt::ControlModifier))
215         {
216                 if(ui->closeButton->isEnabled()) testKnownHosts();
217         }
218         else
219         {
220                 QDialog::keyPressEvent(e);
221         }
222 }
223
224 bool UpdateDialog::event(QEvent *e)
225 {
226         if((e->type() == QEvent::ActivationChange) && (m_updaterProcess != NULL))
227         {
228                 lamexp_bring_process_to_front(m_updaterProcess);
229         }
230         return QDialog::event(e);
231 }
232
233 bool UpdateDialog::winEvent(MSG *message, long *result)
234 {
235         return WinSevenTaskbar::handleWinEvent(message, result);
236 }
237
238 void UpdateDialog::updateInit(void)
239 {
240         setMinimumSize(size());
241         setMaximumHeight(height());
242         QTimer::singleShot(0, this, SLOT(checkForUpdates()));
243 }
244
245 void UpdateDialog::checkForUpdates(void)
246 {
247         if(m_thread->isRunning())
248         {
249                 qWarning("Update in progress, cannot check for updates now!");
250         }
251
252         if(!lamexp_user_is_admin())
253         {
254                 qWarning("User is not in the \"admin\" group, cannot update!");
255                 QString message;
256                 message += QString("<nobr>%1</nobr><br>").arg(tr("Sorry, but only users in the \"Administrators\" group can install updates."));
257                 message += QString("<nobr>%1</nobr>").arg(tr("Please start application from an administrator account and try again!"));
258                 if(QMessageBox::critical(this, this->windowTitle(), message, tr("Discard"), tr("Ignore")) != 1)
259                 {
260                         ui->closeButton->setEnabled(true);
261                         close(); return;
262                 }
263         }
264
265         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNormalState);
266         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/transmit_blue.png"));
267
268         ui->progressBar->setValue(0);
269         ui->installButton->setEnabled(false);
270         ui->closeButton->setEnabled(false);
271         ui->retryButton->setEnabled(false);
272         ui->logButton->setEnabled(false);
273         if(ui->infoLabel->isVisible()) ui->infoLabel->hide();
274         if(ui->hintLabel->isVisible()) ui->hintLabel->hide();
275         if(ui->hintIcon->isVisible()) ui->hintIcon->hide();
276         ui->frameAnimation->show();
277
278         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
279         QApplication::setOverrideCursor(Qt::WaitCursor);
280
281         m_logFile->clear();
282         m_thread->start();
283 }
284
285 void UpdateDialog::threadStatusChanged(const int status)
286 {
287         switch(status)
288         {
289         case UpdateCheckThread::UpdateStatus_NotStartedYet:
290                 ui->statusLabel->setText(tr("Initializing, please wait..."));
291                 break;
292         case UpdateCheckThread::UpdateStatus_CheckingConnection:
293                 ui->statusLabel->setText(tr("Testing your internet connection, please wait..."));
294                 break;
295         case UpdateCheckThread::UpdateStatus_FetchingUpdates:
296                 ui->statusLabel->setText(tr("Checking for new updates online, please wait..."));
297                 break;
298         case UpdateCheckThread::UpdateStatus_CompletedUpdateAvailable:
299                 ui->statusLabel->setText(tr("A new version of LameXP is available!"));
300                 SHOW_HINT(tr("We highly recommend all users to install this update as soon as possible."), ":/icons/shield_exclamation.png");
301                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarNormalState, ":/icons/shield_exclamation.png");
302                 break;
303         case UpdateCheckThread::UpdateStatus_CompletedNoUpdates:
304                 ui->statusLabel->setText(tr("No new updates available at this time."));
305                 SHOW_HINT(tr("Your version of LameXP is still up-to-date. Please check for updates regularly!"), ":/icons/shield_green.png");
306                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarNormalState, ":/icons/shield_green.png");
307                 break;
308         case UpdateCheckThread::UpdateStatus_CompletedNewVersionOlder:
309                 ui->statusLabel->setText(tr("Your version appears to be newer than the latest release."));
310                 SHOW_HINT(tr("This usually indicates your are currently using a pre-release version of LameXP."), ":/icons/shield_blue.png");
311                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarNormalState, ":/icons/shield_error.png");
312                 break;
313         case UpdateCheckThread::UpdateStatus_ErrorNoConnection:
314                 ui->statusLabel->setText(tr("It appears that the computer currently is offline!"));
315                 SHOW_HINT(tr("Please make sure your computer is connected to the internet and try again."), ":/icons/network_error.png");
316                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarErrorState, ":/icons/exclamation.png");
317                 break;
318         case UpdateCheckThread::UpdateStatus_ErrorConnectionTestFailed:
319                 ui->statusLabel->setText(tr("Network connectivity test has failed!"));
320                 SHOW_HINT(tr("Please make sure your computer is connected to the internet and try again."), ":/icons/network_error.png");
321                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarErrorState, ":/icons/exclamation.png");
322                 break;
323         case UpdateCheckThread::UpdateStatus_ErrorFetchUpdateInfo:
324                 ui->statusLabel->setText(tr("Failed to fetch update information from server!"));
325                 SHOW_HINT(tr("Sorry, the update server might be busy at this time. Plase try again later."), ":/icons/server_error.png");
326                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarErrorState, ":/icons/exclamation.png");
327                 break;
328         default:
329                 qWarning("Unknown status %d !!!", int(status));
330         }
331 }
332
333 void UpdateDialog::threadProgressChanged(const int progress)
334 {
335         ui->progressBar->setValue(progress);
336 }
337
338 void UpdateDialog::threadMessageLogged(const QString &message)
339 {
340         (*m_logFile) << message;
341 }
342
343 void UpdateDialog::threadFinished(void)
344 {
345         const bool bSuccess = m_thread->getSuccess();
346         
347         ui->closeButton->setEnabled(true);
348         if(ui->frameAnimation->isVisible()) ui->frameAnimation->hide();
349         ui->progressBar->setValue(ui->progressBar->maximum());
350
351         if(!bSuccess)
352         {
353                 if(m_settings->soundsEnabled()) lamexp_play_sound("error", true);
354         }
355         else
356         {
357                 const bool bHaveUpdate = (m_thread->getUpdateStatus() == UpdateCheckThread::UpdateStatus_CompletedUpdateAvailable);
358                 ui->installButton->setEnabled(bHaveUpdate);
359                 lamexp_beep(bHaveUpdate ? lamexp_beep_info : lamexp_beep_warning);
360
361                 if(const UpdateInfo *const updateInfo = m_thread->getUpdateInfo())
362                 {
363                         ui->infoLabel->setText(QString("%1<br><a href=\"%2\">%2</a>").arg(tr("More information available at:"), updateInfo->m_downloadSite));
364                         ui->labelVersionLatest->setText(QString("%1 %2 (%3)").arg(tr("Build"), QString::number(updateInfo->m_buildNo), updateInfo->m_buildDate.toString(Qt::ISODate)));
365                         ui->infoLabel->show();
366                 }
367
368                 m_success = true;
369         }
370
371         ui->retryButton->setVisible(!bSuccess);
372         ui->logButton->setVisible(!bSuccess);
373         ui->retryButton->setEnabled(!bSuccess);
374         ui->logButton->setEnabled(!bSuccess);
375
376         QApplication::restoreOverrideCursor();
377 }
378
379 void UpdateDialog::linkActivated(const QString &link)
380 {
381         QDesktopServices::openUrl(QUrl(link));
382 }
383
384 void UpdateDialog::applyUpdate(void)
385 {
386         ui->installButton->setEnabled(false);
387         ui->closeButton->setEnabled(false);
388         ui->retryButton->setEnabled(false);
389
390         if(const UpdateInfo *updateInfo = m_thread->getUpdateInfo())
391         {
392                 ui->statusLabel->setText(tr("Update is being downloaded, please be patient..."));
393                 ui->frameAnimation->show();
394                 if(ui->hintLabel->isVisible()) ui->hintLabel->hide();
395                 if(ui->hintIcon->isVisible()) ui->hintIcon->hide();
396                 int oldMax = ui->progressBar->maximum();
397                 int oldMin = ui->progressBar->minimum();
398                 ui->progressBar->setRange(0, 0);
399                 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
400                 
401                 QProcess process;
402                 QStringList args;
403                 QEventLoop loop;
404
405                 lamexp_init_process(process, QFileInfo(m_binaryUpdater).absolutePath(), false);
406
407                 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
408                 connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()));
409
410                 args << QString("/Location=%1").arg(updateInfo->m_downloadAddress);
411                 args << QString("/Filename=%1").arg(updateInfo->m_downloadFilename);
412                 args << QString("/TicketID=%1").arg(updateInfo->m_downloadFilecode);
413                 args << QString("/ToFolder=%1").arg(QDir::toNativeSeparators(QDir(QApplication::applicationDirPath()).canonicalPath())); 
414                 args << QString("/ToExFile=%1.exe").arg(QFileInfo(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath()).completeBaseName());
415                 args << QString("/AppTitle=LameXP (Build #%1)").arg(QString::number(updateInfo->m_buildNo));
416
417                 QApplication::setOverrideCursor(Qt::WaitCursor);
418                 UPDATE_TASKBAR(WinSevenTaskbar::WinSevenTaskbarIndeterminateState, ":/icons/transmit_blue.png");
419
420                 process.start(m_binaryUpdater, args);
421                 bool updateStarted = process.waitForStarted();
422                 if(updateStarted)
423                 {
424                         m_updaterProcess = lamexp_process_id(&process);
425                         loop.exec(QEventLoop::ExcludeUserInputEvents);
426                 }
427
428                 m_updaterProcess = NULL;
429                 QApplication::restoreOverrideCursor();
430
431                 ui->hintLabel->show();
432                 ui->hintIcon->show();
433                 ui->progressBar->setRange(oldMin, oldMax);
434                 ui->progressBar->setValue(oldMax);
435                 ui->frameAnimation->hide();
436
437                 if(updateStarted && (process.exitCode() == 0))
438                 {
439                         ui->statusLabel->setText(tr("Update ready to install. Applicaion will quit..."));
440                         m_updateReadyToInstall = true;
441                         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarNoState);
442                         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), NULL);
443                         accept();
444                 }
445                 else
446                 {
447                         ui->statusLabel->setText(tr("Update failed. Please try again or download manually!"));
448                         WinSevenTaskbar::setTaskbarState(this->parentWidget(), WinSevenTaskbar::WinSevenTaskbarErrorState);
449                         WinSevenTaskbar::setOverlayIcon(this->parentWidget(), &QIcon(":/icons/exclamation.png"));
450                         WinSevenTaskbar::setTaskbarProgress(this->parentWidget(), 100, 100);
451                 }
452         }
453
454         ui->installButton->setEnabled(true);
455         ui->closeButton->setEnabled(true);
456 }
457
458 void UpdateDialog::logButtonClicked(void)
459 {
460         LogViewDialog *logView = new LogViewDialog(this);
461         logView->exec(*m_logFile);
462         LAMEXP_DELETE(logView);
463 }
464
465 void UpdateDialog::progressBarValueChanged(int value)
466 {
467         WinSevenTaskbar::setTaskbarProgress(this->parentWidget(), value, ui->progressBar->maximum());
468 }
469
470 void UpdateDialog::testKnownHosts(void)
471 {
472         ui->statusLabel->setText("Testing all known hosts, this may take a few minutes...");
473         
474         if(UpdateCheckThread *testThread = new UpdateCheckThread(m_binaryWGet, m_binaryGnuPG, m_binaryKeys, m_betaUpdates, true))
475         {
476                 QEventLoop loop;
477                 m_logFile->clear();
478
479                 connect(testThread, SIGNAL(messageLogged(QString)), this, SLOT(threadMessageLogged(QString)));
480                 connect(testThread, SIGNAL(finished()), &loop, SLOT(quit()));
481                 connect(testThread, SIGNAL(terminated()), &loop, SLOT(quit()));
482
483                 testThread->start();
484                 while(testThread->isRunning())
485                 {
486                         QTimer::singleShot(5000, &loop, SLOT(quit()));
487                         loop.exec(QEventLoop::ExcludeUserInputEvents);
488                 }
489
490                 LAMEXP_DELETE(testThread);
491                 logButtonClicked();
492         }
493
494         ui->statusLabel->setText("Test completed.");
495         lamexp_beep(lamexp_beep_info);
496 }