Fixed compilation for "Release" and "Debug" configuration.
[MUtilities.git] / src / UpdateChecker.cpp
blob1f801993d47742f6b69735dcd5d3b4e0a67b68a1
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2016 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>
35 using namespace MUtils;
37 ///////////////////////////////////////////////////////////////////////////////
38 // CONSTANTS
39 ///////////////////////////////////////////////////////////////////////////////
41 static const char *header_id = "!Update";
43 static const char *mirror_url_postfix[] =
45 "update.ver",
46 "update_beta.ver",
47 NULL
50 static const char *update_mirrors[] =
52 "http://muldersoft.com/",
53 "http://mulder.bplaced.net/",
54 "http://mulder.6te.net/",
55 "http://mulder.webuda.com/",
56 "http://mulder.pe.hu/",
57 "http://muldersoft.square7.ch/",
58 "http://muldersoft.co.nf/",
59 "http://muldersoft.eu.pn/",
60 "http://muldersoft.lima-city.de/",
61 "http://www.muldersoft.keepfree.de/",
62 "http://lamexp.sourceforge.net/",
63 "http://lordmulder.github.io/LameXP/",
64 "http://lord_mulder.bitbucket.org/",
65 "http://www.tricksoft.de/",
66 "http://repo.or.cz/LameXP.git/blob_plain/gh-pages:/",
67 "http://gitlab.com/lamexp/lamexp/raw/gh-pages/",
68 NULL
71 static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!!
73 "www.163.com",
74 "www.7-zip.org",
75 "www.ac3filter.net",
76 "clbianco.altervista.org",
77 "status.aws.amazon.com",
78 "build.antergos.com",
79 "www.aol.com",
80 "www.apache.org",
81 "www.apple.com",
82 "www.adobe.com",
83 "archive.org",
84 "www.artlebedev.ru",
85 "web.audacityteam.org",
86 "status.automattic.com",
87 "www.avidemux.org",
88 "www.babylon.com",
89 "www.baidu.com",
90 "bandcamp.com",
91 "www.bbc.co.uk",
92 "www.berlios.de",
93 "www.bing.com",
94 "www.bingeandgrab.com",
95 "www.bucketheadpikes.com",
96 "www.buckethead-coop.com",
97 "www.buzzfeed.com",
98 "www.ccc.de",
99 "www.citizeninsomniac.com",
100 "www.cnet.com",
101 "cnzz.com",
102 "www.codeplex.com",
103 "www.codeproject.com",
104 "www.der-postillon.com",
105 "www.ebay.com",
106 "www.equation.com",
107 "www.farbrausch.de",
108 "fc2.com",
109 "fedoraproject.org",
110 "blog.fefe.de",
111 "www.ffmpeg.org",
112 "blog.flickr.net",
113 "free-codecs.com",
114 "git-scm.com",
115 "doc.gitlab.com",
116 "www.gmx.net",
117 "news.gnome.org",
118 "www.gnu.org",
119 "go.com",
120 "code.google.com",
121 "haali.su",
122 "www.heise.de",
123 "www.huffingtonpost.co.uk",
124 "www.iana.org",
125 "www.imdb.com",
126 "www.imgburn.com",
127 "imgur.com",
128 "www.jd.com",
129 "www.jiscdigitalmedia.ac.uk",
130 "kannmanumdieuhrzeitschonnbierchentrinken.de",
131 "mirrors.kernel.org",
132 "komisar.gin.by",
133 "lame.sourceforge.net",
134 "www.libav.org",
135 "blog.linkedin.com",
136 "www.linuxmint.com",
137 "www.livedoor.com",
138 "www.livejournal.com",
139 "longplayer.org",
140 "go.mail.ru",
141 "marknelson.us",
142 "www.mediafire.com",
143 "www.mod-technologies.com",
144 "ftp.mozilla.org",
145 "mplayerhq.hu",
146 "www.msn.com",
147 "wiki.multimedia.cx",
148 "www.nch.com.au",
149 "mirror.netcologne.de",
150 "oss.netfarm.it",
151 "blog.netflix.com",
152 "netrenderer.de",
153 "www.nytimes.com",
154 "www.opera.com",
155 "www.partha.com",
156 "pastebin.com",
157 "pastie.org",
158 "portableapps.com",
159 "www.portablefreeware.com",
160 "support.proboards.com",
161 "www.qq.com",
162 "www.qt.io",
163 "www.quakelive.com",
164 "rationalqm.us",
165 "www.seamonkey-project.org",
166 "selfhtml.org",
167 "www.sina.com.cn",
168 "www.sohu.com",
169 "help.sogou.com",
170 "sourceforge.net",
171 "www.spiegel.de",
172 "www.sputnikmusic.com",
173 "stackoverflow.com",
174 "www.t-online.de",
175 "www.tagesschau.de",
176 "tdm-gcc.tdragon.net",
177 "www.tdrsmusic.com",
178 "www.ubuntu.com",
179 "www.uol.com.br",
180 "www.videohelp.com",
181 "www.videolan.org",
182 "virtualdub.org",
183 "blog.virustotal.com",
184 "www.vkgoeswild.com",
185 "www.warr.org",
186 "www.weibo.com",
187 "status.wikimedia.org",
188 "www.winamp.com",
189 "www.winhoros.de",
190 "wpde.org",
191 "x265.org",
192 "xhmikosr.1f0.de",
193 "xiph.org",
194 "us.mail.yahoo.com",
195 "www.youtube.com",
196 "www.zedo.com",
197 "ffmpeg.zeranoe.com",
198 NULL
201 static const int MIN_CONNSCORE = 5;
202 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
203 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0"; /*use something innocuous*/
205 ////////////////////////////////////////////////////////////
206 // Helper Functions
207 ////////////////////////////////////////////////////////////
209 static int getMaxProgress(void)
211 int counter = MIN_CONNSCORE + 2;
212 for(int i = 0; update_mirrors[i]; i++) counter++;
213 return counter;
216 static QStringList buildRandomList(const char *const values[])
218 QStringList list;
219 for (int index = 0; values[index]; index++)
221 const int pos = next_rand32() % (index + 1);
222 list.insert(pos, QString::fromLatin1(values[index]));
224 return list;
227 ////////////////////////////////////////////////////////////
228 // Update Info Class
229 ////////////////////////////////////////////////////////////
231 UpdateCheckerInfo::UpdateCheckerInfo(void)
233 resetInfo();
236 void UpdateCheckerInfo::resetInfo(void)
238 m_buildNo = 0;
239 m_buildDate.setDate(1900, 1, 1);
240 m_downloadSite.clear();
241 m_downloadAddress.clear();
242 m_downloadFilename.clear();
243 m_downloadFilecode.clear();
244 m_downloadChecksum.clear();
247 bool UpdateCheckerInfo::isComplete(void)
249 if(this->m_buildNo < 1) return false;
250 if(this->m_buildDate.year() < 2010) return false;
251 if(this->m_downloadSite.isEmpty()) return false;
252 if(this->m_downloadAddress.isEmpty()) return false;
253 if(this->m_downloadFilename.isEmpty()) return false;
254 if(this->m_downloadFilecode.isEmpty()) return false;
255 if(this->m_downloadChecksum.isEmpty()) return false;
257 return true;
260 ////////////////////////////////////////////////////////////
261 // Constructor & Destructor
262 ////////////////////////////////////////////////////////////
264 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)
266 m_updateInfo(new UpdateCheckerInfo()),
267 m_binaryWGet(binWGet),
268 m_binaryNC(binNC),
269 m_binaryGnuPG(binGnuPG),
270 m_binaryKeys(binKeys),
271 m_applicationId(applicationId),
272 m_installedBuildNo(installedBuildNo),
273 m_betaUpdates(betaUpdates),
274 m_testMode(testMode),
275 m_maxProgress(getMaxProgress())
277 m_success = false;
278 m_status = UpdateStatus_NotStartedYet;
279 m_progress = 0;
281 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
283 MUTILS_THROW("Tools not initialized correctly!");
287 UpdateChecker::~UpdateChecker(void)
291 ////////////////////////////////////////////////////////////
292 // Protected functions
293 ////////////////////////////////////////////////////////////
295 void UpdateChecker::run(void)
297 qDebug("Update checker thread started!");
298 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
299 qDebug("Update checker thread completed.");
302 void UpdateChecker::checkForUpdates(void)
304 // ----- Initialization ----- //
306 m_success = false;
307 m_updateInfo->resetInfo();
308 seed_rand();
309 setProgress(0);
311 // ----- Test Internet Connection ----- //
313 log("Checking internet connection...", "");
314 setStatus(UpdateStatus_CheckingConnection);
316 const int networkStatus = OS::network_status();
317 if(networkStatus == OS::NETWORK_TYPE_NON)
319 log("Operating system reports that the computer is currently offline !!!");
320 setProgress(m_maxProgress);
321 setStatus(UpdateStatus_ErrorNoConnection);
322 return;
325 setProgress(1);
327 // ----- Test Known Hosts Connectivity ----- //
329 int connectionScore = 0, connectionRetry = MIN_CONNSCORE;
331 QStringList hostList = buildRandomList(known_hosts);
332 while((!hostList.isEmpty()) && (connectionScore < MIN_CONNSCORE) && (connectionRetry > 0))
334 connectionRetry--;
335 if(tryContactHost(hostList.takeFirst()))
337 connectionScore += 1;
338 connectionRetry = MIN_CONNSCORE;
340 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
341 msleep(25);
344 if(connectionScore < MIN_CONNSCORE)
346 log("", "Connectivity test has failed: Internet connection appears to be broken!");
347 setProgress(m_maxProgress);
348 setStatus(UpdateStatus_ErrorConnectionTestFailed);
349 return;
352 // ----- Fetch Update Info From Server ----- //
354 log("----", "", "Checking for updates online...");
355 setStatus(UpdateStatus_FetchingUpdates);
357 QStringList mirrorList = buildRandomList(update_mirrors);
358 while(!mirrorList.isEmpty())
360 QString currentMirror = mirrorList.takeFirst();
361 setProgress(m_progress + 1);
362 if(!m_success)
364 if(tryUpdateMirror(m_updateInfo.data(), currentMirror))
366 m_success = true;
369 else
371 msleep(25);
375 setProgress(m_maxProgress);
377 if(m_success)
379 if(m_updateInfo->m_buildNo > m_installedBuildNo)
381 setStatus(UpdateStatus_CompletedUpdateAvailable);
383 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
385 setStatus(UpdateStatus_CompletedNoUpdates);
387 else
389 setStatus(UpdateStatus_CompletedNewVersionOlder);
392 else
394 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
398 void UpdateChecker::testKnownHosts(void)
400 QStringList hostList;
401 for(int i = 0; known_hosts[i]; i++)
403 hostList << QString::fromLatin1(known_hosts[i]);
406 qDebug("\n[Known Hosts]");
407 log("Testing all known hosts...", "", "---");
409 int hostCount = hostList.count();
410 while(!hostList.isEmpty())
412 QString currentHost = hostList.takeFirst();
413 qDebug("Testing: %s", currentHost.toLatin1().constData());
414 log("", "Testing:", currentHost, "");
415 if (!tryContactHost(currentHost))
417 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
419 log("", "---");
423 ////////////////////////////////////////////////////////////
424 // PRIVATE FUNCTIONS
425 ////////////////////////////////////////////////////////////
427 void UpdateChecker::setStatus(const int status)
429 if(m_status != status)
431 m_status = status;
432 emit statusChanged(status);
436 void UpdateChecker::setProgress(const int progress)
438 if(m_progress != progress)
440 m_progress = progress;
441 emit progressChanged(progress);
445 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
447 if(!str1.isNull()) emit messageLogged(str1);
448 if(!str2.isNull()) emit messageLogged(str2);
449 if(!str3.isNull()) emit messageLogged(str3);
450 if(!str4.isNull()) emit messageLogged(str4);
453 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url)
455 bool success = false;
456 log("", "Trying mirror:", url);
458 const QString randPart = rand_str();
459 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
460 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
462 if(getUpdateInfo(url, outFileVers, outFileSign))
464 log("", "Download okay, checking signature:");
465 if(checkSignature(outFileVers, outFileSign))
467 log("", "Signature okay, parsing info:");
468 success = parseVersionInfo(outFileVers, updateInfo);
470 else
472 log("", "Bad signature, take care!");
475 else
477 log("", "Download has failed!");
480 QFile::remove(outFileVers);
481 QFile::remove(outFileSign);
483 return success;
486 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
488 log("", "Downloading update info:");
489 if(!getFile(QString("%1%2" ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
491 return false;
494 log("", "Downloading signature:");
495 if(!getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
497 return false;
500 return true;
503 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
505 QRegExp value("^(\\w+)=(.+)$");
506 QRegExp section("^\\[(.+)\\]$");
508 QDate updateInfoDate;
509 updateInfo->resetInfo();
511 QFile data(file);
512 if(!data.open(QIODevice::ReadOnly))
514 qWarning("Cannot open update info file for reading!");
515 return false;
518 bool inHdr = false;
519 bool inSec = false;
521 while(!data.atEnd())
523 QString line = QString::fromLatin1(data.readLine()).trimmed();
524 if(section.indexIn(line) >= 0)
526 log(QString("Sec: [%1]").arg(section.cap(1)));
527 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
528 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
529 continue;
531 if(inSec && (value.indexIn(line) >= 0))
533 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
534 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
536 bool ok = false;
537 const unsigned int temp = value.cap(2).toUInt(&ok);
538 if(ok) updateInfo->m_buildNo = temp;
540 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
542 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
543 if(temp.isValid()) updateInfo->m_buildDate = temp;
545 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
547 updateInfo->m_downloadSite = value.cap(2).trimmed();
549 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
551 updateInfo->m_downloadAddress = value.cap(2).trimmed();
553 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
555 updateInfo->m_downloadFilename = value.cap(2).trimmed();
557 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
559 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
561 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
563 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
566 if(inHdr && (value.indexIn(line) >= 0))
568 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
569 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
571 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
572 if(temp.isValid()) updateInfoDate = temp;
577 if(!updateInfoDate.isValid())
579 updateInfo->resetInfo();
580 log("WARNING: Version info timestamp is missing!");
581 return false;
584 const QDate currentDate = OS::current_date();
585 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
587 updateInfo->resetInfo();
588 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
589 return false;
591 else if(currentDate < updateInfoDate)
593 log("Version info is from the future, take care!");
594 qWarning("Version info is from the future, take care!");
597 if(!updateInfo->isComplete())
599 log("WARNING: Version info is incomplete!");
600 return false;
603 return true;
606 //----------------------------------------------------------
607 // EXTERNAL TOOLS
608 //----------------------------------------------------------
610 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir, bool *httpOk)
612 for (int i = 0; i < 2; i++)
614 if (getFile(url, (i > 0), outFile, maxRedir, httpOk))
616 return true;
619 return false;
622 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir, bool *httpOk)
624 QFileInfo output(outFile);
625 output.setCaching(false);
626 if (httpOk) *httpOk = false;
628 if (output.exists())
630 QFile::remove(output.canonicalFilePath());
631 if (output.exists())
633 return false;
637 QProcess process;
638 init_process(process, output.absolutePath());
640 QStringList args;
641 if (forceIp4)
643 args << "-4";
646 args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts";
647 args << QString().sprintf("--max-redirect=%u", maxRedir) << "--timeout=15";
648 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
649 args << "-O" << output.fileName() << url;
651 QEventLoop loop;
652 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
653 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
654 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
656 QTimer timer;
657 timer.setSingleShot(true);
658 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
660 const QRegExp httpResponseOK("200 OK$");
662 process.start(m_binaryWGet, args);
664 if (!process.waitForStarted())
666 return false;
669 timer.start(25000);
671 while (process.state() != QProcess::NotRunning)
673 loop.exec();
674 const bool bTimeOut = (!timer.isActive());
675 while (process.canReadLine())
677 QString line = QString::fromLatin1(process.readLine()).simplified();
678 if (line.contains(httpResponseOK))
680 line.append(" [OK]");
681 if (httpOk) *httpOk = true;
683 log(line);
685 if (bTimeOut)
687 qWarning("WGet process timed out <-- killing!");
688 process.kill();
689 process.waitForFinished();
690 log("!!! TIMEOUT !!!");
691 return false;
695 timer.stop();
696 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
698 log(QString().sprintf("Exited with code %d", process.exitCode()));
699 return (process.exitCode() == 0) && output.exists() && output.isFile();
702 bool UpdateChecker::tryContactHost(const QString &hostname)
704 log(QString("Connecting to host: %1").arg(hostname));
706 QProcess process;
707 init_process(process, temp_folder());
709 QStringList args;
710 args << "-z" << hostname << QString::number(80);
712 QEventLoop loop;
713 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
714 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
715 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
717 QTimer timer;
718 timer.setSingleShot(true);
719 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
721 process.start(m_binaryNC, args);
723 if (!process.waitForStarted())
725 return false;
728 timer.start(10000);
730 while (process.state() != QProcess::NotRunning)
732 loop.exec();
733 const bool bTimeOut = (!timer.isActive());
734 while (process.canReadLine())
736 QString line = QString::fromLatin1(process.readLine()).simplified();
737 log(line);
739 if (bTimeOut)
741 qWarning("NC process timed out <-- killing!");
742 process.kill();
743 process.waitForFinished();
744 log("!!! TIMEOUT !!!");
745 return false;
749 timer.stop();
750 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
752 if (process.exitCode() != 0)
754 log("Connection has failed!");
757 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
758 return (process.exitCode() == 0);
761 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
763 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
765 qWarning("CheckSignature: File and signature should be in same folder!");
766 return false;
769 QString keyRingPath(m_binaryKeys);
770 bool removeKeyring = false;
771 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
773 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
774 removeKeyring = true;
775 if (!QFile::copy(m_binaryKeys, keyRingPath))
777 qWarning("CheckSignature: Failed to copy the key-ring file!");
778 return false;
782 QProcess process;
783 init_process(process, QFileInfo(file).absolutePath());
785 QEventLoop loop;
786 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
787 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
788 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
790 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
792 if (!process.waitForStarted())
794 if (removeKeyring)
796 remove_file(keyRingPath);
798 return false;
801 while (process.state() == QProcess::Running)
803 loop.exec();
804 while (process.canReadLine())
806 log(QString::fromLatin1(process.readLine()).simplified());
810 if (removeKeyring)
812 remove_file(keyRingPath);
815 log(QString().sprintf("Exited with code %d", process.exitCode()));
816 return (process.exitCode() == 0);
819 ////////////////////////////////////////////////////////////
820 // SLOTS
821 ////////////////////////////////////////////////////////////
823 /*NONE*/
825 ////////////////////////////////////////////////////////////
826 // EVENTS
827 ////////////////////////////////////////////////////////////
829 /*NONE*/