Improved get_real_os_version() function. Now also detects the real build number.
[MUtilities.git] / src / UpdateChecker.cpp
blobfb64ef292c0859892d9ecef7b25ac5b1b7d88591
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/", //"http://mulder.cwsurf.de/",
54 "http://mulder.6te.net/",
55 "http://mulder.webuda.com/", //"http://mulder.byethost13.com/",
56 "http://mulder.pe.hu/", //"http://muldersoft.kilu.de/",
57 "http://muldersoft.square7.ch/", //"http://muldersoft.zxq.net/",
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 NULL
69 static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!!
71 "www.163.com",
72 "www.7-zip.org",
73 "www.ac3filter.net",
74 "clbianco.altervista.org",
75 "status.aws.amazon.com",
76 "build.antergos.com",
77 "www.aol.com",
78 "www.apache.org",
79 "www.apple.com",
80 "www.adobe.com",
81 "archive.org",
82 "www.artlebedev.ru",
83 "web.audacityteam.org",
84 "status.automattic.com",
85 "www.avidemux.org",
86 "www.babylon.com",
87 "www.baidu.com",
88 "bandcamp.com",
89 "www.bbc.co.uk",
90 "www.berlios.de",
91 "www.bing.com",
92 "www.bingeandgrab.com",
93 "www.bucketheadpikes.com",
94 "www.buckethead-coop.com",
95 "www.buzzfeed.com",
96 "www.ccc.de",
97 "www.citizeninsomniac.com",
98 "www.cnet.com",
99 "cnzz.com",
100 "www.codeplex.com",
101 "www.codeproject.com",
102 "www.der-postillon.com",
103 "www.ebay.com",
104 "www.equation.com",
105 "www.farbrausch.de",
106 "fc2.com",
107 "fedoraproject.org",
108 "blog.fefe.de",
109 "www.ffmpeg.org",
110 "blog.flickr.net",
111 "free-codecs.com",
112 "git-scm.com",
113 "doc.gitlab.com",
114 "www.gmx.net",
115 "news.gnome.org",
116 "www.gnu.org",
117 "go.com",
118 "code.google.com",
119 "haali.su",
120 "www.heise.de",
121 "www.huffingtonpost.co.uk",
122 "www.iana.org",
123 "www.imdb.com",
124 "www.imgburn.com",
125 "imgur.com",
126 "www.jd.com",
127 "www.jiscdigitalmedia.ac.uk",
128 "kannmanumdieuhrzeitschonnbierchentrinken.de",
129 "mirrors.kernel.org",
130 "komisar.gin.by",
131 "lame.sourceforge.net",
132 "www.libav.org",
133 "blog.linkedin.com",
134 "www.linuxmint.com",
135 "www.livedoor.com",
136 "www.livejournal.com",
137 "longplayer.org",
138 "go.mail.ru",
139 "marknelson.us",
140 "www.mediafire.com",
141 "www.mod-technologies.com",
142 "ftp.mozilla.org",
143 "mplayerhq.hu",
144 "www.msn.com",
145 "wiki.multimedia.cx",
146 "www.nch.com.au",
147 "mirror.netcologne.de",
148 "oss.netfarm.it",
149 "blog.netflix.com",
150 "netrenderer.de",
151 "www.nytimes.com",
152 "www.opera.com",
153 "www.partha.com",
154 "pastebin.com",
155 "pastie.org",
156 "portableapps.com",
157 "www.portablefreeware.com",
158 "support.proboards.com",
159 "www.qq.com",
160 "www.qt.io",
161 "www.quakelive.com",
162 "rationalqm.us",
163 "www.seamonkey-project.org",
164 "selfhtml.org",
165 "www.sina.com.cn",
166 "www.sohu.com",
167 "help.sogou.com",
168 "sourceforge.net",
169 "www.spiegel.de",
170 "www.sputnikmusic.com",
171 "stackoverflow.com",
172 "www.t-online.de",
173 "www.tagesschau.de",
174 "tdm-gcc.tdragon.net",
175 "www.tdrsmusic.com",
176 "www.ubuntu.com",
177 "www.uol.com.br",
178 "www.videohelp.com",
179 "www.videolan.org",
180 "virtualdub.org",
181 "blog.virustotal.com",
182 "www.vkgoeswild.com",
183 "www.warr.org",
184 "www.weibo.com",
185 "status.wikimedia.org",
186 "www.winamp.com",
187 "www.winhoros.de",
188 "wpde.org",
189 "x265.org",
190 "xhmikosr.1f0.de",
191 "xiph.org",
192 "us.mail.yahoo.com",
193 "www.youtube.com",
194 "www.zedo.com",
195 "ffmpeg.zeranoe.com",
196 NULL
199 static const int MIN_CONNSCORE = 5;
200 static const int VERSION_INFO_EXPIRES_MONTHS = 6;
201 static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111106 IceCat/7.0.1";
203 ////////////////////////////////////////////////////////////
204 // Helper Functions
205 ////////////////////////////////////////////////////////////
207 static int getMaxProgress(void)
209 int counter = MIN_CONNSCORE + 2;
210 for(int i = 0; update_mirrors[i]; i++) counter++;
211 return counter;
214 static QStringList buildRandomList(const char *const values[])
216 QStringList list;
217 for (int index = 0; values[index]; index++)
219 const int pos = next_rand32() % (index + 1);
220 list.insert(pos, QString::fromLatin1(values[index]));
222 return list;
225 ////////////////////////////////////////////////////////////
226 // Update Info Class
227 ////////////////////////////////////////////////////////////
229 UpdateCheckerInfo::UpdateCheckerInfo(void)
231 resetInfo();
234 void UpdateCheckerInfo::resetInfo(void)
236 m_buildNo = 0;
237 m_buildDate.setDate(1900, 1, 1);
238 m_downloadSite.clear();
239 m_downloadAddress.clear();
240 m_downloadFilename.clear();
241 m_downloadFilecode.clear();
242 m_downloadChecksum.clear();
245 bool UpdateCheckerInfo::isComplete(void)
247 if(this->m_buildNo < 1) return false;
248 if(this->m_buildDate.year() < 2010) return false;
249 if(this->m_downloadSite.isEmpty()) return false;
250 if(this->m_downloadAddress.isEmpty()) return false;
251 if(this->m_downloadFilename.isEmpty()) return false;
252 if(this->m_downloadFilecode.isEmpty()) return false;
253 if(this->m_downloadChecksum.isEmpty()) return false;
255 return true;
258 ////////////////////////////////////////////////////////////
259 // Constructor & Destructor
260 ////////////////////////////////////////////////////////////
262 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)
264 m_updateInfo(new UpdateCheckerInfo()),
265 m_binaryWGet(binWGet),
266 m_binaryNC(binNC),
267 m_binaryGnuPG(binGnuPG),
268 m_binaryKeys(binKeys),
269 m_applicationId(applicationId),
270 m_installedBuildNo(installedBuildNo),
271 m_betaUpdates(betaUpdates),
272 m_testMode(testMode),
273 m_maxProgress(getMaxProgress())
275 m_success = false;
276 m_status = UpdateStatus_NotStartedYet;
277 m_progress = 0;
279 if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty())
281 MUTILS_THROW("Tools not initialized correctly!");
285 UpdateChecker::~UpdateChecker(void)
289 ////////////////////////////////////////////////////////////
290 // Protected functions
291 ////////////////////////////////////////////////////////////
293 void UpdateChecker::run(void)
295 qDebug("Update checker thread started!");
296 MUTILS_EXCEPTION_HANDLER(m_testMode ? testKnownHosts() : checkForUpdates());
297 qDebug("Update checker thread completed.");
300 void UpdateChecker::checkForUpdates(void)
302 // ----- Initialization ----- //
304 m_success = false;
305 m_updateInfo->resetInfo();
306 seed_rand();
307 setProgress(0);
309 // ----- Test Internet Connection ----- //
311 log("Checking internet connection...", "");
312 setStatus(UpdateStatus_CheckingConnection);
314 const int networkStatus = OS::network_status();
315 if(networkStatus == OS::NETWORK_TYPE_NON)
317 log("Operating system reports that the computer is currently offline !!!");
318 setProgress(m_maxProgress);
319 setStatus(UpdateStatus_ErrorNoConnection);
320 return;
323 setProgress(1);
325 // ----- Test Known Hosts Connectivity ----- //
327 int connectionScore = 0, connectionRetry = MIN_CONNSCORE;
329 QStringList hostList = buildRandomList(known_hosts);
330 while((!hostList.isEmpty()) && (connectionScore < MIN_CONNSCORE) && (connectionRetry > 0))
332 connectionRetry--;
333 if(tryContactHost(hostList.takeFirst()))
335 connectionScore += 1;
336 connectionRetry = MIN_CONNSCORE;
338 setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1));
339 msleep(25);
342 if(connectionScore < MIN_CONNSCORE)
344 log("", "Connectivity test has failed: Internet connection appears to be broken!");
345 setProgress(m_maxProgress);
346 setStatus(UpdateStatus_ErrorConnectionTestFailed);
347 return;
350 // ----- Fetch Update Info From Server ----- //
352 log("----", "", "Checking for updates online...");
353 setStatus(UpdateStatus_FetchingUpdates);
355 QStringList mirrorList = buildRandomList(update_mirrors);
356 while(!mirrorList.isEmpty())
358 QString currentMirror = mirrorList.takeFirst();
359 setProgress(m_progress + 1);
360 if(!m_success)
362 if(tryUpdateMirror(m_updateInfo.data(), currentMirror))
364 m_success = true;
367 else
369 msleep(25);
373 setProgress(m_maxProgress);
375 if(m_success)
377 if(m_updateInfo->m_buildNo > m_installedBuildNo)
379 setStatus(UpdateStatus_CompletedUpdateAvailable);
381 else if(m_updateInfo->m_buildNo == m_installedBuildNo)
383 setStatus(UpdateStatus_CompletedNoUpdates);
385 else
387 setStatus(UpdateStatus_CompletedNewVersionOlder);
390 else
392 setStatus(UpdateStatus_ErrorFetchUpdateInfo);
396 void UpdateChecker::testKnownHosts(void)
398 QStringList hostList;
399 for(int i = 0; known_hosts[i]; i++)
401 hostList << QString::fromLatin1(known_hosts[i]);
404 qDebug("\n[Known Hosts]");
405 log("Testing all known hosts...", "", "---");
407 int hostCount = hostList.count();
408 while(!hostList.isEmpty())
410 QString currentHost = hostList.takeFirst();
411 qDebug("Testing: %s", currentHost.toLatin1().constData());
412 log("", "Testing:", currentHost, "");
413 if (!tryContactHost(currentHost))
415 qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData());
417 log("", "---");
421 ////////////////////////////////////////////////////////////
422 // PRIVATE FUNCTIONS
423 ////////////////////////////////////////////////////////////
425 void UpdateChecker::setStatus(const int status)
427 if(m_status != status)
429 m_status = status;
430 emit statusChanged(status);
434 void UpdateChecker::setProgress(const int progress)
436 if(m_progress != progress)
438 m_progress = progress;
439 emit progressChanged(progress);
443 void UpdateChecker::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4)
445 if(!str1.isNull()) emit messageLogged(str1);
446 if(!str2.isNull()) emit messageLogged(str2);
447 if(!str3.isNull()) emit messageLogged(str3);
448 if(!str4.isNull()) emit messageLogged(str4);
451 bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url)
453 bool success = false;
454 log("", "Trying mirror:", url);
456 const QString randPart = rand_str();
457 const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart);
458 const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart);
460 if(getUpdateInfo(url, outFileVers, outFileSign))
462 log("", "Download okay, checking signature:");
463 if(checkSignature(outFileVers, outFileSign))
465 log("", "Signature okay, parsing info:");
466 success = parseVersionInfo(outFileVers, updateInfo);
468 else
470 log("", "Bad signature, take care!");
473 else
475 log("", "Download has failed!");
478 QFile::remove(outFileVers);
479 QFile::remove(outFileSign);
481 return success;
484 bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign)
486 log("", "Downloading update info:");
487 if(!getFile(QString("%1%2" ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers))
489 return false;
492 log("", "Downloading signature:");
493 if(!getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign))
495 return false;
498 return true;
501 bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo)
503 QRegExp value("^(\\w+)=(.+)$");
504 QRegExp section("^\\[(.+)\\]$");
506 QDate updateInfoDate;
507 updateInfo->resetInfo();
509 QFile data(file);
510 if(!data.open(QIODevice::ReadOnly))
512 qWarning("Cannot open update info file for reading!");
513 return false;
516 bool inHdr = false;
517 bool inSec = false;
519 while(!data.atEnd())
521 QString line = QString::fromLatin1(data.readLine()).trimmed();
522 if(section.indexIn(line) >= 0)
524 log(QString("Sec: [%1]").arg(section.cap(1)));
525 inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0);
526 inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0);
527 continue;
529 if(inSec && (value.indexIn(line) >= 0))
531 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
532 if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0)
534 bool ok = false;
535 const unsigned int temp = value.cap(2).toUInt(&ok);
536 if(ok) updateInfo->m_buildNo = temp;
538 else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0)
540 const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
541 if(temp.isValid()) updateInfo->m_buildDate = temp;
543 else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0)
545 updateInfo->m_downloadSite = value.cap(2).trimmed();
547 else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0)
549 updateInfo->m_downloadAddress = value.cap(2).trimmed();
551 else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0)
553 updateInfo->m_downloadFilename = value.cap(2).trimmed();
555 else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0)
557 updateInfo->m_downloadFilecode = value.cap(2).trimmed();
559 else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0)
561 updateInfo->m_downloadChecksum = value.cap(2).trimmed();
564 if(inHdr && (value.indexIn(line) >= 0))
566 log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2)));
567 if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0)
569 QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate);
570 if(temp.isValid()) updateInfoDate = temp;
575 if(!updateInfoDate.isValid())
577 updateInfo->resetInfo();
578 log("WARNING: Version info timestamp is missing!");
579 return false;
582 const QDate currentDate = OS::current_date();
583 if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate)
585 updateInfo->resetInfo();
586 log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate)));
587 return false;
589 else if(currentDate < updateInfoDate)
591 log("Version info is from the future, take care!");
592 qWarning("Version info is from the future, take care!");
595 if(!updateInfo->isComplete())
597 log("WARNING: Version info is incomplete!");
598 return false;
601 return true;
604 //----------------------------------------------------------
605 // EXTERNAL TOOLS
606 //----------------------------------------------------------
608 bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir, bool *httpOk)
610 for (int i = 0; i < 2; i++)
612 if (getFile(url, (i > 0), outFile, maxRedir, httpOk))
614 return true;
617 return false;
620 bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir, bool *httpOk)
622 QFileInfo output(outFile);
623 output.setCaching(false);
624 if (httpOk) *httpOk = false;
626 if (output.exists())
628 QFile::remove(output.canonicalFilePath());
629 if (output.exists())
631 return false;
635 QProcess process;
636 init_process(process, output.absolutePath());
638 QStringList args;
639 if (forceIp4)
641 args << "-4";
644 args << "-T" << "15" << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir);
645 args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR;
646 args << "-O" << output.fileName() << url;
648 QEventLoop loop;
649 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
650 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
651 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
653 QTimer timer;
654 timer.setSingleShot(true);
655 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
657 const QRegExp httpResponseOK("200 OK$");
659 process.start(m_binaryWGet, args);
661 if (!process.waitForStarted())
663 return false;
666 timer.start(25000);
668 while (process.state() != QProcess::NotRunning)
670 loop.exec();
671 const bool bTimeOut = (!timer.isActive());
672 while (process.canReadLine())
674 QString line = QString::fromLatin1(process.readLine()).simplified();
675 if (line.contains(httpResponseOK))
677 line.append(" [OK]");
678 if (httpOk) *httpOk = true;
680 log(line);
682 if (bTimeOut)
684 qWarning("WGet process timed out <-- killing!");
685 process.kill();
686 process.waitForFinished();
687 log("!!! TIMEOUT !!!");
688 return false;
692 timer.stop();
693 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
695 log(QString().sprintf("Exited with code %d", process.exitCode()));
696 return (process.exitCode() == 0) && output.exists() && output.isFile();
699 bool UpdateChecker::tryContactHost(const QString &hostname)
701 log(QString("Connecting to host: %1").arg(hostname));
703 QProcess process;
704 init_process(process, temp_folder());
706 QStringList args;
707 args << "-z" << hostname << QString::number(80);
709 QEventLoop loop;
710 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
711 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
712 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
714 QTimer timer;
715 timer.setSingleShot(true);
716 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
718 process.start(m_binaryNC, args);
720 if (!process.waitForStarted())
722 return false;
725 timer.start(10000);
727 while (process.state() != QProcess::NotRunning)
729 loop.exec();
730 const bool bTimeOut = (!timer.isActive());
731 while (process.canReadLine())
733 QString line = QString::fromLatin1(process.readLine()).simplified();
734 log(line);
736 if (bTimeOut)
738 qWarning("NC process timed out <-- killing!");
739 process.kill();
740 process.waitForFinished();
741 log("!!! TIMEOUT !!!");
742 return false;
746 timer.stop();
747 timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
749 if (process.exitCode() != 0)
751 log("Connection has failed!");
754 log(QString().sprintf("Exited with code %d", process.exitCode()), "");
755 return (process.exitCode() == 0);
758 bool UpdateChecker::checkSignature(const QString &file, const QString &signature)
760 if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0)
762 qWarning("CheckSignature: File and signature should be in same folder!");
763 return false;
766 QString keyRingPath(m_binaryKeys);
767 bool removeKeyring = false;
768 if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0)
770 keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg");
771 removeKeyring = true;
772 if (!QFile::copy(m_binaryKeys, keyRingPath))
774 qWarning("CheckSignature: Failed to copy the key-ring file!");
775 return false;
779 QProcess process;
780 init_process(process, QFileInfo(file).absolutePath());
782 QEventLoop loop;
783 connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
784 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
785 connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit()));
787 process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName());
789 if (!process.waitForStarted())
791 if (removeKeyring)
793 remove_file(keyRingPath);
795 return false;
798 while (process.state() == QProcess::Running)
800 loop.exec();
801 while (process.canReadLine())
803 log(QString::fromLatin1(process.readLine()).simplified());
807 if (removeKeyring)
809 remove_file(keyRingPath);
812 log(QString().sprintf("Exited with code %d", process.exitCode()));
813 return (process.exitCode() == 0);
816 ////////////////////////////////////////////////////////////
817 // SLOTS
818 ////////////////////////////////////////////////////////////
820 /*NONE*/
822 ////////////////////////////////////////////////////////////
823 // EVENTS
824 ////////////////////////////////////////////////////////////
826 /*NONE*/