X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2FUpdateChecker.cpp;h=158fba58e4a47197db9fa8ef22c343e19b17cc4a;hb=f3a762cfba8c93f12fb7a70009bfe926a70b50f3;hp=ab6c11ca6efbf09b859c181f6e399871d911a46d;hpb=4128a250574dde7187247ab0093947fe91638647;p=mutilities%2FMUtilities.git diff --git a/src/UpdateChecker.cpp b/src/UpdateChecker.cpp index ab6c11c..158fba5 100644 --- a/src/UpdateChecker.cpp +++ b/src/UpdateChecker.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // MuldeR's Utilities for Qt -// Copyright (C) 2004-2014 LoRd_MuldeR +// Copyright (C) 2004-2017 LoRd_MuldeR // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -31,6 +31,7 @@ #include #include #include +#include using namespace MUtils; @@ -39,7 +40,6 @@ using namespace MUtils; /////////////////////////////////////////////////////////////////////////////// static const char *header_id = "!Update"; -static const char *section_id = "LameXP"; static const char *mirror_url_postfix[] = { @@ -48,132 +48,217 @@ static const char *mirror_url_postfix[] = NULL }; -static const char *update_mirrors_prim[] = +static const char *update_mirrors[] = { "http://muldersoft.com/", "http://mulder.bplaced.net/", - "http://mulder.cwsurf.de/", "http://mulder.6te.net/", "http://mulder.webuda.com/", - "http://mulder.byethost13.com/", - "http://muldersoft.kilu.de/", "http://mulder.pe.hu/", - "http://muldersoft.zxq.net/", + "http://muldersoft.square7.ch/", + "http://muldersoft.co.nf/", + "http://muldersoft.eu.pn/", + "http://muldersoft.lima-city.de/", + "http://www.muldersoft.keepfree.de/", "http://lamexp.sourceforge.net/", "http://lordmulder.github.io/LameXP/", - "http://lord_mulder.bitbucket.org/", + "http://muldersoft.bitbucket.io/", //http://lord_mulder.bitbucket.org/ "http://www.tricksoft.de/", - NULL -}; - -static const char *update_mirrors_back[] = -{ - "http://mplayer.savedonthe.net/", + "http://repo.or.cz/LameXP.git/blob_plain/gh-pages:/", + "http://gitlab.com/lamexp/lamexp/raw/gh-pages/", NULL }; static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!! { - "http://www.163.com/", - "http://www.7-zip.org/", - "http://www.ac3filter.net/", - "http://www.amazon.com/", - "http://antergos.com/", - "http://www.aol.com/", - "http://www.apache.org/", - "http://www.apple.com/", - "http://www.adobe.com/", - "http://www.avidemux.org/", - "http://www.babylon.com/", - "http://www.baidu.com/", - "http://bandcamp.com/", - "http://www.bbc.co.uk/", - "http://www.berlios.de/", - "http://www.bing.com/", - "http://www.cnet.com/", - "http://cnzz.com/", - "http://www.codeplex.com/", - "http://qt.digia.com/", - "http://www.ebay.com/", - "http://www.equation.com/", - "http://fc2.com/", - "http://fedoraproject.org/", - "http://www.ffmpeg.org/", - "http://blog.flickr.net/en", - "http://free-codecs.com/", - "http://blog.gitorious.org/", - "http://git-scm.com/", - "http://www.gmx.net/", - "http://www.gnome.org/", - "http://www.gnu.org/", - "http://go.com/", - "http://code.google.com/", - "http://www.heise.de/", - "http://www.huffingtonpost.co.uk/", - "http://www.iana.org/", - "http://www.imdb.com/", - "http://www.imgburn.com/", - "http://imgur.com/", - "http://en.jd.com/", - "http://kannmanumdieuhrzeitschonnbierchentrinken.de/", - "http://mirrors.kernel.org/", - "http://lame.sourceforge.net/", - "http://www.libav.org/", - "http://www.linkedin.com/about-us", - "http://www.linuxmint.com/", - "http://www.livedoor.com/", - "http://www.livejournal.com/", - "http://go.mail.ru/", - "http://www.mediafire.com/", - "http://ftp.mozilla.org/", - "http://mplayerhq.hu/", - "http://www.msn.com/?st=1", - "http://oss.netfarm.it/", - "http://www.nytimes.com/", - "http://www.opera.com/", - "http://pastie.org/", - "http://www.portablefreeware.com/", - "http://qt-project.org/", - "http://www.quakelive.com/", - "http://www.seamonkey-project.org/", - "http://www.sina.com.cn/", - "http://www.sohu.com/", - "http://www.soso.com/", - "http://sourceforge.net/", - "http://www.spiegel.de/", - "http://tdm-gcc.tdragon.net/", - "http://www.tdrsmusic.com/", - "http://www.ubuntu.com/", - "http://status.twitter.com/", - "http://www.uol.com.br/", - "http://www.videohelp.com/", - "http://www.videolan.org/", - "http://virtualdub.org/", - "http://blog.virustotal.com/", - "http://weibo.com/login.php", - "http://www.wikipedia.org/", - "http://www.winamp.com/", - "http://wordpress.com/about/", - "http://xiph.org/", - "http://us.mail.yahoo.com/", - "http://www.yandex.ru/", - "http://www.youtube.com/yt/about/", - "http://www.zedo.com/", - "http://ffmpeg.zeranoe.com/", + "www.163.com", + "www.7-zip.org", + "www.ac3filter.net", + "clbianco.altervista.org", + "status.aws.amazon.com", + "build.antergos.com", + "www.aol.com", + "www.apache.org", + "www.apple.com", + "www.adobe.com", + "archive.org", + "www.artlebedev.ru", + "web.audacityteam.org", + "status.automattic.com", + "www.avidemux.org", + "www.babylon.com", + "www.baidu.com", + "bandcamp.com", + "www.bbc.co.uk", + "www.berlios.de", + "www.bing.com", + "www.bingeandgrab.com", + "www.bucketheadpikes.com", + "www.buckethead-coop.com", + "www.buzzfeed.com", + "www.cam.ac.uk", + "www.ccc.de", + "home.cern", + "www.citizeninsomniac.com", + "www.cnet.com", + "cnzz.com", + "www.cuhk.edu.hk", + "www.codeplex.com", + "www.codeproject.com", + "www.der-postillon.com", + "www.ebay.com", + "www.equation.com", + "www.farbrausch.de", + "fc2.com", + "fedoraproject.org", + "blog.fefe.de", + "www.ffmpeg.org", + "blog.flickr.net", + "www.fraunhofer.de", + "free-codecs.com", + "git-scm.com", + "doc.gitlab.com", + "www.gmx.net", + "news.gnome.org", + "www.gnu.org", + "go.com", + "code.google.com", + "haali.su", + "www.harvard.edu", + "www.heise.de", + "www.helmholtz.de", + "www.huffingtonpost.co.uk", + "www.hu-berlin.de", + "www.iana.org", + "www.imdb.com", + "www.imgburn.com", + "imgur.com", + "www.iuj.ac.jp", + "www.jd.com", + "www.jiscdigitalmedia.ac.uk", + "kannmanumdieuhrzeitschonnbierchentrinken.de", + "mirrors.kernel.org", + "komisar.gin.by", + "lame.sourceforge.net", + "www.libav.org", + "blog.linkedin.com", + "www.linuxmint.com", + "www.livedoor.com", + "www.livejournal.com", + "longplayer.org", + "go.mail.ru", + "marknelson.us", + "www.mediafire.com", + "web.mit.edu", + "www.mod-technologies.com", + "ftp.mozilla.org", + "www.mpg.de", + "mplayerhq.hu", + "www.msn.com", + "wiki.multimedia.cx", + "www.nch.com.au", + "neocities.org", + "mirror.netcologne.de", + "oss.netfarm.it", + "blog.netflix.com", + "netrenderer.de", + "www.nytimes.com", + "www.opera.com", + "www.oxford.gov.uk", + "www.partha.com", + "pastebin.com", + "pastie.org", + "portableapps.com", + "www.portablefreeware.com", + "support.proboards.com", + "www.qq.com", + "www.qt.io", + "www.quakelive.com", + "rationalqm.us", + "www.rwth-aachen.de", + "www.seamonkey-project.org", + "selfhtml.org", + "www.sina.com.cn", + "www.sohu.com", + "help.sogou.com", + "sourceforge.net", + "www.spiegel.de", + "www.sputnikmusic.com", + "stackoverflow.com", + "www.stanford.edu", + "www.t-online.de", + "www.tagesschau.de", + "tdm-gcc.tdragon.net", + "www.tdrsmusic.com", + "tu-dresden.de", + "www.ubuntu.com", + "www.uol.com.br", + "www.videohelp.com", + "www.videolan.org", + "virtualdub.org", + "blog.virustotal.com", + "www.vkgoeswild.com", + "www.warr.org", + "www.weibo.com", + "status.wikimedia.org", + "www.winamp.com", + "www.winhoros.de", + "wpde.org", + "x265.org", + "xhmikosr.1f0.de", + "xiph.org", + "us.mail.yahoo.com", + "www.youtube.com", + "www.zedo.com", + "ffmpeg.zeranoe.com", NULL }; -static const int MIN_CONNSCORE = 8; +static const int MIN_CONNSCORE = 5; +static const int QUICK_MIRRORS = 3; +static const int MAX_CONN_TIMEOUT = 8000; +static const int DOWNLOAD_TIMEOUT = 30000; + static const int VERSION_INFO_EXPIRES_MONTHS = 6; -static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111106 IceCat/7.0.1"; +static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0"; /*use something innocuous*/ + +#define CHECK_CANCELLED() do \ +{ \ + if(m_cancelled) \ + { \ + m_success = false; \ + log("", "Update check has been cancelled by user!"); \ + setProgress(m_maxProgress); \ + setStatus(UpdateStatus_CancelledByUser); \ + return; \ + } \ +} \ +while(0) + +//////////////////////////////////////////////////////////// +// Helper Functions +//////////////////////////////////////////////////////////// -//Helper function static int getMaxProgress(void) { - int counter = MIN_CONNSCORE + 2; - for(int i = 0; update_mirrors_prim[i]; i++) counter++; - for(int i = 0; update_mirrors_back[i]; i++) counter++; - return counter; + int counter = 0; + while (update_mirrors[counter]) + { + counter++; + } + counter += MIN_CONNSCORE + QUICK_MIRRORS + 2; + return counter; ; +} + +static QStringList buildRandomList(const char *const values[]) +{ + QStringList list; + for (int index = 0; values[index]; index++) + { + const int pos = next_rand_u32() % (index + 1); + list.insert(pos, QString::fromLatin1(values[index])); + } + return list; } //////////////////////////////////////////////////////////// @@ -193,24 +278,40 @@ void UpdateCheckerInfo::resetInfo(void) m_downloadAddress.clear(); m_downloadFilename.clear(); m_downloadFilecode.clear(); + m_downloadChecksum.clear(); +} + +bool UpdateCheckerInfo::isComplete(void) +{ + if(this->m_buildNo < 1) return false; + if(this->m_buildDate.year() < 2010) return false; + if(this->m_downloadSite.isEmpty()) return false; + if(this->m_downloadAddress.isEmpty()) return false; + if(this->m_downloadFilename.isEmpty()) return false; + if(this->m_downloadFilecode.isEmpty()) return false; + if(this->m_downloadChecksum.isEmpty()) return false; + + return true; } //////////////////////////////////////////////////////////// // Constructor & Destructor //////////////////////////////////////////////////////////// -UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binGnuPG, const QString &binKeys, const quint32 &installedBuildNo, const bool betaUpdates, const bool testMode) +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) : m_updateInfo(new UpdateCheckerInfo()), m_binaryWGet(binWGet), + m_binaryNC(binNC), m_binaryGnuPG(binGnuPG), m_binaryKeys(binKeys), + m_applicationId(applicationId), m_installedBuildNo(installedBuildNo), m_betaUpdates(betaUpdates), m_testMode(testMode), m_maxProgress(getMaxProgress()) { - m_success = false; + m_success = m_cancelled = false; m_status = UpdateStatus_NotStartedYet; m_progress = 0; @@ -222,7 +323,16 @@ UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binGnuPG, co UpdateChecker::~UpdateChecker(void) { - delete m_updateInfo; +} + +//////////////////////////////////////////////////////////// +// Public slots +//////////////////////////////////////////////////////////// + +void UpdateChecker::start(Priority priority) +{ + m_cancelled = m_success = false; + QThread::start(priority); } //////////////////////////////////////////////////////////// @@ -240,23 +350,18 @@ void UpdateChecker::checkForUpdates(void) { // ----- Initialization ----- // - m_success = false; m_updateInfo->resetInfo(); - seed_rand(); setProgress(0); // ----- Test Internet Connection ----- // - int connectionScore = 0; - int maxConnectTries = (3 * MIN_CONNSCORE) / 2; - - log("Checking internet connection..."); + log("Checking internet connection...", ""); setStatus(UpdateStatus_CheckingConnection); - const int networkStatus = lamexp_network_status(); - if(networkStatus == lamexp_network_non) + const int networkStatus = OS::network_status(); + if(networkStatus == OS::NETWORK_TYPE_NON) { - log("", "Operating system reports that the computer is currently offline !!!"); + log("Operating system reports that the computer is currently offline !!!"); setProgress(m_maxProgress); setStatus(UpdateStatus_ErrorNoConnection); return; @@ -266,24 +371,37 @@ void UpdateChecker::checkForUpdates(void) // ----- Test Known Hosts Connectivity ----- // - QStringList hostList; - for(int i = 0; known_hosts[i]; i++) - { - hostList << QString::fromLatin1(known_hosts[i]); - } + int connectionScore = 0; + QStringList mirrorList = buildRandomList(known_hosts); - while(!(hostList.isEmpty() || (connectionScore >= MIN_CONNSCORE) || (maxConnectTries < 1))) + for(int connectionTimout = 125; connectionTimout <= MAX_CONN_TIMEOUT; connectionTimout *= 2) { - switch(tryContactHost(hostList.takeAt(next_rand32() % hostList.count()))) + QElapsedTimer elapsedTimer; + elapsedTimer.start(); + const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout; + while (!elapsedTimer.hasExpired(globalTimout)) { - case 01: connectionScore += 1; break; - case 02: connectionScore += 2; break; - default: maxConnectTries -= 1; break; + const QString hostName = mirrorList.takeFirst(); + if (tryContactHost(hostName, connectionTimout)) + { + connectionScore += 1; + setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1)); + elapsedTimer.restart(); + if (connectionScore >= MIN_CONNSCORE) + { + goto endLoop; /*success*/ + } + } + else + { + mirrorList.append(hostName); /*re-schedule*/ + } + CHECK_CANCELLED(); + msleep(1); } - setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1)); - msleep(64); } +endLoop: if(connectionScore < MIN_CONNSCORE) { log("", "Connectivity test has failed: Internet connection appears to be broken!"); @@ -292,51 +410,40 @@ void UpdateChecker::checkForUpdates(void) return; } - // ----- Build Mirror List ----- // + // ----- Fetch Update Info From Server ----- // - log("", "----", "", "Checking for updates online..."); + log("----", "", "Checking for updates online..."); setStatus(UpdateStatus_FetchingUpdates); - QStringList mirrorList; - for(int index = 0; update_mirrors_prim[index]; index++) - { - mirrorList << QString::fromLatin1(update_mirrors_prim[index]); - } - - if(const int len = mirrorList.count()) - { - const int rounds = len * 1097; - for(int i = 0; i < rounds; i++) - { - mirrorList.swap(i % len, next_rand32() % len); - } - } - - for(int index = 0; update_mirrors_back[index]; index++) - { - mirrorList << QString::fromLatin1(update_mirrors_back[index]); - } - - // ----- Fetch Update Info From Server ----- // + int mirrorCount = 0; + mirrorList = buildRandomList(update_mirrors); while(!mirrorList.isEmpty()) { - QString currentMirror = mirrorList.takeFirst(); setProgress(m_progress + 1); - if(!m_success) + const QString currentMirror = mirrorList.takeFirst(); + const bool isQuick = (mirrorCount++ < QUICK_MIRRORS); + if(tryUpdateMirror(m_updateInfo.data(), currentMirror, isQuick)) { - if(tryUpdateMirror(m_updateInfo, currentMirror)) - { - m_success = true; - } + m_success = true; /*success*/ + break; } - else + if (isQuick) { - msleep(64); + mirrorList.append(currentMirror); /*re-schedule*/ } + CHECK_CANCELLED(); + msleep(1); } - setProgress(m_maxProgress); + while (m_progress < m_maxProgress) + { + msleep(16); + setProgress(m_progress + 1); + CHECK_CANCELLED(); + } + + // ----- Generate final result ----- // if(m_success) { @@ -376,21 +483,11 @@ void UpdateChecker::testKnownHosts(void) QString currentHost = hostList.takeFirst(); qDebug("Testing: %s", currentHost.toLatin1().constData()); log("", "Testing:", currentHost, ""); - QString outFile = QString("%1/%2.htm").arg(temp_folder(), rand_str()); - bool httpOk = false; - if(!getFile(currentHost, outFile, 0, &httpOk)) + if (!tryContactHost(currentHost, DOWNLOAD_TIMEOUT)) { - if(httpOk) - { - qWarning("\nConnectivity test was SLOW on the following site:\n%s\n", currentHost.toLatin1().constData()); - } - else - { - qWarning("\nConnectivity test FAILED on the following site:\n%s\n", currentHost.toLatin1().constData()); - } + qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData()); } log("", "---"); - QFile::remove(outFile); } } @@ -424,48 +521,25 @@ void UpdateChecker::log(const QString &str1, const QString &str2, const QString if(!str4.isNull()) emit messageLogged(str4); } -int UpdateChecker::tryContactHost(const QString &url) -{ - int result = -1; bool httpOkay = false; - const QString outFile = QString("%1/%2.htm").arg(temp_folder(), rand_str()); - log("", "Testing host:", url); - - if(getFile(url, outFile, 0, &httpOkay)) - { - log("Connection to host was established successfully."); - result = 2; - } - else - { - if(httpOkay) - { - log("Connection to host timed out after HTTP OK was received."); - result = 1; - } - else - { - log("Connection failed: The host could not be reached!"); - result = 0; - } - } - - QFile::remove(outFile); - return result; -} - -bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url) +bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString &url, const bool &quick) { bool success = false; - log("", "Trying mirror:", url); + log("", "Trying mirror:", url, ""); - const QString randPart = rand_str(); + if (!tryContactHost(QUrl(url).host(), quick ? (MAX_CONN_TIMEOUT / 10) : MAX_CONN_TIMEOUT)) + { + log("", quick ? "Mirror is too slow!" :"Mirror is unreachable!"); + return false; + } + + const QString randPart = next_rand_str(); const QString outFileVers = QString("%1/%2.ver").arg(temp_folder(), randPart); const QString outFileSign = QString("%1/%2.sig").arg(temp_folder(), randPart); - if(getUpdateInfo(url, outFileVers, outFileSign)) + if (getUpdateInfo(url, outFileVers, outFileSign)) { log("", "Download okay, checking signature:"); - if(checkSignature(outFileVers, outFileSign)) + if (checkSignature(outFileVers, outFileSign)) { log("", "Signature okay, parsing info:"); success = parseVersionInfo(outFileVers, updateInfo); @@ -488,31 +562,149 @@ bool UpdateChecker::tryUpdateMirror(UpdateCheckerInfo *updateInfo, const QString bool UpdateChecker::getUpdateInfo(const QString &url, const QString &outFileVers, const QString &outFileSign) { - log("", "Downloading update info:"); - if(!getFile(QString("%1%2" ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers)) + log("Downloading update info:"); + if(!getFile(QString("%1%2" ).arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVers)) { return false; } log("", "Downloading signature:"); - if(!getFile(QString("%1%2.sig").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign)) + if(!getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign)) + { + return false; + } + + return true; +} + +bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo) +{ + QRegExp value("^(\\w+)=(.+)$"); + QRegExp section("^\\[(.+)\\]$"); + + QDate updateInfoDate; + updateInfo->resetInfo(); + + QFile data(file); + if(!data.open(QIODevice::ReadOnly)) + { + qWarning("Cannot open update info file for reading!"); + return false; + } + + bool inHdr = false; + bool inSec = false; + + while(!data.atEnd()) + { + QString line = QString::fromLatin1(data.readLine()).trimmed(); + if(section.indexIn(line) >= 0) + { + log(QString("Sec: [%1]").arg(section.cap(1))); + inSec = (section.cap(1).compare(m_applicationId, Qt::CaseInsensitive) == 0); + inHdr = (section.cap(1).compare(QString::fromLatin1(header_id), Qt::CaseInsensitive) == 0); + continue; + } + if(inSec && (value.indexIn(line) >= 0)) + { + log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); + if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0) + { + bool ok = false; + const unsigned int temp = value.cap(2).toUInt(&ok); + if(ok) updateInfo->m_buildNo = temp; + } + else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0) + { + const QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); + if(temp.isValid()) updateInfo->m_buildDate = temp; + } + else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadSite = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadAddress = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilename = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilecode = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadChecksum", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadChecksum = value.cap(2).trimmed(); + } + } + if(inHdr && (value.indexIn(line) >= 0)) + { + log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); + if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0) + { + QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); + if(temp.isValid()) updateInfoDate = temp; + } + } + } + + if(!updateInfoDate.isValid()) + { + updateInfo->resetInfo(); + log("WARNING: Version info timestamp is missing!"); + return false; + } + + const QDate currentDate = OS::current_date(); + if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < currentDate) + { + updateInfo->resetInfo(); + log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate))); + return false; + } + else if(currentDate < updateInfoDate) + { + log("Version info is from the future, take care!"); + qWarning("Version info is from the future, take care!"); + } + + if(!updateInfo->isComplete()) { + log("WARNING: Version info is incomplete!"); return false; } return true; } -bool UpdateChecker::getFile(const QString &url, const QString &outFile, unsigned int maxRedir, bool *httpOk) +//---------------------------------------------------------- +// EXTERNAL TOOLS +//---------------------------------------------------------- + +bool UpdateChecker::getFile(const QString &url, const QString &outFile, const unsigned int maxRedir) +{ + for (int i = 0; i < 2; i++) + { + if (getFile(url, (i > 0), outFile, maxRedir)) + { + return true; + } + } + return false; +} + +bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir) { QFileInfo output(outFile); output.setCaching(false); - if(httpOk) *httpOk = false; - if(output.exists()) + if (output.exists()) { QFile::remove(output.canonicalFilePath()); - if(output.exists()) + if (output.exists()) { return false; } @@ -522,55 +714,55 @@ bool UpdateChecker::getFile(const QString &url, const QString &outFile, unsigned init_process(process, output.absolutePath()); QStringList args; - args << "-T" << "15" << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir); + if (forceIp4) + { + args << "-4"; + } + + args << "--no-config" << "--no-cache" << "--no-dns-cache" << "--no-check-certificate" << "--no-hsts"; + args << QString().sprintf("--max-redirect=%u", maxRedir) << QString().sprintf("--timeout=%u", DOWNLOAD_TIMEOUT / 1000); args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR; args << "-O" << output.fileName() << url; QEventLoop loop; connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); - connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit())); connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); QTimer timer; timer.setSingleShot(true); - timer.setInterval(25000); connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); const QRegExp httpResponseOK("200 OK$"); - + process.start(m_binaryWGet, args); - - if(!process.waitForStarted()) + + if (!process.waitForStarted()) { return false; } - timer.start(); + timer.start(DOWNLOAD_TIMEOUT); - while(process.state() != QProcess::NotRunning) + while (process.state() != QProcess::NotRunning) { loop.exec(); const bool bTimeOut = (!timer.isActive()); - while(process.canReadLine()) + while (process.canReadLine()) { - QString line = QString::fromLatin1(process.readLine()).simplified(); - if(line.contains(httpResponseOK)) - { - line.append(" [OK]"); - if(httpOk) *httpOk = true; - } + const QString line = QString::fromLatin1(process.readLine()).simplified(); log(line); } - if(bTimeOut) + if (bTimeOut || m_cancelled) { qWarning("WGet process timed out <-- killing!"); process.kill(); process.waitForFinished(); - log("!!! TIMEOUT !!!"); + log(bTimeOut ? "!!! TIMEOUT !!!": "!!! CANCELLED !!!"); return false; } } - + timer.stop(); timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); @@ -578,150 +770,121 @@ bool UpdateChecker::getFile(const QString &url, const QString &outFile, unsigned return (process.exitCode() == 0) && output.exists() && output.isFile(); } -bool UpdateChecker::checkSignature(const QString &file, const QString &signature) +bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec) { - if(QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0) - { - qWarning("CheckSignature: File and signature should be in same folder!"); - return false; - } - if(QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0) - { - qWarning("CheckSignature: File and keyring should be in same folder!"); - return false; - } + log(QString("Connecting to host: %1").arg(hostname)); QProcess process; - init_process(process, QFileInfo(file).absolutePath()); + init_process(process, temp_folder()); + + QStringList args; + args << "-z" << hostname << QString::number(80); QEventLoop loop; connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); - connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit())); connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); - - process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(m_binaryKeys).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName()); - if(!process.waitForStarted()) + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + process.start(m_binaryNC, args); + + if (!process.waitForStarted()) { return false; } - while(process.state() == QProcess::Running) + timer.start(timeoutMsec); + + while (process.state() != QProcess::NotRunning) { loop.exec(); - while(process.canReadLine()) + const bool bTimeOut = (!timer.isActive()); + while (process.canReadLine()) { - log(QString::fromLatin1(process.readLine()).simplified()); + QString line = QString::fromLatin1(process.readLine()).simplified(); + log(line); + } + if (bTimeOut || m_cancelled) + { + qWarning("NC process timed out <-- killing!"); + process.kill(); + process.waitForFinished(); + log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!"); + return false; } } - - log(QString().sprintf("Exited with code %d", process.exitCode())); + + timer.stop(); + timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + if (process.exitCode() != 0) + { + log("Connection has failed!"); + } + + log(QString().sprintf("Exited with code %d", process.exitCode()), ""); return (process.exitCode() == 0); } -bool UpdateChecker::parseVersionInfo(const QString &file, UpdateCheckerInfo *updateInfo) +bool UpdateChecker::checkSignature(const QString &file, const QString &signature) { - QRegExp value("^(\\w+)=(.+)$"); - QRegExp section("^\\[(.+)\\]$"); - - QDate updateInfoDate; - updateInfo->resetInfo(); - - QFile data(file); - if(!data.open(QIODevice::ReadOnly)) + if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0) { - qWarning("Cannot open update info file for reading!"); + qWarning("CheckSignature: File and signature should be in same folder!"); return false; } - - bool inHeader = false; - bool inSection = false; - - while(!data.atEnd()) + + QString keyRingPath(m_binaryKeys); + bool removeKeyring = false; + if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0) { - QString line = QString::fromLatin1(data.readLine()).trimmed(); - if(section.indexIn(line) >= 0) - { - log(QString("Sec: [%1]").arg(section.cap(1))); - inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0); - inHeader = (section.cap(1).compare(header_id, Qt::CaseInsensitive) == 0); - continue; - } - if(inSection && (value.indexIn(line) >= 0)) - { - log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); - if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0) - { - bool ok = false; - unsigned int temp = value.cap(2).toUInt(&ok); - if(ok) updateInfo->m_buildNo = temp; - } - else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0) - { - QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); - if(temp.isValid()) updateInfo->m_buildDate = temp; - } - else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0) - { - updateInfo->m_downloadSite = value.cap(2).trimmed(); - } - else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0) - { - updateInfo->m_downloadAddress = value.cap(2).trimmed(); - } - else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0) - { - updateInfo->m_downloadFilename = value.cap(2).trimmed(); - } - else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0) - { - updateInfo->m_downloadFilecode = value.cap(2).trimmed(); - } - } - if(inHeader && (value.indexIn(line) >= 0)) + keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg"); + removeKeyring = true; + if (!QFile::copy(m_binaryKeys, keyRingPath)) { - log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); - if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0) - { - QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); - if(temp.isValid()) updateInfoDate = temp; - } + qWarning("CheckSignature: Failed to copy the key-ring file!"); + return false; } } - - if(!updateInfoDate.isValid()) - { - updateInfo->resetInfo(); - log("WARNING: Version info timestamp is missing!"); - return false; - } - else if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < current_date_safe()) + + QProcess process; + init_process(process, QFileInfo(file).absolutePath()); + + QEventLoop loop; + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); + + process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName()); + + if (!process.waitForStarted()) { - updateInfo->resetInfo(); - log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate))); + if (removeKeyring) + { + remove_file(keyRingPath); + } return false; } - else if(current_date_safe() < updateInfoDate) + + while (process.state() == QProcess::Running) { - log("Version info is from the future, take care!"); - qWarning("Version info is from the future, take care!"); + loop.exec(); + while (process.canReadLine()) + { + log(QString::fromLatin1(process.readLine()).simplified()); + } } - bool complete = true; - - if(!(updateInfo->m_buildNo > 0)) complete = false; - if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false; - if(updateInfo->m_downloadSite.isEmpty()) complete = false; - if(updateInfo->m_downloadAddress.isEmpty()) complete = false; - if(updateInfo->m_downloadFilename.isEmpty()) complete = false; - if(updateInfo->m_downloadFilecode.isEmpty()) complete = false; - - if(!complete) + if (removeKeyring) { - log("WARNING: Version info is incomplete!"); + remove_file(keyRingPath); } - return complete; + log(QString().sprintf("Exited with code %d", process.exitCode())); + return (process.exitCode() == 0); } ////////////////////////////////////////////////////////////