Updated VS2013 project file.
[MUtilities.git] / src / UpdateChecker.cpp
blob467ab178a432697c2c76930ef1db03f2b1fbe238
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>
35 #include <QSet>
37 using namespace MUtils;
39 ///////////////////////////////////////////////////////////////////////////////
40 // CONSTANTS
41 ///////////////////////////////////////////////////////////////////////////////
43 static const char *header_id = "!Update";
45 static const char *mirror_url_postfix[] =
47 "update.ver",
48 "update_beta.ver",
49 NULL
52 static const char *update_mirrors[] =
54 "http://muldersoft.com/",
55 "http://mulder.bplaced.net/",
56 "http://mulder.6te.net/",
57 "http://mulder.000webhostapp.com/", //"http://mulder.webuda.com/",
58 "http://mulder.pe.hu/",
59 "http://muldersoft.square7.ch/",
60 "http://muldersoft.co.nf/",
61 "http://muldersoft.eu.pn/",
62 "http://muldersoft.lima-city.de/",
63 "http://www.muldersoft.keepfree.de/",
64 "http://lamexp.sourceforge.net/",
65 "http://lordmulder.github.io/LameXP/",
66 "http://muldersoft.bitbucket.io/", //http://lord_mulder.bitbucket.org/
67 "http://www.tricksoft.de/",
68 "http://repo.or.cz/LameXP.git/blob_plain/gh-pages:/",
69 "http://gitlab.com/lamexp/lamexp/raw/gh-pages/",
70 NULL
73 static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!!
75 "www.163.com",
76 "www.7-zip.org",
77 "www.ac3filter.net",
78 "clbianco.altervista.org",
79 "status.aws.amazon.com",
80 "build.antergos.com",
81 "www.aol.com",
82 "www.apache.org",
83 "www.apple.com",
84 "www.adobe.com",
85 "archive.org",
86 "www.artlebedev.ru",
87 "web.audacityteam.org",
88 "status.automattic.com",
89 "www.avidemux.org",
90 "www.babylon.com",
91 "www.baidu.com",
92 "bandcamp.com",
93 "www.bbc.co.uk",
94 "www.berlios.de",
95 "www.bing.com",
96 "www.bingeandgrab.com",
97 "www.bucketheadpikes.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.ethz.ch",
112 "www.farbrausch.de",
113 "fc2.com",
114 "fedoraproject.org",
115 "blog.fefe.de",
116 "www.ffmpeg.org",
117 "blog.flickr.net",
118 "www.fraunhofer.de",
119 "free-codecs.com",
120 "git-scm.com",
121 "doc.gitlab.com",
122 "www.gmx.net",
123 "news.gnome.org",
124 "www.gnu.org",
125 "go.com",
126 "code.google.com",
127 "haali.su",
128 "www.harvard.edu",
129 "www.heise.de",
130 "www.helmholtz.de",
131 "www.huffingtonpost.co.uk",
132 "www.hu-berlin.de",
133 "www.iana.org",
134 "www.imdb.com",
135 "www.imgburn.com",
136 "imgur.com",
137 "www.iuj.ac.jp",
138 "www.jd.com",
139 "www.jiscdigitalmedia.ac.uk",
140 "kannmanumdieuhrzeitschonnbierchentrinken.de",
141 "mirrors.kernel.org",
142 "komisar.gin.by",
143 "lame.sourceforge.net",
144 "www.libav.org",
145 "blog.linkedin.com",
146 "www.linuxmint.com",
147 "www.livedoor.com",
148 "www.livejournal.com",
149 "longplayer.org",
150 "go.mail.ru",
151 "marknelson.us",
152 "www.mediafire.com",
153 "web.mit.edu",
154 "www.mod-technologies.com",
155 "ftp.mozilla.org",
156 "www.mpg.de",
157 "mplayerhq.hu",
158 "www.msn.com",
159 "wiki.multimedia.cx",
160 "www.nch.com.au",
161 "neocities.org",
162 "mirror.netcologne.de",
163 "oss.netfarm.it",
164 "blog.netflix.com",
165 "netrenderer.de",
166 "www.nytimes.com",
167 "www.opera.com",
168 "www.oxford.gov.uk",
169 "www.ox-fanzine.de",
170 "www.partha.com",
171 "pastebin.com",
172 "pastie.org",
173 "portableapps.com",
174 "www.portablefreeware.com",
175 "support.proboards.com",
176 "www.qq.com",
177 "www.qt.io",
178 "www.quakelive.com",
179 "rationalqm.us",
180 "www.reddit.com",
181 "www.rwth-aachen.de",
182 "www.seamonkey-project.org",
183 "selfhtml.org",
184 "www.sina.com.cn",
185 "www.sohu.com",
186 "help.sogou.com",
187 "sourceforge.net",
188 "www.spiegel.de",
189 "www.sputnikmusic.com",
190 "stackoverflow.com",
191 "www.stanford.edu",
192 "www.t-online.de",
193 "www.tagesschau.de",
194 "tdm-gcc.tdragon.net",
195 "www.tdrsmusic.com",
196 "tu-dresden.de",
197 "www.ubuntu.com",
198 "portal.uned.es",
199 "www.unibuc.ro",
200 "www.uniroma1.it",
201 "www.univ-paris1.fr",
202 "www.univer.kharkov.ua",
203 "www.univie.ac.at",
204 "www.uol.com.br",
205 "www.uva.nl",
206 "www.uw.edu.pl",
207 "www.videohelp.com",
208 "www.videolan.org",
209 "virtualdub.org",
210 "blog.virustotal.com",
211 "www.vkgoeswild.com",
212 "www.warr.org",
213 "www.weibo.com",
214 "status.wikimedia.org",
215 "www.winamp.com",
216 "www.winhoros.de",
217 "wpde.org",
218 "x265.org",
219 "xhmikosr.1f0.de",
220 "xiph.org",
221 "us.mail.yahoo.com",
222 "www.youtube.com",
223 "www.zedo.com",
224 "ffmpeg.zeranoe.com",
225 NULL
228 static const int MIN_CONNSCORE = 5;
229 static const int QUICK_MIRRORS = 3;
230 static const int MAX_CONN_TIMEOUT = 8000;
231 static const int DOWNLOAD_TIMEOUT = 30000;
233 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
234 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0"; /*use something innocuous*/
236 #define CHECK_CANCELLED() do \
238 if(MUTILS_BOOLIFY(m_cancelled)) \
240 m_success.fetchAndStoreOrdered(0); \
241 log("", "Update check has been cancelled by user!"); \
242 setProgress(m_maxProgress); \
243 setStatus(UpdateStatus_CancelledByUser); \
244 return; \
247 while(0)
249 ////////////////////////////////////////////////////////////
250 // Helper Functions
251 ////////////////////////////////////////////////////////////
253 static int getMaxProgress(void)
255 int counter = 0;
256 while (update_mirrors[counter])
258 counter++;
260 counter += MIN_CONNSCORE + QUICK_MIRRORS + 2;
261 return counter; ;
264 static QStringList buildRandomList(const char *const values[])
266 QStringList list;
267 for (int index = 0; values[index]; index++)
269 const int pos = next_rand_u32() % (index + 1);
270 list.insert(pos, QString::fromLatin1(values[index]));
272 return list;
275 ////////////////////////////////////////////////////////////
276 // Update Info Class
277 ////////////////////////////////////////////////////////////
279 UpdateCheckerInfo::UpdateCheckerInfo(void)
281 resetInfo();
284 void UpdateCheckerInfo::resetInfo(void)
286 m_buildNo = 0;
287 m_buildDate.setDate(1900, 1, 1);
288 m_downloadSite.clear();
289 m_downloadAddress.clear();
290 m_downloadFilename.clear();
291 m_downloadFilecode.clear();
292 m_downloadChecksum.clear();
295 bool UpdateCheckerInfo::isComplete(void)
297 if(this->m_buildNo < 1) return false;
298 if(this->m_buildDate.year() < 2010) return false;
299 if(this->m_downloadSite.isEmpty()) return false;
300 if(this->m_downloadAddress.isEmpty()) return false;
301 if(this->m_downloadFilename.isEmpty()) return false;
302 if(this->m_downloadFilecode.isEmpty()) return false;
303 if(this->m_downloadChecksum.isEmpty()) return false;
305 return true;
308 ////////////////////////////////////////////////////////////
309 // Constructor & Destructor
310 ////////////////////////////////////////////////////////////
312 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)
314 m_updateInfo(new UpdateCheckerInfo()),
315 m_binaryWGet(binWGet),
316 m_binaryMCat(binMCat),
317 m_binaryGnuPG(binGnuPG),
318 m_binaryKeys(binKeys),
319 m_applicationId(applicationId),
320 m_installedBuildNo(installedBuildNo),
321 m_betaUpdates(betaUpdates),
322 m_testMode(testMode),
323 m_maxProgress(getMaxProgress())
325 m_status = UpdateStatus_NotStartedYet;
326 m_progress = 0;
328 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
330 MUTILS_THROW("Tools not initialized correctly!");
334 UpdateChecker::~UpdateChecker(void)
338 ////////////////////////////////////////////////////////////
339 // Public slots
340 ////////////////////////////////////////////////////////////
342 void UpdateChecker::start(Priority priority)
344 m_success.fetchAndStoreOrdered(0);
345 m_cancelled.fetchAndStoreOrdered(0);
346 QThread::start(priority);
349 ////////////////////////////////////////////////////////////
350 // Protected functions
351 ////////////////////////////////////////////////////////////
353 void UpdateChecker::run(void)
355 qDebug("Update checker thread started!");
356 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
357 qDebug("Update checker thread completed.");
360 void UpdateChecker::checkForUpdates(void)
362 // ----- Initialization ----- //
364 m_updateInfo->resetInfo();
365 setProgress(0);
367 // ----- Test Internet Connection ----- //
369 log("Checking internet connection...", "");
370 setStatus(UpdateStatus_CheckingConnection);
372 const int networkStatus = OS::network_status();
373 if(networkStatus == OS::NETWORK_TYPE_NON)
375 log("Operating system reports that the computer is currently offline !!!");
376 setProgress(m_maxProgress);
377 setStatus(UpdateStatus_ErrorNoConnection);
378 return;
381 setProgress(1);
383 // ----- Test Known Hosts Connectivity ----- //
385 int connectionScore = 0;
386 QStringList mirrorList = buildRandomList(known_hosts);
388 for(int connectionTimout = 250; connectionTimout <= MAX_CONN_TIMEOUT; connectionTimout *= 2)
390 QElapsedTimer elapsedTimer;
391 elapsedTimer.start();
392 const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout;
393 while (!elapsedTimer.hasExpired(globalTimout))
395 const QString hostName = mirrorList.takeFirst();
396 if (tryContactHost(hostName, connectionTimout))
398 connectionScore += 1;
399 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
400 elapsedTimer.restart();
401 if (connectionScore >= MIN_CONNSCORE)
403 goto endLoop; /*success*/
406 else
408 mirrorList.append(hostName); /*re-schedule*/
410 CHECK_CANCELLED();
411 msleep(1);
415 endLoop:
416 if(connectionScore < MIN_CONNSCORE)
418 log("", "Connectivity test has failed: Internet connection appears to be broken!");
419 setProgress(m_maxProgress);
420 setStatus(UpdateStatus_ErrorConnectionTestFailed);
421 return;
424 // ----- Fetch Update Info From Server ----- //
426 log("----", "", "Checking for updates online...");
427 setStatus(UpdateStatus_FetchingUpdates);
429 int mirrorCount = 0;
430 mirrorList = buildRandomList(update_mirrors);
432 while(!mirrorList.isEmpty())
434 setProgress(m_progress + 1);
435 const QString currentMirror = mirrorList.takeFirst();
436 const bool isQuick = (mirrorCount++ < QUICK_MIRRORS);
437 if(tryUpdateMirror(m_updateInfo.data(), currentMirror, isQuick))
439 m_success.ref(); /*success*/
440 break;
442 if (isQuick)
444 mirrorList.append(currentMirror); /*re-schedule*/
446 CHECK_CANCELLED();
447 msleep(1);
450 while (m_progress < m_maxProgress)
452 msleep(16);
453 setProgress(m_progress + 1);
454 CHECK_CANCELLED();
457 // ----- Generate final result ----- //
459 if(MUTILS_BOOLIFY(m_success))
461 if(m_updateInfo->m_buildNo > m_installedBuildNo)
463 setStatus(UpdateStatus_CompletedUpdateAvailable);
465 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
467 setStatus(UpdateStatus_CompletedNoUpdates);
469 else
471 setStatus(UpdateStatus_CompletedNewVersionOlder);
474 else
476 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
480 void UpdateChecker::testKnownHosts(void)
482 QStringList hostList;
483 for(int i = 0; known_hosts[i]; i++)
485 hostList << QString::fromLatin1(known_hosts[i]);
488 qDebug("\n[Known Hosts]");
489 log("Testing all known hosts...", "", "---");
491 QSet<quint32> ipAddrSet;
492 quint32 ipAddr;
493 while(!hostList.isEmpty())
495 const QString currentHost = hostList.takeFirst();
496 qDebug("Testing: %s", currentHost.toLatin1().constData());
497 log("", "Testing:", currentHost, "");
498 if (tryContactHost(currentHost, DOWNLOAD_TIMEOUT, &ipAddr))
500 if (ipAddrSet.contains(ipAddr))
502 qWarning("Duplicate IP-address 0x%08X was encountered!", ipAddr);
504 else
506 ipAddrSet << ipAddr; /*not encountered yet*/
509 else
511 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
513 log("", "---");
517 ////////////////////////////////////////////////////////////
518 // PRIVATE FUNCTIONS
519 ////////////////////////////////////////////////////////////
521 void UpdateChecker::setStatus(const int status)
523 if(m_status != status)
525 m_status = status;
526 emit statusChanged(status);
530 void UpdateChecker::setProgress(const int progress)
532 if(m_progress != progress)
534 m_progress = progress;
535 emit progressChanged(progress);
539 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
541 if(!str1.isNull()) emit messageLogged(str1);
542 if(!str2.isNull()) emit messageLogged(str2);
543 if(!str3.isNull()) emit messageLogged(str3);
544 if(!str4.isNull()) emit messageLogged(str4);
547 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url, const bool &quick)
549 bool success = false;
550 log("", "Trying mirror:", url, "");
552 if (!tryContactHost(QUrl(url).host(), quick ? (MAX_CONN_TIMEOUT / 10) : MAX_CONN_TIMEOUT))
554 log("", quick ? "Mirror is too slow!" :"Mirror is unreachable!");
555 return false;
558 const QString randPart = next_rand_str();
559 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
560 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
562 if (getUpdateInfo(url, outFileVers, outFileSign))
564 log("", "Download okay, checking signature:");
565 if (checkSignature(outFileVers, outFileSign))
567 log("", "Signature okay, parsing info:", "");
568 success = parseVersionInfo(outFileVers, updateInfo);
570 else
572 log("", "Bad signature, take care!");
575 else
577 log("", "Download has failed!");
580 QFile::remove(outFileVers);
581 QFile::remove(outFileSign);
583 return success;
586 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
588 log("Downloading update info:", "");
589 if(getFile(QString("%1%2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
591 if (!m_cancelled)
593 log("", "Downloading signature:", "");
594 if (getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
596 return true;
600 return false;
603 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
605 QRegExp value("^(\\w+)=(.+)$");
606 QRegExp section("^\\[(.+)\\]$");
608 QDate updateInfoDate;
609 updateInfo->resetInfo();
611 QFile data(file);
612 if(!data.open(QIODevice::ReadOnly))
614 qWarning("Cannot open update info file for reading!");
615 return false;
618 bool inHdr = false;
619 bool inSec = false;
621 while(!data.atEnd())
623 QString line = QString::fromLatin1(data.readLine()).trimmed();
624 if(section.indexIn(line) >= 0)
626 log(QString("Sec: [%1]").arg(section.cap(1)));
627 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
628 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
629 continue;
631 if(inSec && (value.indexIn(line) >= 0))
633 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
634 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
636 bool ok = false;
637 const unsigned int temp = value.cap(2).toUInt(&ok);
638 if(ok) updateInfo->m_buildNo = temp;
640 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
642 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
643 if(temp.isValid()) updateInfo->m_buildDate = temp;
645 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
647 updateInfo->m_downloadSite = value.cap(2).trimmed();
649 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
651 updateInfo->m_downloadAddress = value.cap(2).trimmed();
653 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
655 updateInfo->m_downloadFilename = value.cap(2).trimmed();
657 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
659 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
661 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
663 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
666 if(inHdr && (value.indexIn(line) >= 0))
668 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
669 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
671 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
672 if(temp.isValid()) updateInfoDate = temp;
677 if(!updateInfoDate.isValid())
679 updateInfo->resetInfo();
680 log("WARNING: Version info timestamp is missing!");
681 return false;
684 const QDate currentDate = OS::current_date();
685 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
687 updateInfo->resetInfo();
688 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
689 return false;
691 else if(currentDate < updateInfoDate)
693 log("Version info is from the future, take care!");
694 qWarning("Version info is from the future, take care!");
697 if(!updateInfo->isComplete())
699 log("WARNING: Version info is incomplete!");
700 return false;
703 return true;
706 //----------------------------------------------------------
707 // EXTERNAL TOOLS
708 //----------------------------------------------------------
710 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir)
712 for (int i = 0; i < 2; i++)
714 if (getFile(url, (i > 0), outFile, maxRedir))
716 return true;
718 if (MUTILS_BOOLIFY(m_cancelled))
720 break; /*cancelled*/
723 return false;
726 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir)
728 QFileInfo output(outFile);
729 output.setCaching(false);
731 if (output.exists())
733 QFile::remove(output.canonicalFilePath());
734 if (output.exists())
736 return false;
740 QProcess process;
741 init_process(process, output.absolutePath());
743 QStringList args;
744 if (forceIp4)
746 args << "-4";
749 args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts";
750 args << QString().sprintf("--max-redirect=%u", maxRedir) << QString().sprintf("--timeout=%u", DOWNLOAD_TIMEOUT / 1000);
751 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
752 args << "-O" << output.fileName() << url;
754 QEventLoop loop;
755 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
756 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
757 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
759 QTimer timer;
760 timer.setSingleShot(true);
761 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
763 const QRegExp httpResponseOK("200 OK$");
765 process.start(m_binaryWGet, args);
767 if (!process.waitForStarted())
769 return false;
772 timer.start(DOWNLOAD_TIMEOUT);
774 while (process.state() != QProcess::NotRunning)
776 loop.exec();
777 const bool bTimeOut = (!timer.isActive());
778 while (process.canReadLine())
780 const QString line = QString::fromLatin1(process.readLine()).simplified();
781 log(line);
783 if (bTimeOut || MUTILS_BOOLIFY(m_cancelled))
785 qWarning("WGet process timed out <-- killing!");
786 process.kill();
787 process.waitForFinished();
788 log(bTimeOut ? "!!! TIMEOUT !!!": "!!! CANCELLED !!!");
789 return false;
793 timer.stop();
794 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
796 log(QString().sprintf("Exited with code %d", process.exitCode()));
797 return (process.exitCode() == 0) && output.exists() && output.isFile();
800 bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec, quint32 *const ipAddrOut)
802 log(QString("Connecting to host: %1").arg(hostname), "");
804 QProcess process;
805 init_process(process, temp_folder());
807 QStringList args;
808 args << "--retry" << QString::number(3) << hostname << QString::number(80);
810 QEventLoop loop;
811 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
812 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
813 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
815 QTimer timer;
816 timer.setSingleShot(true);
817 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
819 QScopedPointer<QRegExp> ipAddr;
820 if (ipAddrOut)
822 *ipAddrOut = 0;
823 ipAddr.reset(new QRegExp("Connecting\\s+to\\s+(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+):(\\d+)", Qt::CaseInsensitive));
826 process.start(m_binaryMCat, args);
828 if (!process.waitForStarted())
830 return false;
833 timer.start(timeoutMsec);
835 while (process.state() != QProcess::NotRunning)
837 loop.exec();
838 const bool bTimeOut = (!timer.isActive());
839 while (process.canReadLine())
841 const QString line = QString::fromLatin1(process.readLine()).simplified();
842 if (!ipAddr.isNull())
844 if (ipAddr->indexIn(line) >= 0)
846 quint32 values[4];
847 if (MUtils::regexp_parse_uint32((*ipAddr), values, 4))
849 *ipAddrOut |= ((values[0] & 0xFF) << 0x18);
850 *ipAddrOut |= ((values[1] & 0xFF) << 0x10);
851 *ipAddrOut |= ((values[2] & 0xFF) << 0x08);
852 *ipAddrOut |= ((values[3] & 0xFF) << 0x00);
856 log(line);
858 if (bTimeOut || MUTILS_BOOLIFY(m_cancelled))
860 qWarning("MCat process timed out <-- killing!");
861 process.kill();
862 process.waitForFinished();
863 log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!");
864 return false;
868 timer.stop();
869 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
871 if (process.exitCode() != 0)
873 log("Connection has failed!");
876 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
877 return (process.exitCode() == 0);
880 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
882 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
884 qWarning("CheckSignature: File and signature should be in same folder!");
885 return false;
888 QString keyRingPath(m_binaryKeys);
889 bool removeKeyring = false;
890 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
892 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
893 removeKeyring = true;
894 if (!QFile::copy(m_binaryKeys, keyRingPath))
896 qWarning("CheckSignature: Failed to copy the key-ring file!");
897 return false;
901 QProcess process;
902 init_process(process, QFileInfo(file).absolutePath());
904 QEventLoop loop;
905 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
906 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
907 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
909 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
911 if (!process.waitForStarted())
913 if (removeKeyring)
915 remove_file(keyRingPath);
917 return false;
920 while (process.state() == QProcess::Running)
922 loop.exec();
923 while (process.canReadLine())
925 log(QString::fromLatin1(process.readLine()).simplified());
929 if (removeKeyring)
931 remove_file(keyRingPath);
934 log(QString().sprintf("Exited with code %d", process.exitCode()));
935 return (process.exitCode() == 0);
938 ////////////////////////////////////////////////////////////
939 // SLOTS
940 ////////////////////////////////////////////////////////////
942 /*NONE*/
944 ////////////////////////////////////////////////////////////
945 // EVENTS
946 ////////////////////////////////////////////////////////////
948 /*NONE*/