Added option for creating "pretty" file names to clean_file_name_make_pretty() function.
[MUtilities.git] / src / UpdateChecker.cpp
blobc36765beea8116236e3ba0ed74f353dc48ff1383
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 QUICK_MIRRORS = 3;
219 static const int MAX_CONN_TIMEOUT = 8000;
220 static const int DOWNLOAD_TIMEOUT = 30000;
222 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
223 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0"; /*use something innocuous*/
225 #define CHECK_CANCELLED() do \
227 if(m_cancelled) \
229 m_success = false; \
230 log("", "Update check has been cancelled by user!"); \
231 setProgress(m_maxProgress); \
232 setStatus(UpdateStatus_CancelledByUser); \
233 return; \
236 while(0)
238 ////////////////////////////////////////////////////////////
239 // Helper Functions
240 ////////////////////////////////////////////////////////////
242 static int getMaxProgress(void)
244 int counter = 0;
245 while (update_mirrors[counter])
247 counter++;
249 counter += MIN_CONNSCORE + QUICK_MIRRORS + 2;
250 return counter; ;
253 static QStringList buildRandomList(const char *const values[])
255 QStringList list;
256 for (int index = 0; values[index]; index++)
258 const int pos = next_rand_u32() % (index + 1);
259 list.insert(pos, QString::fromLatin1(values[index]));
261 return list;
264 ////////////////////////////////////////////////////////////
265 // Update Info Class
266 ////////////////////////////////////////////////////////////
268 UpdateCheckerInfo::UpdateCheckerInfo(void)
270 resetInfo();
273 void UpdateCheckerInfo::resetInfo(void)
275 m_buildNo = 0;
276 m_buildDate.setDate(1900, 1, 1);
277 m_downloadSite.clear();
278 m_downloadAddress.clear();
279 m_downloadFilename.clear();
280 m_downloadFilecode.clear();
281 m_downloadChecksum.clear();
284 bool UpdateCheckerInfo::isComplete(void)
286 if(this->m_buildNo < 1) return false;
287 if(this->m_buildDate.year() < 2010) return false;
288 if(this->m_downloadSite.isEmpty()) return false;
289 if(this->m_downloadAddress.isEmpty()) return false;
290 if(this->m_downloadFilename.isEmpty()) return false;
291 if(this->m_downloadFilecode.isEmpty()) return false;
292 if(this->m_downloadChecksum.isEmpty()) return false;
294 return true;
297 ////////////////////////////////////////////////////////////
298 // Constructor & Destructor
299 ////////////////////////////////////////////////////////////
301 UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binMCat, const QString &binGnuPG, const QString &binKeys, const QString &applicationId, const quint32 &installedBuildNo, const bool betaUpdates, const bool testMode)
303 m_updateInfo(new UpdateCheckerInfo()),
304 m_binaryWGet(binWGet),
305 m_binaryMCat(binMCat),
306 m_binaryGnuPG(binGnuPG),
307 m_binaryKeys(binKeys),
308 m_applicationId(applicationId),
309 m_installedBuildNo(installedBuildNo),
310 m_betaUpdates(betaUpdates),
311 m_testMode(testMode),
312 m_maxProgress(getMaxProgress())
314 m_success = m_cancelled = false;
315 m_status = UpdateStatus_NotStartedYet;
316 m_progress = 0;
318 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
320 MUTILS_THROW("Tools not initialized correctly!");
324 UpdateChecker::~UpdateChecker(void)
328 ////////////////////////////////////////////////////////////
329 // Public slots
330 ////////////////////////////////////////////////////////////
332 void UpdateChecker::start(Priority priority)
334 m_cancelled = m_success = false;
335 QThread::start(priority);
338 ////////////////////////////////////////////////////////////
339 // Protected functions
340 ////////////////////////////////////////////////////////////
342 void UpdateChecker::run(void)
344 qDebug("Update checker thread started!");
345 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
346 qDebug("Update checker thread completed.");
349 void UpdateChecker::checkForUpdates(void)
351 // ----- Initialization ----- //
353 m_updateInfo->resetInfo();
354 setProgress(0);
356 // ----- Test Internet Connection ----- //
358 log("Checking internet connection...", "");
359 setStatus(UpdateStatus_CheckingConnection);
361 const int networkStatus = OS::network_status();
362 if(networkStatus == OS::NETWORK_TYPE_NON)
364 log("Operating system reports that the computer is currently offline !!!");
365 setProgress(m_maxProgress);
366 setStatus(UpdateStatus_ErrorNoConnection);
367 return;
370 setProgress(1);
372 // ----- Test Known Hosts Connectivity ----- //
374 int connectionScore = 0;
375 QStringList mirrorList = buildRandomList(known_hosts);
377 for(int connectionTimout = 250; connectionTimout <= MAX_CONN_TIMEOUT; connectionTimout *= 2)
379 QElapsedTimer elapsedTimer;
380 elapsedTimer.start();
381 const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout;
382 while (!elapsedTimer.hasExpired(globalTimout))
384 const QString hostName = mirrorList.takeFirst();
385 if (tryContactHost(hostName, connectionTimout))
387 connectionScore += 1;
388 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
389 elapsedTimer.restart();
390 if (connectionScore >= MIN_CONNSCORE)
392 goto endLoop; /*success*/
395 else
397 mirrorList.append(hostName); /*re-schedule*/
399 CHECK_CANCELLED();
400 msleep(1);
404 endLoop:
405 if(connectionScore < MIN_CONNSCORE)
407 log("", "Connectivity test has failed: Internet connection appears to be broken!");
408 setProgress(m_maxProgress);
409 setStatus(UpdateStatus_ErrorConnectionTestFailed);
410 return;
413 // ----- Fetch Update Info From Server ----- //
415 log("----", "", "Checking for updates online...");
416 setStatus(UpdateStatus_FetchingUpdates);
418 int mirrorCount = 0;
419 mirrorList = buildRandomList(update_mirrors);
421 while(!mirrorList.isEmpty())
423 setProgress(m_progress + 1);
424 const QString currentMirror = mirrorList.takeFirst();
425 const bool isQuick = (mirrorCount++ < QUICK_MIRRORS);
426 if(tryUpdateMirror(m_updateInfo.data(), currentMirror, isQuick))
428 m_success = true; /*success*/
429 break;
431 if (isQuick)
433 mirrorList.append(currentMirror); /*re-schedule*/
435 CHECK_CANCELLED();
436 msleep(1);
439 while (m_progress < m_maxProgress)
441 msleep(16);
442 setProgress(m_progress + 1);
443 CHECK_CANCELLED();
446 // ----- Generate final result ----- //
448 if(m_success)
450 if(m_updateInfo->m_buildNo > m_installedBuildNo)
452 setStatus(UpdateStatus_CompletedUpdateAvailable);
454 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
456 setStatus(UpdateStatus_CompletedNoUpdates);
458 else
460 setStatus(UpdateStatus_CompletedNewVersionOlder);
463 else
465 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
469 void UpdateChecker::testKnownHosts(void)
471 QStringList hostList;
472 for(int i = 0; known_hosts[i]; i++)
474 hostList << QString::fromLatin1(known_hosts[i]);
477 qDebug("\n[Known Hosts]");
478 log("Testing all known hosts...", "", "---");
480 int hostCount = hostList.count();
481 while(!hostList.isEmpty())
483 QString currentHost = hostList.takeFirst();
484 qDebug("Testing: %s", currentHost.toLatin1().constData());
485 log("", "Testing:", currentHost, "");
486 if (!tryContactHost(currentHost, DOWNLOAD_TIMEOUT))
488 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
490 log("", "---");
494 ////////////////////////////////////////////////////////////
495 // PRIVATE FUNCTIONS
496 ////////////////////////////////////////////////////////////
498 void UpdateChecker::setStatus(const int status)
500 if(m_status != status)
502 m_status = status;
503 emit statusChanged(status);
507 void UpdateChecker::setProgress(const int progress)
509 if(m_progress != progress)
511 m_progress = progress;
512 emit progressChanged(progress);
516 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
518 if(!str1.isNull()) emit messageLogged(str1);
519 if(!str2.isNull()) emit messageLogged(str2);
520 if(!str3.isNull()) emit messageLogged(str3);
521 if(!str4.isNull()) emit messageLogged(str4);
524 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url, const bool &quick)
526 bool success = false;
527 log("", "Trying mirror:", url, "");
529 if (!tryContactHost(QUrl(url).host(), quick ? (MAX_CONN_TIMEOUT / 10) : MAX_CONN_TIMEOUT))
531 log("", quick ? "Mirror is too slow!" :"Mirror is unreachable!");
532 return false;
535 const QString randPart = next_rand_str();
536 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
537 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
539 if (getUpdateInfo(url, outFileVers, outFileSign))
541 log("", "Download okay, checking signature:");
542 if (checkSignature(outFileVers, outFileSign))
544 log("", "Signature okay, parsing info:", "");
545 success = parseVersionInfo(outFileVers, updateInfo);
547 else
549 log("", "Bad signature, take care!");
552 else
554 log("", "Download has failed!");
557 QFile::remove(outFileVers);
558 QFile::remove(outFileSign);
560 return success;
563 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
565 log("Downloading update info:", "");
566 if(getFile(QString("%1%2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
568 if (!m_cancelled)
570 log("", "Downloading signature:", "");
571 if (getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
573 return true;
577 return false;
580 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
582 QRegExp value("^(\\w+)=(.+)$");
583 QRegExp section("^\\[(.+)\\]$");
585 QDate updateInfoDate;
586 updateInfo->resetInfo();
588 QFile data(file);
589 if(!data.open(QIODevice::ReadOnly))
591 qWarning("Cannot open update info file for reading!");
592 return false;
595 bool inHdr = false;
596 bool inSec = false;
598 while(!data.atEnd())
600 QString line = QString::fromLatin1(data.readLine()).trimmed();
601 if(section.indexIn(line) >= 0)
603 log(QString("Sec: [%1]").arg(section.cap(1)));
604 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
605 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
606 continue;
608 if(inSec && (value.indexIn(line) >= 0))
610 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
611 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
613 bool ok = false;
614 const unsigned int temp = value.cap(2).toUInt(&ok);
615 if(ok) updateInfo->m_buildNo = temp;
617 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
619 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
620 if(temp.isValid()) updateInfo->m_buildDate = temp;
622 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
624 updateInfo->m_downloadSite = value.cap(2).trimmed();
626 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
628 updateInfo->m_downloadAddress = value.cap(2).trimmed();
630 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
632 updateInfo->m_downloadFilename = value.cap(2).trimmed();
634 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
636 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
638 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
640 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
643 if(inHdr && (value.indexIn(line) >= 0))
645 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
646 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
648 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
649 if(temp.isValid()) updateInfoDate = temp;
654 if(!updateInfoDate.isValid())
656 updateInfo->resetInfo();
657 log("WARNING: Version info timestamp is missing!");
658 return false;
661 const QDate currentDate = OS::current_date();
662 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
664 updateInfo->resetInfo();
665 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
666 return false;
668 else if(currentDate < updateInfoDate)
670 log("Version info is from the future, take care!");
671 qWarning("Version info is from the future, take care!");
674 if(!updateInfo->isComplete())
676 log("WARNING: Version info is incomplete!");
677 return false;
680 return true;
683 //----------------------------------------------------------
684 // EXTERNAL TOOLS
685 //----------------------------------------------------------
687 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir)
689 for (int i = 0; i < 2; i++)
691 if (getFile(url, (i > 0), outFile, maxRedir))
693 return true;
695 if (m_cancelled)
697 break; /*cancelled*/
700 return false;
703 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir)
705 QFileInfo output(outFile);
706 output.setCaching(false);
708 if (output.exists())
710 QFile::remove(output.canonicalFilePath());
711 if (output.exists())
713 return false;
717 QProcess process;
718 init_process(process, output.absolutePath());
720 QStringList args;
721 if (forceIp4)
723 args << "-4";
726 args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts";
727 args << QString().sprintf("--max-redirect=%u", maxRedir) << QString().sprintf("--timeout=%u", DOWNLOAD_TIMEOUT / 1000);
728 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
729 args << "-O" << output.fileName() << url;
731 QEventLoop loop;
732 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
733 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
734 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
736 QTimer timer;
737 timer.setSingleShot(true);
738 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
740 const QRegExp httpResponseOK("200 OK$");
742 process.start(m_binaryWGet, args);
744 if (!process.waitForStarted())
746 return false;
749 timer.start(DOWNLOAD_TIMEOUT);
751 while (process.state() != QProcess::NotRunning)
753 loop.exec();
754 const bool bTimeOut = (!timer.isActive());
755 while (process.canReadLine())
757 const QString line = QString::fromLatin1(process.readLine()).simplified();
758 log(line);
760 if (bTimeOut || m_cancelled)
762 qWarning("WGet process timed out <-- killing!");
763 process.kill();
764 process.waitForFinished();
765 log(bTimeOut ? "!!! TIMEOUT !!!": "!!! CANCELLED !!!");
766 return false;
770 timer.stop();
771 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
773 log(QString().sprintf("Exited with code %d", process.exitCode()));
774 return (process.exitCode() == 0) && output.exists() && output.isFile();
777 bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec)
779 log(QString("Connecting to host: %1").arg(hostname), "");
781 QProcess process;
782 init_process(process, temp_folder());
784 QStringList args;
785 args << "--retry" << QString::number(3) << hostname << QString::number(80);
787 QEventLoop loop;
788 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
789 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
790 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
792 QTimer timer;
793 timer.setSingleShot(true);
794 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
796 process.start(m_binaryMCat, args);
798 if (!process.waitForStarted())
800 return false;
803 timer.start(timeoutMsec);
805 while (process.state() != QProcess::NotRunning)
807 loop.exec();
808 const bool bTimeOut = (!timer.isActive());
809 while (process.canReadLine())
811 QString line = QString::fromLatin1(process.readLine()).simplified();
812 log(line);
814 if (bTimeOut || m_cancelled)
816 qWarning("MCat process timed out <-- killing!");
817 process.kill();
818 process.waitForFinished();
819 log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!");
820 return false;
824 timer.stop();
825 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
827 if (process.exitCode() != 0)
829 log("Connection has failed!");
832 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
833 return (process.exitCode() == 0);
836 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
838 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
840 qWarning("CheckSignature: File and signature should be in same folder!");
841 return false;
844 QString keyRingPath(m_binaryKeys);
845 bool removeKeyring = false;
846 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
848 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
849 removeKeyring = true;
850 if (!QFile::copy(m_binaryKeys, keyRingPath))
852 qWarning("CheckSignature: Failed to copy the key-ring file!");
853 return false;
857 QProcess process;
858 init_process(process, QFileInfo(file).absolutePath());
860 QEventLoop loop;
861 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
862 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
863 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
865 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
867 if (!process.waitForStarted())
869 if (removeKeyring)
871 remove_file(keyRingPath);
873 return false;
876 while (process.state() == QProcess::Running)
878 loop.exec();
879 while (process.canReadLine())
881 log(QString::fromLatin1(process.readLine()).simplified());
885 if (removeKeyring)
887 remove_file(keyRingPath);
890 log(QString().sprintf("Exited with code %d", process.exitCode()));
891 return (process.exitCode() == 0);
894 ////////////////////////////////////////////////////////////
895 // SLOTS
896 ////////////////////////////////////////////////////////////
898 /*NONE*/
900 ////////////////////////////////////////////////////////////
901 // EVENTS
902 ////////////////////////////////////////////////////////////
904 /*NONE*/