Some code refactoring.
[MUtilities.git] / src / UpdateChecker.cpp
blob46441601f016dec8663a1fae2aef93c53a12cc8f
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 IS_CANCELLED (!(!m_cancelled))
226 #define CHECK_CANCELLED() do \
228 if(IS_CANCELLED) \
230 m_success = false; \
231 log("", "Update check has been cancelled by user!"); \
232 setProgress(m_maxProgress); \
233 setStatus(UpdateStatus_CancelledByUser); \
234 return; \
237 while(0)
239 ////////////////////////////////////////////////////////////
240 // Helper Functions
241 ////////////////////////////////////////////////////////////
243 static int getMaxProgress(void)
245 int counter = 0;
246 while (update_mirrors[counter])
248 counter++;
250 counter += MIN_CONNSCORE + QUICK_MIRRORS + 2;
251 return counter; ;
254 static QStringList buildRandomList(const char *const values[])
256 QStringList list;
257 for (int index = 0; values[index]; index++)
259 const int pos = next_rand_u32() % (index + 1);
260 list.insert(pos, QString::fromLatin1(values[index]));
262 return list;
265 ////////////////////////////////////////////////////////////
266 // Update Info Class
267 ////////////////////////////////////////////////////////////
269 UpdateCheckerInfo::UpdateCheckerInfo(void)
271 resetInfo();
274 void UpdateCheckerInfo::resetInfo(void)
276 m_buildNo = 0;
277 m_buildDate.setDate(1900, 1, 1);
278 m_downloadSite.clear();
279 m_downloadAddress.clear();
280 m_downloadFilename.clear();
281 m_downloadFilecode.clear();
282 m_downloadChecksum.clear();
285 bool UpdateCheckerInfo::isComplete(void)
287 if(this->m_buildNo < 1) return false;
288 if(this->m_buildDate.year() < 2010) return false;
289 if(this->m_downloadSite.isEmpty()) return false;
290 if(this->m_downloadAddress.isEmpty()) return false;
291 if(this->m_downloadFilename.isEmpty()) return false;
292 if(this->m_downloadFilecode.isEmpty()) return false;
293 if(this->m_downloadChecksum.isEmpty()) return false;
295 return true;
298 ////////////////////////////////////////////////////////////
299 // Constructor & Destructor
300 ////////////////////////////////////////////////////////////
302 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)
304 m_updateInfo(new UpdateCheckerInfo()),
305 m_binaryWGet(binWGet),
306 m_binaryMCat(binMCat),
307 m_binaryGnuPG(binGnuPG),
308 m_binaryKeys(binKeys),
309 m_applicationId(applicationId),
310 m_installedBuildNo(installedBuildNo),
311 m_betaUpdates(betaUpdates),
312 m_testMode(testMode),
313 m_maxProgress(getMaxProgress())
315 m_success = false;
316 m_status = UpdateStatus_NotStartedYet;
317 m_progress = 0;
319 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
321 MUTILS_THROW("Tools not initialized correctly!");
325 UpdateChecker::~UpdateChecker(void)
329 ////////////////////////////////////////////////////////////
330 // Public slots
331 ////////////////////////////////////////////////////////////
333 void UpdateChecker::start(Priority priority)
335 m_success = false;
336 m_cancelled.fetchAndStoreOrdered(0);
337 QThread::start(priority);
340 ////////////////////////////////////////////////////////////
341 // Protected functions
342 ////////////////////////////////////////////////////////////
344 void UpdateChecker::run(void)
346 qDebug("Update checker thread started!");
347 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
348 qDebug("Update checker thread completed.");
351 void UpdateChecker::checkForUpdates(void)
353 // ----- Initialization ----- //
355 m_updateInfo->resetInfo();
356 setProgress(0);
358 // ----- Test Internet Connection ----- //
360 log("Checking internet connection...", "");
361 setStatus(UpdateStatus_CheckingConnection);
363 const int networkStatus = OS::network_status();
364 if(networkStatus == OS::NETWORK_TYPE_NON)
366 log("Operating system reports that the computer is currently offline !!!");
367 setProgress(m_maxProgress);
368 setStatus(UpdateStatus_ErrorNoConnection);
369 return;
372 setProgress(1);
374 // ----- Test Known Hosts Connectivity ----- //
376 int connectionScore = 0;
377 QStringList mirrorList = buildRandomList(known_hosts);
379 for(int connectionTimout = 250; connectionTimout <= MAX_CONN_TIMEOUT; connectionTimout *= 2)
381 QElapsedTimer elapsedTimer;
382 elapsedTimer.start();
383 const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout;
384 while (!elapsedTimer.hasExpired(globalTimout))
386 const QString hostName = mirrorList.takeFirst();
387 if (tryContactHost(hostName, connectionTimout))
389 connectionScore += 1;
390 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
391 elapsedTimer.restart();
392 if (connectionScore >= MIN_CONNSCORE)
394 goto endLoop; /*success*/
397 else
399 mirrorList.append(hostName); /*re-schedule*/
401 CHECK_CANCELLED();
402 msleep(1);
406 endLoop:
407 if(connectionScore < MIN_CONNSCORE)
409 log("", "Connectivity test has failed: Internet connection appears to be broken!");
410 setProgress(m_maxProgress);
411 setStatus(UpdateStatus_ErrorConnectionTestFailed);
412 return;
415 // ----- Fetch Update Info From Server ----- //
417 log("----", "", "Checking for updates online...");
418 setStatus(UpdateStatus_FetchingUpdates);
420 int mirrorCount = 0;
421 mirrorList = buildRandomList(update_mirrors);
423 while(!mirrorList.isEmpty())
425 setProgress(m_progress + 1);
426 const QString currentMirror = mirrorList.takeFirst();
427 const bool isQuick = (mirrorCount++ < QUICK_MIRRORS);
428 if(tryUpdateMirror(m_updateInfo.data(), currentMirror, isQuick))
430 m_success = true; /*success*/
431 break;
433 if (isQuick)
435 mirrorList.append(currentMirror); /*re-schedule*/
437 CHECK_CANCELLED();
438 msleep(1);
441 while (m_progress < m_maxProgress)
443 msleep(16);
444 setProgress(m_progress + 1);
445 CHECK_CANCELLED();
448 // ----- Generate final result ----- //
450 if(m_success)
452 if(m_updateInfo->m_buildNo > m_installedBuildNo)
454 setStatus(UpdateStatus_CompletedUpdateAvailable);
456 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
458 setStatus(UpdateStatus_CompletedNoUpdates);
460 else
462 setStatus(UpdateStatus_CompletedNewVersionOlder);
465 else
467 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
471 void UpdateChecker::testKnownHosts(void)
473 QStringList hostList;
474 for(int i = 0; known_hosts[i]; i++)
476 hostList << QString::fromLatin1(known_hosts[i]);
479 qDebug("\n[Known Hosts]");
480 log("Testing all known hosts...", "", "---");
482 int hostCount = hostList.count();
483 while(!hostList.isEmpty())
485 QString currentHost = hostList.takeFirst();
486 qDebug("Testing: %s", currentHost.toLatin1().constData());
487 log("", "Testing:", currentHost, "");
488 if (!tryContactHost(currentHost, DOWNLOAD_TIMEOUT))
490 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
492 log("", "---");
496 ////////////////////////////////////////////////////////////
497 // PRIVATE FUNCTIONS
498 ////////////////////////////////////////////////////////////
500 void UpdateChecker::setStatus(const int status)
502 if(m_status != status)
504 m_status = status;
505 emit statusChanged(status);
509 void UpdateChecker::setProgress(const int progress)
511 if(m_progress != progress)
513 m_progress = progress;
514 emit progressChanged(progress);
518 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
520 if(!str1.isNull()) emit messageLogged(str1);
521 if(!str2.isNull()) emit messageLogged(str2);
522 if(!str3.isNull()) emit messageLogged(str3);
523 if(!str4.isNull()) emit messageLogged(str4);
526 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url, const bool &quick)
528 bool success = false;
529 log("", "Trying mirror:", url, "");
531 if (!tryContactHost(QUrl(url).host(), quick ? (MAX_CONN_TIMEOUT / 10) : MAX_CONN_TIMEOUT))
533 log("", quick ? "Mirror is too slow!" :"Mirror is unreachable!");
534 return false;
537 const QString randPart = next_rand_str();
538 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
539 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
541 if (getUpdateInfo(url, outFileVers, outFileSign))
543 log("", "Download okay, checking signature:");
544 if (checkSignature(outFileVers, outFileSign))
546 log("", "Signature okay, parsing info:", "");
547 success = parseVersionInfo(outFileVers, updateInfo);
549 else
551 log("", "Bad signature, take care!");
554 else
556 log("", "Download has failed!");
559 QFile::remove(outFileVers);
560 QFile::remove(outFileSign);
562 return success;
565 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
567 log("Downloading update info:", "");
568 if(getFile(QString("%1%2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
570 if (!m_cancelled)
572 log("", "Downloading signature:", "");
573 if (getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
575 return true;
579 return false;
582 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
584 QRegExp value("^(\\w+)=(.+)$");
585 QRegExp section("^\\[(.+)\\]$");
587 QDate updateInfoDate;
588 updateInfo->resetInfo();
590 QFile data(file);
591 if(!data.open(QIODevice::ReadOnly))
593 qWarning("Cannot open update info file for reading!");
594 return false;
597 bool inHdr = false;
598 bool inSec = false;
600 while(!data.atEnd())
602 QString line = QString::fromLatin1(data.readLine()).trimmed();
603 if(section.indexIn(line) >= 0)
605 log(QString("Sec: [%1]").arg(section.cap(1)));
606 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
607 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
608 continue;
610 if(inSec && (value.indexIn(line) >= 0))
612 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
613 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
615 bool ok = false;
616 const unsigned int temp = value.cap(2).toUInt(&ok);
617 if(ok) updateInfo->m_buildNo = temp;
619 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
621 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
622 if(temp.isValid()) updateInfo->m_buildDate = temp;
624 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
626 updateInfo->m_downloadSite = value.cap(2).trimmed();
628 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
630 updateInfo->m_downloadAddress = value.cap(2).trimmed();
632 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
634 updateInfo->m_downloadFilename = value.cap(2).trimmed();
636 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
638 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
640 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
642 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
645 if(inHdr && (value.indexIn(line) >= 0))
647 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
648 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
650 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
651 if(temp.isValid()) updateInfoDate = temp;
656 if(!updateInfoDate.isValid())
658 updateInfo->resetInfo();
659 log("WARNING: Version info timestamp is missing!");
660 return false;
663 const QDate currentDate = OS::current_date();
664 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
666 updateInfo->resetInfo();
667 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
668 return false;
670 else if(currentDate < updateInfoDate)
672 log("Version info is from the future, take care!");
673 qWarning("Version info is from the future, take care!");
676 if(!updateInfo->isComplete())
678 log("WARNING: Version info is incomplete!");
679 return false;
682 return true;
685 //----------------------------------------------------------
686 // EXTERNAL TOOLS
687 //----------------------------------------------------------
689 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir)
691 for (int i = 0; i < 2; i++)
693 if (getFile(url, (i > 0), outFile, maxRedir))
695 return true;
697 if (IS_CANCELLED)
699 break; /*cancelled*/
702 return false;
705 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir)
707 QFileInfo output(outFile);
708 output.setCaching(false);
710 if (output.exists())
712 QFile::remove(output.canonicalFilePath());
713 if (output.exists())
715 return false;
719 QProcess process;
720 init_process(process, output.absolutePath());
722 QStringList args;
723 if (forceIp4)
725 args << "-4";
728 args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts";
729 args << QString().sprintf("--max-redirect=%u", maxRedir) << QString().sprintf("--timeout=%u", DOWNLOAD_TIMEOUT / 1000);
730 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
731 args << "-O" << output.fileName() << url;
733 QEventLoop loop;
734 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
735 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
736 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
738 QTimer timer;
739 timer.setSingleShot(true);
740 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
742 const QRegExp httpResponseOK("200 OK$");
744 process.start(m_binaryWGet, args);
746 if (!process.waitForStarted())
748 return false;
751 timer.start(DOWNLOAD_TIMEOUT);
753 while (process.state() != QProcess::NotRunning)
755 loop.exec();
756 const bool bTimeOut = (!timer.isActive());
757 while (process.canReadLine())
759 const QString line = QString::fromLatin1(process.readLine()).simplified();
760 log(line);
762 if (bTimeOut || IS_CANCELLED)
764 qWarning("WGet process timed out <-- killing!");
765 process.kill();
766 process.waitForFinished();
767 log(bTimeOut ? "!!! TIMEOUT !!!": "!!! CANCELLED !!!");
768 return false;
772 timer.stop();
773 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
775 log(QString().sprintf("Exited with code %d", process.exitCode()));
776 return (process.exitCode() == 0) && output.exists() && output.isFile();
779 bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec)
781 log(QString("Connecting to host: %1").arg(hostname), "");
783 QProcess process;
784 init_process(process, temp_folder());
786 QStringList args;
787 args << "--retry" << QString::number(3) << hostname << QString::number(80);
789 QEventLoop loop;
790 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
791 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
792 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
794 QTimer timer;
795 timer.setSingleShot(true);
796 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
798 process.start(m_binaryMCat, args);
800 if (!process.waitForStarted())
802 return false;
805 timer.start(timeoutMsec);
807 while (process.state() != QProcess::NotRunning)
809 loop.exec();
810 const bool bTimeOut = (!timer.isActive());
811 while (process.canReadLine())
813 QString line = QString::fromLatin1(process.readLine()).simplified();
814 log(line);
816 if (bTimeOut || IS_CANCELLED)
818 qWarning("MCat process timed out <-- killing!");
819 process.kill();
820 process.waitForFinished();
821 log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!");
822 return false;
826 timer.stop();
827 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
829 if (process.exitCode() != 0)
831 log("Connection has failed!");
834 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
835 return (process.exitCode() == 0);
838 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
840 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
842 qWarning("CheckSignature: File and signature should be in same folder!");
843 return false;
846 QString keyRingPath(m_binaryKeys);
847 bool removeKeyring = false;
848 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
850 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
851 removeKeyring = true;
852 if (!QFile::copy(m_binaryKeys, keyRingPath))
854 qWarning("CheckSignature: Failed to copy the key-ring file!");
855 return false;
859 QProcess process;
860 init_process(process, QFileInfo(file).absolutePath());
862 QEventLoop loop;
863 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
864 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
865 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
867 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
869 if (!process.waitForStarted())
871 if (removeKeyring)
873 remove_file(keyRingPath);
875 return false;
878 while (process.state() == QProcess::Running)
880 loop.exec();
881 while (process.canReadLine())
883 log(QString::fromLatin1(process.readLine()).simplified());
887 if (removeKeyring)
889 remove_file(keyRingPath);
892 log(QString().sprintf("Exited with code %d", process.exitCode()));
893 return (process.exitCode() == 0);
896 ////////////////////////////////////////////////////////////
897 // SLOTS
898 ////////////////////////////////////////////////////////////
900 /*NONE*/
902 ////////////////////////////////////////////////////////////
903 // EVENTS
904 ////////////////////////////////////////////////////////////
906 /*NONE*/