X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2FUpdateChecker.cpp;h=467ab178a432697c2c76930ef1db03f2b1fbe238;hb=094d203774862cca1ecd401c2baef078f92a48bf;hp=386e1ff7cd050959e6df6daac00e7ce779cb2dc3;hpb=d64aff1dcd9e3d5d7befd86110611d66e5197856;p=mutilities%2FMUtilities.git diff --git a/src/UpdateChecker.cpp b/src/UpdateChecker.cpp index 386e1ff..467ab17 100644 --- a/src/UpdateChecker.cpp +++ b/src/UpdateChecker.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // MuldeR's Utilities for Qt -// Copyright (C) 2004-2015 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,8 @@ #include #include #include +#include +#include using namespace MUtils; @@ -47,172 +49,227 @@ 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.bplaced.net/", "http://mulder.6te.net/", - "http://mulder.webuda.com/", //"http://mulder.byethost13.com/", - "http://mulder.pe.hu/", //"http://muldersoft.kilu.de/", - "http://muldersoft.square7.ch/", //"http://muldersoft.zxq.net/", + "http://mulder.000webhostapp.com/", //"http://mulder.webuda.com/", + "http://mulder.pe.hu/", + "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://clbianco.altervista.org/", - "http://status.aws.amazon.com/", - "http://build.antergos.com/", - "http://www.aol.com/", - "http://www.apache.org/", - "http://www.apple.com/", - "http://www.adobe.com/", - "http://archive.org/web/", - "http://www.artlebedev.ru/", - "http://web.audacityteam.org/", - "http://status.automattic.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.bingeandgrab.com/", - "http://www.bucketheadpikes.com/", - "http://www.buckethead-coop.com/", - "http://www.buzzfeed.com/", - "http://www.ccc.de/", - "http://www.citizeninsomniac.com/WMV/", - "http://www.cnet.com/", - "http://cnzz.com/", - "http://www.codeplex.com/", - "http://www.codeproject.com/", - "http://www.der-postillon.com/", - "http://www.ebay.com/", - "http://www.equation.com/", - "http://www.farbrausch.de/", - "http://fc2.com/", - "http://fedoraproject.org/wiki/Fedora_Project_Wiki", - "http://blog.fefe.de/", - "http://www.ffmpeg.org/", - "http://blog.flickr.net/en", - "http://free-codecs.com/", - "http://git-scm.com/", - "http://doc.gitlab.com/", - "http://www.gmx.net/", - "http://news.gnome.org/", - "http://www.gnu.org/", - "http://go.com/", - "http://code.google.com/", - "http://haali.su/mkv/", - "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://www.jd.com/contact/", - "http://www.jiscdigitalmedia.ac.uk/", - "http://kannmanumdieuhrzeitschonnbierchentrinken.de/", - "http://mirrors.kernel.org/", - "http://komisar.gin.by/", - "http://lame.sourceforge.net/", - "http://www.libav.org/", - "http://blog.linkedin.com/", - "http://www.linuxmint.com/", - "http://www.livedoor.com/", - "http://www.livejournal.com/", - "http://longplayer.org/", - "http://go.mail.ru/", - "http://marknelson.us/", - "http://www.mediafire.com/about/", - "http://www.mod-technologies.com/", - "http://ftp.mozilla.org/", - "http://mplayerhq.hu/", - "http://www.msn.com/en-us/", - "http://wiki.multimedia.cx/", - "http://www.nch.com.au/", - "http://mirror.netcologne.de/", - "http://oss.netfarm.it/", - "http://blog.netflix.com/", - "http://netrenderer.de/", - "http://www.nytimes.com/", - "http://www.opera.com/", - "http://www.partha.com/", - "http://pastebin.com/", - "http://pastie.org/", - "http://portableapps.com/about", - "http://www.portablefreeware.com/", - "http://support.proboards.com/", - "http://www.qq.com/", - "http://www.qt.io/", - "http://www.quakelive.com/", - "http://rationalqm.us/mine.html", - "http://www.seamonkey-project.org/", - "http://selfhtml.org/", - "http://www.sina.com.cn/", - "http://www.sohu.com/", - "http://help.sogou.com/", - "http://sourceforge.net/", - "http://www.spiegel.de/", - "http://www.sputnikmusic.com/", - "http://stackoverflow.com/", - "http://www.t-online.de/", - "http://www.tagesschau.de/", - "http://tdm-gcc.tdragon.net/", - "http://www.tdrsmusic.com/", - "http://www.ubuntu.com/", - "http://www.uol.com.br/", - "http://www.videohelp.com/", - "http://www.videolan.org/", - "http://virtualdub.org/", - "http://blog.virustotal.com/", - "http://www.vkgoeswild.com/", - "http://www.warr.org/WAhere.html", - "http://www.weibo.com/login.php", - "http://status.wikimedia.org/", - "http://www.winamp.com/", - "http://www.winhoros.de/", - "http://wpde.org/", - "http://x265.org/", - "http://xhmikosr.1f0.de/", - "http://xiph.org/", - "http://us.mail.yahoo.com/", - "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.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.ethz.ch", + "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.ox-fanzine.de", + "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.reddit.com", + "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", + "portal.uned.es", + "www.unibuc.ro", + "www.uniroma1.it", + "www.univ-paris1.fr", + "www.univer.kharkov.ua", + "www.univie.ac.at", + "www.uol.com.br", + "www.uva.nl", + "www.uw.edu.pl", + "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(MUTILS_BOOLIFY(m_cancelled)) \ + { \ + m_success.fetchAndStoreOrdered(0); \ + 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; } //////////////////////////////////////////////////////////// @@ -252,10 +309,11 @@ bool UpdateCheckerInfo::isComplete(void) // Constructor & Destructor //////////////////////////////////////////////////////////// -UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binGnuPG, const QString &binKeys, const QString &applicationId, const quint32 &installedBuildNo, const bool betaUpdates, const bool testMode) +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) : m_updateInfo(new UpdateCheckerInfo()), m_binaryWGet(binWGet), + m_binaryMCat(binMCat), m_binaryGnuPG(binGnuPG), m_binaryKeys(binKeys), m_applicationId(applicationId), @@ -264,7 +322,6 @@ UpdateChecker::UpdateChecker(const QString &binWGet, const QString &binGnuPG, co m_testMode(testMode), m_maxProgress(getMaxProgress()) { - m_success = false; m_status = UpdateStatus_NotStartedYet; m_progress = 0; @@ -279,6 +336,17 @@ UpdateChecker::~UpdateChecker(void) } //////////////////////////////////////////////////////////// +// Public slots +//////////////////////////////////////////////////////////// + +void UpdateChecker::start(Priority priority) +{ + m_success.fetchAndStoreOrdered(0); + m_cancelled.fetchAndStoreOrdered(0); + QThread::start(priority); +} + +//////////////////////////////////////////////////////////// // Protected functions //////////////////////////////////////////////////////////// @@ -293,23 +361,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 = 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; @@ -319,24 +382,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 = 250; 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!"); @@ -345,53 +421,42 @@ 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.data(), currentMirror)) - { - m_success = true; - } + m_success.ref(); /*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) + if(MUTILS_BOOLIFY(m_success)) { if(m_updateInfo->m_buildNo > m_installedBuildNo) { @@ -423,27 +488,29 @@ void UpdateChecker::testKnownHosts(void) qDebug("\n[Known Hosts]"); log("Testing all known hosts...", "", "---"); - int hostCount = hostList.count(); + QSet ipAddrSet; + quint32 ipAddr; while(!hostList.isEmpty()) { - QString currentHost = hostList.takeFirst(); + const 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, &ipAddr)) { - if(httpOk) + if (ipAddrSet.contains(ipAddr)) { - qWarning("\nConnectivity test was SLOW on the following site:\n%s\n", currentHost.toLatin1().constData()); + qWarning("Duplicate IP-address 0x%08X was encountered!", ipAddr); } else { - qWarning("\nConnectivity test FAILED on the following site:\n%s\n", currentHost.toLatin1().constData()); + ipAddrSet << ipAddr; /*not encountered yet*/ } } + else + { + qWarning("\nConnectivity test FAILED on the following host:\n%s\n", currentHost.toLatin1().constData()); + } log("", "---"); - QFile::remove(outFile); } } @@ -477,50 +544,27 @@ 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, ""); + + 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 = rand_str(); + 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:"); + log("", "Signature okay, parsing info:", ""); success = parseVersionInfo(outFileVers, updateInfo); } else @@ -541,43 +585,153 @@ 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)) { + if (!m_cancelled) + { + log("", "Downloading signature:", ""); + if (getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign)) + { + return true; + } + } + } + return false; +} + +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; + } + } + } - log("", "Downloading signature:"); - if(!getFile(QString("%1%2.sig2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSign)) + 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, const 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++) + for (int i = 0; i < 2; i++) { - if(getFile(url, (i > 0), outFile, maxRedir, httpOk)) + if (getFile(url, (i > 0), outFile, maxRedir)) { return true; } + if (MUTILS_BOOLIFY(m_cancelled)) + { + break; /*cancelled*/ + } } return false; } -bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QString &outFile, const unsigned int maxRedir, bool *httpOk) +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; } @@ -587,18 +741,19 @@ bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QStri init_process(process, output.absolutePath()); QStringList args; - if(forceIp4) + if (forceIp4) { args << "-4"; } - args << "-T" << "15" << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir); + 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; @@ -606,40 +761,35 @@ bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QStri 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(25000); + 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 || MUTILS_BOOLIFY(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())); @@ -647,9 +797,89 @@ bool UpdateChecker::getFile(const QString &url, const bool forceIp4, const QStri return (process.exitCode() == 0) && output.exists() && output.isFile(); } +bool UpdateChecker::tryContactHost(const QString &hostname, const int &timeoutMsec, quint32 *const ipAddrOut) +{ + log(QString("Connecting to host: %1").arg(hostname), ""); + + QProcess process; + init_process(process, temp_folder()); + + QStringList args; + args << "--retry" << QString::number(3) << 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(readyRead()), &loop, SLOT(quit())); + + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + QScopedPointer ipAddr; + if (ipAddrOut) + { + *ipAddrOut = 0; + ipAddr.reset(new QRegExp("Connecting\\s+to\\s+(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+):(\\d+)", Qt::CaseInsensitive)); + } + + process.start(m_binaryMCat, args); + + if (!process.waitForStarted()) + { + return false; + } + + timer.start(timeoutMsec); + + while (process.state() != QProcess::NotRunning) + { + loop.exec(); + const bool bTimeOut = (!timer.isActive()); + while (process.canReadLine()) + { + const QString line = QString::fromLatin1(process.readLine()).simplified(); + if (!ipAddr.isNull()) + { + if (ipAddr->indexIn(line) >= 0) + { + quint32 values[4]; + if (MUtils::regexp_parse_uint32((*ipAddr), values, 4)) + { + *ipAddrOut |= ((values[0] & 0xFF) << 0x18); + *ipAddrOut |= ((values[1] & 0xFF) << 0x10); + *ipAddrOut |= ((values[2] & 0xFF) << 0x08); + *ipAddrOut |= ((values[3] & 0xFF) << 0x00); + } + } + } + log(line); + } + if (bTimeOut || MUTILS_BOOLIFY(m_cancelled)) + { + qWarning("MCat process timed out <-- killing!"); + process.kill(); + process.waitForFinished(); + log(bTimeOut ? "!!! TIMEOUT !!!" : "!!! CANCELLED !!!"); + return false; + } + } + + 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::checkSignature(const QString &file, const QString &signature) { - if(QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0) + if (QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0) { qWarning("CheckSignature: File and signature should be in same folder!"); return false; @@ -657,11 +887,11 @@ bool UpdateChecker::checkSignature(const QString &file, const QString &signature QString keyRingPath(m_binaryKeys); bool removeKeyring = false; - if(QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0) + if (QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0) { keyRingPath = make_temp_file(QFileInfo(file).absolutePath(), "gpg"); removeKeyring = true; - if(!QFile::copy(m_binaryKeys, keyRingPath)) + if (!QFile::copy(m_binaryKeys, keyRingPath)) { qWarning("CheckSignature: Failed to copy the key-ring file!"); return false; @@ -673,30 +903,30 @@ bool UpdateChecker::checkSignature(const QString &file, const QString &signature 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(keyRingPath).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName()); - if(!process.waitForStarted()) + if (!process.waitForStarted()) { - if(removeKeyring) + if (removeKeyring) { remove_file(keyRingPath); } return false; } - while(process.state() == QProcess::Running) + while (process.state() == QProcess::Running) { loop.exec(); - while(process.canReadLine()) + while (process.canReadLine()) { log(QString::fromLatin1(process.readLine()).simplified()); } } - - if(removeKeyring) + + if (removeKeyring) { remove_file(keyRingPath); } @@ -705,109 +935,6 @@ bool UpdateChecker::checkSignature(const QString &file, const QString &signature return (process.exitCode() == 0); } -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; -} - //////////////////////////////////////////////////////////// // SLOTS ////////////////////////////////////////////////////////////