Some code clean-up + make it possible to cancel UpdateChecker thread.
[MUtilities.git] / src / UpdateChecker.cpp
blob81e80c83a4a7f1ed0e72724fe3ec465fba5c894a
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2017 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library 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 GNU
13 // Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
22 #include <MUtils/Global.h>
23 #include <MUtils/UpdateChecker.h>
24 #include <MUtils/OSSupport.h>
25 #include <MUtils/Exception.h>
27 #include <QStringList>
28 #include <QFile>
29 #include <QFileInfo>
30 #include <QProcess>
31 #include <QUrl>
32 #include <QEventLoop>
33 #include <QTimer>
34 #include <QElapsedTimer>
36 using namespace MUtils;
38 ///////////////////////////////////////////////////////////////////////////////
39 // CONSTANTS
40 ///////////////////////////////////////////////////////////////////////////////
42 static const char *header_id = "!Update";
44 static const char *mirror_url_postfix[] =
46 "update.ver",
47 "update_beta.ver",
48 NULL
51 static const char *update_mirrors[] =
53 "http://muldersoft.com/",
54 "http://mulder.bplaced.net/",
55 "http://mulder.6te.net/",
56 "http://mulder.webuda.com/",
57 "http://mulder.pe.hu/",
58 "http://muldersoft.square7.ch/",
59 "http://muldersoft.co.nf/",
60 "http://muldersoft.eu.pn/",
61 "http://muldersoft.lima-city.de/",
62 "http://www.muldersoft.keepfree.de/",
63 "http://lamexp.sourceforge.net/",
64 "http://lordmulder.github.io/LameXP/",
65 "http://muldersoft.bitbucket.io/", //http://lord_mulder.bitbucket.org/
66 "http://www.tricksoft.de/",
67 "http://repo.or.cz/LameXP.git/blob_plain/gh-pages:/",
68 "http://gitlab.com/lamexp/lamexp/raw/gh-pages/",
69 NULL
72 static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!!
74 "www.163.com",
75 "www.7-zip.org",
76 "www.ac3filter.net",
77 "clbianco.altervista.org",
78 "status.aws.amazon.com",
79 "build.antergos.com",
80 "www.aol.com",
81 "www.apache.org",
82 "www.apple.com",
83 "www.adobe.com",
84 "archive.org",
85 "www.artlebedev.ru",
86 "web.audacityteam.org",
87 "status.automattic.com",
88 "www.avidemux.org",
89 "www.babylon.com",
90 "www.baidu.com",
91 "bandcamp.com",
92 "www.bbc.co.uk",
93 "www.berlios.de",
94 "www.bing.com",
95 "www.bingeandgrab.com",
96 "www.bucketheadpikes.com",
97 "www.buckethead-coop.com",
98 "www.buzzfeed.com",
99 "www.cam.ac.uk",
100 "www.ccc.de",
101 "home.cern",
102 "www.citizeninsomniac.com",
103 "www.cnet.com",
104 "cnzz.com",
105 "www.cuhk.edu.hk",
106 "www.codeplex.com",
107 "www.codeproject.com",
108 "www.der-postillon.com",
109 "www.ebay.com",
110 "www.equation.com",
111 "www.farbrausch.de",
112 "fc2.com",
113 "fedoraproject.org",
114 "blog.fefe.de",
115 "www.ffmpeg.org",
116 "blog.flickr.net",
117 "www.fraunhofer.de",
118 "free-codecs.com",
119 "git-scm.com",
120 "doc.gitlab.com",
121 "www.gmx.net",
122 "news.gnome.org",
123 "www.gnu.org",
124 "go.com",
125 "code.google.com",
126 "haali.su",
127 "www.harvard.edu",
128 "www.heise.de",
129 "www.helmholtz.de",
130 "www.huffingtonpost.co.uk",
131 "www.hu-berlin.de",
132 "www.iana.org",
133 "www.imdb.com",
134 "www.imgburn.com",
135 "imgur.com",
136 "www.iuj.ac.jp",
137 "www.jd.com",
138 "www.jiscdigitalmedia.ac.uk",
139 "kannmanumdieuhrzeitschonnbierchentrinken.de",
140 "mirrors.kernel.org",
141 "komisar.gin.by",
142 "lame.sourceforge.net",
143 "www.libav.org",
144 "blog.linkedin.com",
145 "www.linuxmint.com",
146 "www.livedoor.com",
147 "www.livejournal.com",
148 "longplayer.org",
149 "go.mail.ru",
150 "marknelson.us",
151 "www.mediafire.com",
152 "web.mit.edu",
153 "www.mod-technologies.com",
154 "ftp.mozilla.org",
155 "www.mpg.de",
156 "mplayerhq.hu",
157 "www.msn.com",
158 "wiki.multimedia.cx",
159 "www.nch.com.au",
160 "neocities.org",
161 "mirror.netcologne.de",
162 "oss.netfarm.it",
163 "blog.netflix.com",
164 "netrenderer.de",
165 "www.nytimes.com",
166 "www.opera.com",
167 "www.oxford.gov.uk",
168 "www.partha.com",
169 "pastebin.com",
170 "pastie.org",
171 "portableapps.com",
172 "www.portablefreeware.com",
173 "support.proboards.com",
174 "www.qq.com",
175 "www.qt.io",
176 "www.quakelive.com",
177 "rationalqm.us",
178 "www.rwth-aachen.de",
179 "www.seamonkey-project.org",
180 "selfhtml.org",
181 "www.sina.com.cn",
182 "www.sohu.com",
183 "help.sogou.com",
184 "sourceforge.net",
185 "www.spiegel.de",
186 "www.sputnikmusic.com",
187 "stackoverflow.com",
188 "www.stanford.edu",
189 "www.t-online.de",
190 "www.tagesschau.de",
191 "tdm-gcc.tdragon.net",
192 "www.tdrsmusic.com",
193 "tu-dresden.de",
194 "www.ubuntu.com",
195 "www.uol.com.br",
196 "www.videohelp.com",
197 "www.videolan.org",
198 "virtualdub.org",
199 "blog.virustotal.com",
200 "www.vkgoeswild.com",
201 "www.warr.org",
202 "www.weibo.com",
203 "status.wikimedia.org",
204 "www.winamp.com",
205 "www.winhoros.de",
206 "wpde.org",
207 "x265.org",
208 "xhmikosr.1f0.de",
209 "xiph.org",
210 "us.mail.yahoo.com",
211 "www.youtube.com",
212 "www.zedo.com",
213 "ffmpeg.zeranoe.com",
214 NULL
217 static const int MIN_CONNSCORE = 5;
218 static const int MAX_CONN_TIMEOUT = 8000;
219 static const int DOWNLOAD_TIMEOUT = 30000;
221 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
222 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0"; /*use something innocuous*/
224 #define CHECK_CANCELLED() do \
226 if(m_cancelled) \
228 m_success = false; \
229 log("", "Update check has been cancelled by user!"); \
230 setProgress(m_maxProgress); \
231 setStatus(UpdateStatus_CancelledByUser); \
232 return; \
235 while(0)
237 ////////////////////////////////////////////////////////////
238 // Helper Functions
239 ////////////////////////////////////////////////////////////
241 static int getMaxProgress(void)
243 int counter = MIN_CONNSCORE + 2;
244 for(int i = 0; update_mirrors[i]; i++) counter++;
245 return counter;
248 static QStringList buildRandomList(const char *const values[])
250 QStringList list;
251 for (int index = 0; values[index]; index++)
253 const int pos = next_rand_u32() % (index + 1);
254 list.insert(pos, QString::fromLatin1(values[index]));
256 return list;
259 ////////////////////////////////////////////////////////////
260 // Update Info Class
261 ////////////////////////////////////////////////////////////
263 UpdateCheckerInfo::UpdateCheckerInfo(void)
265 resetInfo();
268 void UpdateCheckerInfo::resetInfo(void)
270 m_buildNo = 0;
271 m_buildDate.setDate(1900, 1, 1);
272 m_downloadSite.clear();
273 m_downloadAddress.clear();
274 m_downloadFilename.clear();
275 m_downloadFilecode.clear();
276 m_downloadChecksum.clear();
279 bool UpdateCheckerInfo::isComplete(void)
281 if(this->m_buildNo < 1) return false;
282 if(this->m_buildDate.year() < 2010) return false;
283 if(this->m_downloadSite.isEmpty()) return false;
284 if(this->m_downloadAddress.isEmpty()) return false;
285 if(this->m_downloadFilename.isEmpty()) return false;
286 if(this->m_downloadFilecode.isEmpty()) return false;
287 if(this->m_downloadChecksum.isEmpty()) return false;
289 return true;
292 ////////////////////////////////////////////////////////////
293 // Constructor & Destructor
294 ////////////////////////////////////////////////////////////
296 UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binNC, const QString &binGnuPG, const QString &binKeys, const QString &applicationId, const quint32 &installedBuildNo, const bool betaUpdates, const bool testMode)
298 m_updateInfo(new UpdateCheckerInfo()),
299 m_binaryWGet(binWGet),
300 m_binaryNC(binNC),
301 m_binaryGnuPG(binGnuPG),
302 m_binaryKeys(binKeys),
303 m_applicationId(applicationId),
304 m_installedBuildNo(installedBuildNo),
305 m_betaUpdates(betaUpdates),
306 m_testMode(testMode),
307 m_maxProgress(getMaxProgress())
309 m_success = m_cancelled = false;
310 m_status = UpdateStatus_NotStartedYet;
311 m_progress = 0;
313 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
315 MUTILS_THROW("Tools not initialized correctly!");
319 UpdateChecker::~UpdateChecker(void)
323 ////////////////////////////////////////////////////////////
324 // Public slots
325 ////////////////////////////////////////////////////////////
327 void UpdateChecker::start(Priority priority)
329 m_cancelled = m_success = false;
330 QThread::start(priority);
333 ////////////////////////////////////////////////////////////
334 // Protected functions
335 ////////////////////////////////////////////////////////////
337 void UpdateChecker::run(void)
339 qDebug("Update checker thread started!");
340 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
341 qDebug("Update checker thread completed.");
344 void UpdateChecker::checkForUpdates(void)
346 // ----- Initialization ----- //
348 m_updateInfo->resetInfo();
349 setProgress(0);
351 // ----- Test Internet Connection ----- //
353 log("Checking internet connection...", "");
354 setStatus(UpdateStatus_CheckingConnection);
356 const int networkStatus = OS::network_status();
357 if(networkStatus == OS::NETWORK_TYPE_NON)
359 log("Operating system reports that the computer is currently offline !!!");
360 setProgress(m_maxProgress);
361 setStatus(UpdateStatus_ErrorNoConnection);
362 return;
365 setProgress(1);
367 // ----- Test Known Hosts Connectivity ----- //
369 int connectionScore = 0;
370 QStringList hostList = buildRandomList(known_hosts);
372 for(int connectionTimout = 125; connectionTimout <= MAX_CONN_TIMEOUT; connectionTimout *= 2)
374 QElapsedTimer elapsedTimer;
375 elapsedTimer.start();
376 const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout;
377 while (!elapsedTimer.hasExpired(globalTimout))
379 const QString hostName = hostList.takeFirst();
380 if (tryContactHost(hostName, connectionTimout))
382 connectionScore += 1;
383 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
384 elapsedTimer.restart();
385 if (connectionScore >= MIN_CONNSCORE)
387 goto endLoop; /*success*/
390 else
392 hostList.append(hostName); /*re-schedule*/
394 CHECK_CANCELLED();
395 msleep(1);
399 endLoop:
400 if(connectionScore < MIN_CONNSCORE)
402 log("", "Connectivity test has failed: Internet connection appears to be broken!");
403 setProgress(m_maxProgress);
404 setStatus(UpdateStatus_ErrorConnectionTestFailed);
405 return;
408 // ----- Fetch Update Info From Server ----- //
410 log("----", "", "Checking for updates online...");
411 setStatus(UpdateStatus_FetchingUpdates);
413 QStringList mirrorList = buildRandomList(update_mirrors);
414 while(!mirrorList.isEmpty())
416 const QString currentMirror = mirrorList.takeFirst();
417 setProgress(m_progress + 1);
418 if(!m_success)
420 CHECK_CANCELLED();
421 if(tryUpdateMirror(m_updateInfo.data(), currentMirror))
423 m_success = true;
426 else
428 msleep(25);
432 CHECK_CANCELLED();
433 setProgress(m_maxProgress);
435 if(m_success)
437 if(m_updateInfo->m_buildNo > m_installedBuildNo)
439 setStatus(UpdateStatus_CompletedUpdateAvailable);
441 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
443 setStatus(UpdateStatus_CompletedNoUpdates);
445 else
447 setStatus(UpdateStatus_CompletedNewVersionOlder);
450 else
452 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
456 void UpdateChecker::testKnownHosts(void)
458 QStringList hostList;
459 for(int i = 0; known_hosts[i]; i++)
461 hostList << QString::fromLatin1(known_hosts[i]);
464 qDebug("\n[Known Hosts]");
465 log("Testing all known hosts...", "", "---");
467 int hostCount = hostList.count();
468 while(!hostList.isEmpty())
470 QString currentHost = hostList.takeFirst();
471 qDebug("Testing: %s", currentHost.toLatin1().constData());
472 log("", "Testing:", currentHost, "");
473 if (!tryContactHost(currentHost, DOWNLOAD_TIMEOUT))
475 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
477 log("", "---");
481 ////////////////////////////////////////////////////////////
482 // PRIVATE FUNCTIONS
483 ////////////////////////////////////////////////////////////
485 void UpdateChecker::setStatus(const int status)
487 if(m_status != status)
489 m_status = status;
490 emit statusChanged(status);
494 void UpdateChecker::setProgress(const int progress)
496 if(m_progress != progress)
498 m_progress = progress;
499 emit progressChanged(progress);
503 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
505 if(!str1.isNull()) emit messageLogged(str1);
506 if(!str2.isNull()) emit messageLogged(str2);
507 if(!str3.isNull()) emit messageLogged(str3);
508 if(!str4.isNull()) emit messageLogged(str4);
511 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url)
513 bool success = false;
514 log("", "Trying mirror:", url, "");
516 if (!tryContactHost(QUrl(url).host(), MAX_CONN_TIMEOUT))
518 log("", "Mirror is unreachable!");
519 return false;
522 const QString randPart = next_rand_str();
523 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
524 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
526 if (getUpdateInfo(url, outFileVers, outFileSign))
528 log("", "Download okay, checking signature:");
529 if (checkSignature(outFileVers, outFileSign))
531 log("", "Signature okay, parsing info:");
532 success = parseVersionInfo(outFileVers, updateInfo);
534 else
536 log("", "Bad signature, take care!");
539 else
541 log("", "Download has failed!");
544 QFile::remove(outFileVers);
545 QFile::remove(outFileSign);
547 return success;
550 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
552 log("Downloading update info:");
553 if(!getFile(QString("%1%2" ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
555 return false;
558 log("", "Downloading signature:");
559 if(!getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
561 return false;
564 return true;
567 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
569 QRegExp value("^(\\w+)=(.+)$");
570 QRegExp section("^\\[(.+)\\]$");
572 QDate updateInfoDate;
573 updateInfo->resetInfo();
575 QFile data(file);
576 if(!data.open(QIODevice::ReadOnly))
578 qWarning("Cannot open update info file for reading!");
579 return false;
582 bool inHdr = false;
583 bool inSec = false;
585 while(!data.atEnd())
587 QString line = QString::fromLatin1(data.readLine()).trimmed();
588 if(section.indexIn(line) >= 0)
590 log(QString("Sec: [%1]").arg(section.cap(1)));
591 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
592 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
593 continue;
595 if(inSec && (value.indexIn(line) >= 0))
597 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
598 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
600 bool ok = false;
601 const unsigned int temp = value.cap(2).toUInt(&ok);
602 if(ok) updateInfo->m_buildNo = temp;
604 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
606 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
607 if(temp.isValid()) updateInfo->m_buildDate = temp;
609 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
611 updateInfo->m_downloadSite = value.cap(2).trimmed();
613 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
615 updateInfo->m_downloadAddress = value.cap(2).trimmed();
617 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
619 updateInfo->m_downloadFilename = value.cap(2).trimmed();
621 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
623 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
625 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
627 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
630 if(inHdr && (value.indexIn(line) >= 0))
632 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
633 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
635 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
636 if(temp.isValid()) updateInfoDate = temp;
641 if(!updateInfoDate.isValid())
643 updateInfo->resetInfo();
644 log("WARNING: Version info timestamp is missing!");
645 return false;
648 const QDate currentDate = OS::current_date();
649 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
651 updateInfo->resetInfo();
652 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
653 return false;
655 else if(currentDate < updateInfoDate)
657 log("Version info is from the future, take care!");
658 qWarning("Version info is from the future, take care!");
661 if(!updateInfo->isComplete())
663 log("WARNING: Version info is incomplete!");
664 return false;
667 return true;
670 //----------------------------------------------------------
671 // EXTERNAL TOOLS
672 //----------------------------------------------------------
674 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir)
676 for (int i = 0; i < 2; i++)
678 if (getFile(url, (i > 0), outFile, maxRedir))
680 return true;
683 return false;
686 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir)
688 QFileInfo output(outFile);
689 output.setCaching(false);
691 if (output.exists())
693 QFile::remove(output.canonicalFilePath());
694 if (output.exists())
696 return false;
700 QProcess process;
701 init_process(process, output.absolutePath());
703 QStringList args;
704 if (forceIp4)
706 args << "-4";
709 args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts";
710 args << QString().sprintf("--max-redirect=%u", maxRedir) << QString().sprintf("--timeout=%u", DOWNLOAD_TIMEOUT / 1000);
711 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
712 args << "-O" << output.fileName() << url;
714 QEventLoop loop;
715 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
716 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
717 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
719 QTimer timer;
720 timer.setSingleShot(true);
721 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
723 const QRegExp httpResponseOK("200 OK$");
725 process.start(m_binaryWGet, args);
727 if (!process.waitForStarted())
729 return false;
732 timer.start(DOWNLOAD_TIMEOUT);
734 while (process.state() != QProcess::NotRunning)
736 loop.exec();
737 const bool bTimeOut = (!timer.isActive());
738 while (process.canReadLine())
740 const QString line = QString::fromLatin1(process.readLine()).simplified();
741 log(line);
743 if (bTimeOut || m_cancelled)
745 qWarning("WGet process timed out <-- killing!");
746 process.kill();
747 process.waitForFinished();
748 log(bTimeOut ? "!!! TIMEOUT !!!": "!!! CANCELLED !!!");
749 return false;
753 timer.stop();
754 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
756 log(QString().sprintf("Exited with code %d", process.exitCode()));
757 return (process.exitCode() == 0) && output.exists() && output.isFile();
760 bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec)
762 log(QString("Connecting to host: %1").arg(hostname));
764 QProcess process;
765 init_process(process, temp_folder());
767 QStringList args;
768 args << "-z" << hostname << QString::number(80);
770 QEventLoop loop;
771 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
772 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
773 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
775 QTimer timer;
776 timer.setSingleShot(true);
777 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
779 process.start(m_binaryNC, args);
781 if (!process.waitForStarted())
783 return false;
786 timer.start(timeoutMsec);
788 while (process.state() != QProcess::NotRunning)
790 loop.exec();
791 const bool bTimeOut = (!timer.isActive());
792 while (process.canReadLine())
794 QString line = QString::fromLatin1(process.readLine()).simplified();
795 log(line);
797 if (bTimeOut || m_cancelled)
799 qWarning("NC process timed out <-- killing!");
800 process.kill();
801 process.waitForFinished();
802 log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!");
803 return false;
807 timer.stop();
808 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
810 if (process.exitCode() != 0)
812 log("Connection has failed!");
815 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
816 return (process.exitCode() == 0);
819 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
821 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
823 qWarning("CheckSignature: File and signature should be in same folder!");
824 return false;
827 QString keyRingPath(m_binaryKeys);
828 bool removeKeyring = false;
829 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
831 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
832 removeKeyring = true;
833 if (!QFile::copy(m_binaryKeys, keyRingPath))
835 qWarning("CheckSignature: Failed to copy the key-ring file!");
836 return false;
840 QProcess process;
841 init_process(process, QFileInfo(file).absolutePath());
843 QEventLoop loop;
844 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
845 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
846 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
848 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
850 if (!process.waitForStarted())
852 if (removeKeyring)
854 remove_file(keyRingPath);
856 return false;
859 while (process.state() == QProcess::Running)
861 loop.exec();
862 while (process.canReadLine())
864 log(QString::fromLatin1(process.readLine()).simplified());
868 if (removeKeyring)
870 remove_file(keyRingPath);
873 log(QString().sprintf("Exited with code %d", process.exitCode()));
874 return (process.exitCode() == 0);
877 ////////////////////////////////////////////////////////////
878 // SLOTS
879 ////////////////////////////////////////////////////////////
881 /*NONE*/
883 ////////////////////////////////////////////////////////////
884 // EVENTS
885 ////////////////////////////////////////////////////////////
887 /*NONE*/