OSDN Git Service

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