2014-11-21 17:11:57 +01:00
///////////////////////////////////////////////////////////////////////////////
// MuldeR's Utilities for Qt
2017-01-06 23:17:56 +01:00
// Copyright (C) 2004-2017 LoRd_MuldeR <MuldeR2@GMX.de>
2014-11-21 17:11:57 +01:00
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// http://www.gnu.org/licenses/lgpl-2.1.txt
//////////////////////////////////////////////////////////////////////////////////
2014-11-21 17:54:46 +01:00
# include <MUtils/Global.h>
2014-11-21 19:42:39 +01:00
# include <MUtils/UpdateChecker.h>
# include <MUtils/OSSupport.h>
# include <MUtils/Exception.h>
2014-11-21 17:11:57 +01:00
# include <QStringList>
# include <QFile>
# include <QFileInfo>
# include <QProcess>
# include <QUrl>
# include <QEventLoop>
# include <QTimer>
2017-03-27 23:38:43 +02:00
# include <QElapsedTimer>
2017-05-11 21:38:17 +02:00
# include <QSet>
2014-11-21 17:11:57 +01:00
2017-11-26 15:22:37 +01:00
# include "Mirrors.h"
2014-11-21 17:11:57 +01:00
///////////////////////////////////////////////////////////////////////////////
// CONSTANTS
///////////////////////////////////////////////////////////////////////////////
static const char * header_id = " !Update " ;
static const char * mirror_url_postfix [ ] =
{
" update.ver " ,
" update_beta.ver " ,
NULL
} ;
2016-04-23 18:36:19 +02:00
static const int MIN_CONNSCORE = 5 ;
2017-03-29 21:39:05 +02:00
static const int QUICK_MIRRORS = 3 ;
2017-11-26 15:22:37 +01:00
static const int MAX_CONN_TIMEOUT = 16000 ;
2017-03-27 23:38:43 +02:00
static const int DOWNLOAD_TIMEOUT = 30000 ;
2014-11-21 17:11:57 +01:00
static const int VERSION_INFO_EXPIRES_MONTHS = 6 ;
2016-10-22 15:10:58 +02:00
static char * USER_AGENT_STR = " Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 " ; /*use something innocuous*/
2014-11-21 17:11:57 +01:00
2017-03-28 21:13:14 +02:00
# define CHECK_CANCELLED() do \
{ \
2017-04-19 23:51:17 +02:00
if ( MUTILS_BOOLIFY ( m_cancelled ) ) \
2017-03-28 21:13:14 +02:00
{ \
2017-04-19 23:51:17 +02:00
m_success . fetchAndStoreOrdered ( 0 ) ; \
2017-03-28 21:13:14 +02:00
log ( " " , " Update check has been cancelled by user! " ) ; \
setProgress ( m_maxProgress ) ; \
setStatus ( UpdateStatus_CancelledByUser ) ; \
return ; \
} \
} \
while ( 0 )
2016-04-23 18:36:19 +02:00
////////////////////////////////////////////////////////////
// Helper Functions
////////////////////////////////////////////////////////////
2014-11-21 17:11:57 +01:00
static int getMaxProgress ( void )
{
2017-03-29 21:39:05 +02:00
int counter = 0 ;
while ( update_mirrors [ counter ] )
{
counter + + ;
}
counter + = MIN_CONNSCORE + QUICK_MIRRORS + 2 ;
return counter ; ;
2014-11-21 17:11:57 +01:00
}
2016-04-23 13:25:01 +02:00
static QStringList buildRandomList ( const char * const values [ ] )
{
QStringList list ;
for ( int index = 0 ; values [ index ] ; index + + )
{
2017-11-26 15:22:37 +01:00
const int pos = MUtils : : next_rand_u32 ( ) % ( index + 1 ) ;
2016-04-23 13:25:01 +02:00
list . insert ( pos , QString : : fromLatin1 ( values [ index ] ) ) ;
}
return list ;
}
2014-11-21 17:11:57 +01:00
////////////////////////////////////////////////////////////
// Update Info Class
////////////////////////////////////////////////////////////
2017-11-26 15:22:37 +01:00
MUtils : : UpdateCheckerInfo : : UpdateCheckerInfo ( void )
2014-11-21 17:11:57 +01:00
{
resetInfo ( ) ;
}
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateCheckerInfo : : resetInfo ( void )
2014-11-21 17:11:57 +01:00
{
m_buildNo = 0 ;
m_buildDate . setDate ( 1900 , 1 , 1 ) ;
m_downloadSite . clear ( ) ;
m_downloadAddress . clear ( ) ;
m_downloadFilename . clear ( ) ;
m_downloadFilecode . clear ( ) ;
2015-08-16 16:27:43 +02:00
m_downloadChecksum . clear ( ) ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateCheckerInfo : : isComplete ( void )
2015-08-16 16:27:43 +02:00
{
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 ;
2014-11-21 17:11:57 +01:00
}
////////////////////////////////////////////////////////////
// Constructor & Destructor
////////////////////////////////////////////////////////////
2017-11-26 15:22:37 +01:00
MUtils : : 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 )
2014-11-21 17:11:57 +01:00
:
m_updateInfo ( new UpdateCheckerInfo ( ) ) ,
m_binaryWGet ( binWGet ) ,
2017-04-07 20:43:33 +02:00
m_binaryMCat ( binMCat ) ,
2014-11-21 17:11:57 +01:00
m_binaryGnuPG ( binGnuPG ) ,
m_binaryKeys ( binKeys ) ,
2014-11-21 20:56:36 +01:00
m_applicationId ( applicationId ) ,
2014-11-21 19:51:21 +01:00
m_installedBuildNo ( installedBuildNo ) ,
2014-11-21 17:11:57 +01:00
m_betaUpdates ( betaUpdates ) ,
m_testMode ( testMode ) ,
m_maxProgress ( getMaxProgress ( ) )
{
m_status = UpdateStatus_NotStartedYet ;
m_progress = 0 ;
if ( m_binaryWGet . isEmpty ( ) | | m_binaryGnuPG . isEmpty ( ) | | m_binaryKeys . isEmpty ( ) )
{
2014-11-21 19:42:39 +01:00
MUTILS_THROW ( " Tools not initialized correctly! " ) ;
2014-11-21 17:11:57 +01:00
}
}
2017-11-26 15:22:37 +01:00
MUtils : : UpdateChecker : : ~ UpdateChecker ( void )
2014-11-21 17:11:57 +01:00
{
}
2017-03-28 21:13:14 +02:00
////////////////////////////////////////////////////////////
// Public slots
////////////////////////////////////////////////////////////
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : start ( Priority priority )
2017-03-28 21:13:14 +02:00
{
2017-04-19 23:51:17 +02:00
m_success . fetchAndStoreOrdered ( 0 ) ;
2017-04-18 21:03:38 +02:00
m_cancelled . fetchAndStoreOrdered ( 0 ) ;
2017-03-28 21:13:14 +02:00
QThread : : start ( priority ) ;
}
2014-11-21 17:11:57 +01:00
////////////////////////////////////////////////////////////
// Protected functions
////////////////////////////////////////////////////////////
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : run ( void )
2014-11-21 17:11:57 +01:00
{
qDebug ( " Update checker thread started! " ) ;
2017-11-26 15:22:37 +01:00
MUTILS_EXCEPTION_HANDLER ( m_testMode ? testMirrorsList ( ) : checkForUpdates ( ) ) ;
2014-11-21 17:11:57 +01:00
qDebug ( " Update checker thread completed. " ) ;
}
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : checkForUpdates ( void )
2014-11-21 17:11:57 +01:00
{
// ----- Initialization ----- //
m_updateInfo - > resetInfo ( ) ;
setProgress ( 0 ) ;
// ----- Test Internet Connection ----- //
2016-04-23 13:25:01 +02:00
log ( " Checking internet connection... " , " " ) ;
2014-11-21 17:11:57 +01:00
setStatus ( UpdateStatus_CheckingConnection ) ;
2014-11-21 20:11:39 +01:00
const int networkStatus = OS : : network_status ( ) ;
if ( networkStatus = = OS : : NETWORK_TYPE_NON )
2014-11-21 17:11:57 +01:00
{
2016-04-23 13:25:01 +02:00
log ( " Operating system reports that the computer is currently offline !!! " ) ;
2014-11-21 17:11:57 +01:00
setProgress ( m_maxProgress ) ;
setStatus ( UpdateStatus_ErrorNoConnection ) ;
return ;
}
setProgress ( 1 ) ;
// ----- Test Known Hosts Connectivity ----- //
2017-03-28 21:13:14 +02:00
int connectionScore = 0 ;
2017-03-29 21:39:05 +02:00
QStringList mirrorList = buildRandomList ( known_hosts ) ;
2017-03-27 23:38:43 +02:00
2017-11-26 15:22:37 +01:00
for ( int connectionTimout = 500 ; connectionTimout < = MAX_CONN_TIMEOUT ; connectionTimout * = 2 )
2014-11-21 17:11:57 +01:00
{
2017-03-27 23:38:43 +02:00
QElapsedTimer elapsedTimer ;
elapsedTimer . start ( ) ;
2017-03-28 21:13:14 +02:00
const int globalTimout = 2 * MIN_CONNSCORE * connectionTimout ;
2017-03-27 23:38:43 +02:00
while ( ! elapsedTimer . hasExpired ( globalTimout ) )
2014-11-21 17:11:57 +01:00
{
2017-03-29 21:39:05 +02:00
const QString hostName = mirrorList . takeFirst ( ) ;
2017-03-27 23:38:43 +02:00
if ( tryContactHost ( hostName , connectionTimout ) )
{
connectionScore + = 1 ;
setProgress ( qBound ( 1 , connectionScore + 1 , MIN_CONNSCORE + 1 ) ) ;
elapsedTimer . restart ( ) ;
if ( connectionScore > = MIN_CONNSCORE )
{
2017-03-28 21:13:14 +02:00
goto endLoop ; /*success*/
2017-03-27 23:38:43 +02:00
}
}
else
{
2017-03-29 21:39:05 +02:00
mirrorList . append ( hostName ) ; /*re-schedule*/
2017-03-27 23:38:43 +02:00
}
2017-03-28 21:13:14 +02:00
CHECK_CANCELLED ( ) ;
2017-03-27 23:38:43 +02:00
msleep ( 1 ) ;
2014-11-21 17:11:57 +01:00
}
}
2017-03-28 21:13:14 +02:00
endLoop :
2014-11-21 17:11:57 +01:00
if ( connectionScore < MIN_CONNSCORE )
{
log ( " " , " Connectivity test has failed: Internet connection appears to be broken! " ) ;
setProgress ( m_maxProgress ) ;
setStatus ( UpdateStatus_ErrorConnectionTestFailed ) ;
return ;
}
2016-04-23 13:25:01 +02:00
// ----- Fetch Update Info From Server ----- //
2014-11-21 17:11:57 +01:00
2016-04-23 13:25:01 +02:00
log ( " ---- " , " " , " Checking for updates online... " ) ;
2014-11-21 17:11:57 +01:00
setStatus ( UpdateStatus_FetchingUpdates ) ;
2017-03-29 21:39:05 +02:00
int mirrorCount = 0 ;
mirrorList = buildRandomList ( update_mirrors ) ;
2014-11-21 17:11:57 +01:00
while ( ! mirrorList . isEmpty ( ) )
{
setProgress ( m_progress + 1 ) ;
2017-03-29 21:39:05 +02:00
const QString currentMirror = mirrorList . takeFirst ( ) ;
const bool isQuick = ( mirrorCount + + < QUICK_MIRRORS ) ;
if ( tryUpdateMirror ( m_updateInfo . data ( ) , currentMirror , isQuick ) )
2014-11-21 17:11:57 +01:00
{
2017-04-19 23:51:17 +02:00
m_success . ref ( ) ; /*success*/
2017-03-29 21:39:05 +02:00
break ;
2014-11-21 17:11:57 +01:00
}
2017-03-29 21:39:05 +02:00
if ( isQuick )
2014-11-21 17:11:57 +01:00
{
2017-03-29 21:39:05 +02:00
mirrorList . append ( currentMirror ) ; /*re-schedule*/
2014-11-21 17:11:57 +01:00
}
2017-03-29 21:39:05 +02:00
CHECK_CANCELLED ( ) ;
msleep ( 1 ) ;
2014-11-21 17:11:57 +01:00
}
2017-03-29 21:39:05 +02:00
while ( m_progress < m_maxProgress )
{
msleep ( 16 ) ;
setProgress ( m_progress + 1 ) ;
CHECK_CANCELLED ( ) ;
}
// ----- Generate final result ----- //
2014-11-21 17:11:57 +01:00
2017-04-19 23:51:17 +02:00
if ( MUTILS_BOOLIFY ( m_success ) )
2014-11-21 17:11:57 +01:00
{
2014-11-21 19:51:21 +01:00
if ( m_updateInfo - > m_buildNo > m_installedBuildNo )
2014-11-21 17:11:57 +01:00
{
setStatus ( UpdateStatus_CompletedUpdateAvailable ) ;
}
2014-11-21 19:51:21 +01:00
else if ( m_updateInfo - > m_buildNo = = m_installedBuildNo )
2014-11-21 17:11:57 +01:00
{
setStatus ( UpdateStatus_CompletedNoUpdates ) ;
}
else
{
setStatus ( UpdateStatus_CompletedNewVersionOlder ) ;
}
}
else
{
setStatus ( UpdateStatus_ErrorFetchUpdateInfo ) ;
}
}
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : testMirrorsList ( void )
2014-11-21 17:11:57 +01:00
{
2017-11-26 15:22:37 +01:00
// ----- Test update mirrors ----- //
QStringList mirrorList ;
for ( int i = 0 ; update_mirrors [ i ] ; i + + )
2014-11-21 17:11:57 +01:00
{
2017-11-26 15:22:37 +01:00
mirrorList < < QString : : fromLatin1 ( update_mirrors [ i ] ) ;
}
qDebug ( " \n [Known Hosts] " ) ;
log ( " Testing all known hosts... " , " " , " --- " ) ;
UpdateCheckerInfo updateInfo ;
while ( ! mirrorList . isEmpty ( ) )
{
const QString currentMirror = mirrorList . takeFirst ( ) ;
bool success = false ;
qDebug ( " Testing: %s " , MUTILS_L1STR ( currentMirror ) ) ;
log ( " " , " Testing: " , currentMirror , " " ) ;
for ( quint8 attempt = 0 ; attempt < 3 ; + + attempt )
{
updateInfo . resetInfo ( ) ;
if ( tryUpdateMirror ( & updateInfo , currentMirror , ( ! attempt ) ) )
{
success = true ;
break ;
}
}
if ( ! success )
{
qWarning ( " \n Update mirror seems to be unavailable: \n %s \n " , MUTILS_L1STR ( currentMirror ) ) ;
}
log ( " " , " --- " ) ;
}
// ----- Test known hosts ----- //
QStringList knownHostList ;
for ( int i = 0 ; known_hosts [ i ] ; i + + )
{
knownHostList < < QString : : fromLatin1 ( known_hosts [ i ] ) ;
2014-11-21 17:11:57 +01:00
}
qDebug ( " \n [Known Hosts] " ) ;
log ( " Testing all known hosts... " , " " , " --- " ) ;
2017-05-11 21:38:17 +02:00
QSet < quint32 > ipAddrSet ;
quint32 ipAddr ;
2017-11-26 15:22:37 +01:00
while ( ! knownHostList . isEmpty ( ) )
2014-11-21 17:11:57 +01:00
{
2017-11-26 15:22:37 +01:00
const QString currentHost = knownHostList . takeFirst ( ) ;
qDebug ( " Testing: %s " , MUTILS_L1STR ( currentHost ) ) ;
log ( QLatin1String ( " " ) , " Testing: " , currentHost , " " ) ;
2017-05-11 21:38:17 +02:00
if ( tryContactHost ( currentHost , DOWNLOAD_TIMEOUT , & ipAddr ) )
{
if ( ipAddrSet . contains ( ipAddr ) )
{
qWarning ( " Duplicate IP-address 0x%08X was encountered! " , ipAddr ) ;
}
else
{
ipAddrSet < < ipAddr ; /*not encountered yet*/
}
}
else
2014-11-21 17:11:57 +01:00
{
2017-11-26 15:22:37 +01:00
qWarning ( " \n Connectivity test FAILED on the following host: \n %s \n " , MUTILS_L1STR ( currentHost ) ) ;
2014-11-21 17:11:57 +01:00
}
log ( " " , " --- " ) ;
}
}
////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS
////////////////////////////////////////////////////////////
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : setStatus ( const int status )
2014-11-21 17:11:57 +01:00
{
if ( m_status ! = status )
{
m_status = status ;
emit statusChanged ( status ) ;
}
}
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : setProgress ( const int progress )
2014-11-21 17:11:57 +01:00
{
if ( m_progress ! = progress )
{
m_progress = progress ;
emit progressChanged ( progress ) ;
}
}
2017-11-26 15:22:37 +01:00
void MUtils : : UpdateChecker : : log ( const QString & str1 , const QString & str2 , const QString & str3 , const QString & str4 )
2014-11-21 17:11:57 +01:00
{
if ( ! str1 . isNull ( ) ) emit messageLogged ( str1 ) ;
if ( ! str2 . isNull ( ) ) emit messageLogged ( str2 ) ;
if ( ! str3 . isNull ( ) ) emit messageLogged ( str3 ) ;
if ( ! str4 . isNull ( ) ) emit messageLogged ( str4 ) ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : tryUpdateMirror ( UpdateCheckerInfo * updateInfo , const QString & url , const bool & quick )
2014-11-21 17:11:57 +01:00
{
bool success = false ;
2017-03-27 23:38:43 +02:00
log ( " " , " Trying mirror: " , url , " " ) ;
2017-11-26 15:22:37 +01:00
if ( quick )
2017-03-27 23:38:43 +02:00
{
2017-11-26 15:22:37 +01:00
if ( ! tryContactHost ( QUrl ( url ) . host ( ) , ( MAX_CONN_TIMEOUT / 10 ) ) )
{
log ( " " , " Mirror is too slow, skipping! " ) ;
return false ;
}
2017-03-27 23:38:43 +02:00
}
2014-11-21 17:11:57 +01:00
2016-12-16 19:23:35 +01:00
const QString randPart = next_rand_str ( ) ;
2014-11-21 17:54:46 +01:00
const QString outFileVers = QString ( " %1/%2.ver " ) . arg ( temp_folder ( ) , randPart ) ;
const QString outFileSign = QString ( " %1/%2.sig " ) . arg ( temp_folder ( ) , randPart ) ;
2014-11-21 17:11:57 +01:00
2017-03-27 23:38:43 +02:00
if ( getUpdateInfo ( url , outFileVers , outFileSign ) )
2014-11-21 17:11:57 +01:00
{
log ( " " , " Download okay, checking signature: " ) ;
2017-03-27 23:38:43 +02:00
if ( checkSignature ( outFileVers , outFileSign ) )
2014-11-21 17:11:57 +01:00
{
2017-04-07 20:43:33 +02:00
log ( " " , " Signature okay, parsing info: " , " " ) ;
2014-11-21 17:11:57 +01:00
success = parseVersionInfo ( outFileVers , updateInfo ) ;
}
else
{
log ( " " , " Bad signature, take care! " ) ;
}
}
else
{
log ( " " , " Download has failed! " ) ;
}
QFile : : remove ( outFileVers ) ;
QFile : : remove ( outFileSign ) ;
return success ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : getUpdateInfo ( const QString & url , const QString & outFileVers , const QString & outFileSign )
2014-11-21 17:11:57 +01:00
{
2017-04-07 20:43:33 +02:00
log ( " Downloading update info: " , " " ) ;
if ( getFile ( QString ( " %1%2 " ) . arg ( url , mirror_url_postfix [ m_betaUpdates ? 1 : 0 ] ) , outFileVers ) )
2014-11-21 17:11:57 +01:00
{
2017-04-07 20:43:33 +02:00
if ( ! m_cancelled )
{
log ( " " , " Downloading signature: " , " " ) ;
if ( getFile ( QString ( " %1%2.sig2 " ) . arg ( url , mirror_url_postfix [ m_betaUpdates ? 1 : 0 ] ) , outFileSign ) )
{
return true ;
}
}
2014-11-21 17:11:57 +01:00
}
2017-04-07 20:43:33 +02:00
return false ;
2014-11-21 17:11:57 +01:00
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : parseVersionInfo ( const QString & file , UpdateCheckerInfo * updateInfo )
2016-04-23 13:25:01 +02:00
{
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 ;
}
//----------------------------------------------------------
// EXTERNAL TOOLS
//----------------------------------------------------------
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : getFile ( const QString & url , const QString & outFile , const unsigned int maxRedir )
2015-08-24 21:49:15 +02:00
{
2016-04-23 13:25:01 +02:00
for ( int i = 0 ; i < 2 ; i + + )
2015-08-24 21:49:15 +02:00
{
2017-03-27 23:38:43 +02:00
if ( getFile ( url , ( i > 0 ) , outFile , maxRedir ) )
2015-08-24 21:49:15 +02:00
{
return true ;
}
2017-04-19 23:51:17 +02:00
if ( MUTILS_BOOLIFY ( m_cancelled ) )
2017-04-07 20:43:33 +02:00
{
break ; /*cancelled*/
}
2015-08-24 21:49:15 +02:00
}
return false ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : getFile ( const QString & url , const bool forceIp4 , const QString & outFile , const unsigned int maxRedir )
2014-11-21 17:11:57 +01:00
{
QFileInfo output ( outFile ) ;
output . setCaching ( false ) ;
2016-04-23 13:25:01 +02:00
if ( output . exists ( ) )
2014-11-21 17:11:57 +01:00
{
QFile : : remove ( output . canonicalFilePath ( ) ) ;
2016-04-23 13:25:01 +02:00
if ( output . exists ( ) )
2014-11-21 17:11:57 +01:00
{
return false ;
}
}
QProcess process ;
2014-11-21 17:54:46 +01:00
init_process ( process , output . absolutePath ( ) ) ;
2014-11-21 17:11:57 +01:00
QStringList args ;
2016-04-23 13:25:01 +02:00
if ( forceIp4 )
2015-08-24 21:49:15 +02:00
{
args < < " -4 " ;
}
2016-10-22 15:10:58 +02:00
args < < " --no-config " < < " --no-cache " < < " --no-dns-cache " < < " --no-check-certificate " < < " --no-hsts " ;
2017-03-27 23:38:43 +02:00
args < < QString ( ) . sprintf ( " --max-redirect=%u " , maxRedir ) < < QString ( ) . sprintf ( " --timeout=%u " , DOWNLOAD_TIMEOUT / 1000 ) ;
2014-11-21 17:11:57 +01:00
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 ( ) ) ) ;
2016-04-23 13:25:01 +02:00
connect ( & process , SIGNAL ( finished ( int , QProcess : : ExitStatus ) ) , & loop , SLOT ( quit ( ) ) ) ;
2014-11-21 17:11:57 +01:00
connect ( & process , SIGNAL ( readyRead ( ) ) , & loop , SLOT ( quit ( ) ) ) ;
QTimer timer ;
timer . setSingleShot ( true ) ;
connect ( & timer , SIGNAL ( timeout ( ) ) , & loop , SLOT ( quit ( ) ) ) ;
const QRegExp httpResponseOK ( " 200 OK$ " ) ;
2016-04-23 13:25:01 +02:00
2014-11-21 17:11:57 +01:00
process . start ( m_binaryWGet , args ) ;
2016-04-23 13:25:01 +02:00
if ( ! process . waitForStarted ( ) )
2014-11-21 17:11:57 +01:00
{
return false ;
}
2017-03-27 23:38:43 +02:00
timer . start ( DOWNLOAD_TIMEOUT ) ;
2014-11-21 17:11:57 +01:00
2016-04-23 13:25:01 +02:00
while ( process . state ( ) ! = QProcess : : NotRunning )
2014-11-21 17:11:57 +01:00
{
loop . exec ( ) ;
const bool bTimeOut = ( ! timer . isActive ( ) ) ;
2016-04-23 13:25:01 +02:00
while ( process . canReadLine ( ) )
2014-11-21 17:11:57 +01:00
{
2017-03-27 23:38:43 +02:00
const QString line = QString : : fromLatin1 ( process . readLine ( ) ) . simplified ( ) ;
2014-11-21 17:11:57 +01:00
log ( line ) ;
}
2017-04-19 23:51:17 +02:00
if ( bTimeOut | | MUTILS_BOOLIFY ( m_cancelled ) )
2014-11-21 17:11:57 +01:00
{
qWarning ( " WGet process timed out <-- killing! " ) ;
process . kill ( ) ;
process . waitForFinished ( ) ;
2017-03-28 21:13:14 +02:00
log ( bTimeOut ? " !!! TIMEOUT !!! " : " !!! CANCELLED !!! " ) ;
2014-11-21 17:11:57 +01:00
return false ;
}
}
2016-04-23 13:25:01 +02:00
2014-11-21 17:11:57 +01:00
timer . stop ( ) ;
timer . disconnect ( & timer , SIGNAL ( timeout ( ) ) , & loop , SLOT ( quit ( ) ) ) ;
log ( QString ( ) . sprintf ( " Exited with code %d " , process . exitCode ( ) ) ) ;
return ( process . exitCode ( ) = = 0 ) & & output . exists ( ) & & output . isFile ( ) ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : tryContactHost ( const QString & hostname , const int & timeoutMsec , quint32 * const ipAddrOut )
2016-04-23 13:25:01 +02:00
{
2017-04-07 20:43:33 +02:00
log ( QString ( " Connecting to host: %1 " ) . arg ( hostname ) , " " ) ;
2016-04-23 13:25:01 +02:00
QProcess process ;
init_process ( process , temp_folder ( ) ) ;
QStringList args ;
2017-04-07 20:43:33 +02:00
args < < " --retry " < < QString : : number ( 3 ) < < hostname < < QString : : number ( 80 ) ;
2016-04-23 13:25:01 +02:00
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 ( ) ) ) ;
2017-05-11 21:38:17 +02:00
QScopedPointer < QRegExp > ipAddr ;
if ( ipAddrOut )
{
* ipAddrOut = 0 ;
ipAddr . reset ( new QRegExp ( " Connecting \\ s+to \\ s+( \\ d+) \\ .( \\ d+) \\ .( \\ d+) \\ .( \\ d+):( \\ d+) " , Qt : : CaseInsensitive ) ) ;
}
2017-04-07 20:43:33 +02:00
process . start ( m_binaryMCat , args ) ;
2016-04-23 13:25:01 +02:00
if ( ! process . waitForStarted ( ) )
{
return false ;
}
2017-03-27 23:38:43 +02:00
timer . start ( timeoutMsec ) ;
2016-04-23 13:25:01 +02:00
while ( process . state ( ) ! = QProcess : : NotRunning )
{
loop . exec ( ) ;
const bool bTimeOut = ( ! timer . isActive ( ) ) ;
while ( process . canReadLine ( ) )
{
2017-05-11 21:38:17 +02:00
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 ) ;
}
}
}
2016-04-23 13:25:01 +02:00
log ( line ) ;
}
2017-04-19 23:51:17 +02:00
if ( bTimeOut | | MUTILS_BOOLIFY ( m_cancelled ) )
2016-04-23 13:25:01 +02:00
{
2017-04-07 20:43:33 +02:00
qWarning ( " MCat process timed out <-- killing! " ) ;
2016-04-23 13:25:01 +02:00
process . kill ( ) ;
process . waitForFinished ( ) ;
2017-03-28 21:13:14 +02:00
log ( bTimeOut ? " !!! TIMEOUT !!! " : " !!! CANCELLED !!! " ) ;
2016-04-23 13:25:01 +02:00
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 ) ;
}
2017-11-26 15:22:37 +01:00
bool MUtils : : UpdateChecker : : checkSignature ( const QString & file , const QString & signature )
2014-11-21 17:11:57 +01:00
{
2016-04-23 13:25:01 +02:00
if ( QFileInfo ( file ) . absolutePath ( ) . compare ( QFileInfo ( signature ) . absolutePath ( ) , Qt : : CaseInsensitive ) ! = 0 )
2014-11-21 17:11:57 +01:00
{
qWarning ( " CheckSignature: File and signature should be in same folder! " ) ;
return false ;
}
2015-02-01 21:03:28 +01:00
2015-08-31 22:56:39 +02:00
QString keyRingPath ( m_binaryKeys ) ;
bool removeKeyring = false ;
2016-04-23 13:25:01 +02:00
if ( QFileInfo ( file ) . absolutePath ( ) . compare ( QFileInfo ( m_binaryKeys ) . absolutePath ( ) , Qt : : CaseInsensitive ) ! = 0 )
2014-11-21 17:11:57 +01:00
{
2015-08-31 22:56:39 +02:00
keyRingPath = make_temp_file ( QFileInfo ( file ) . absolutePath ( ) , " gpg " ) ;
removeKeyring = true ;
2016-04-23 13:25:01 +02:00
if ( ! QFile : : copy ( m_binaryKeys , keyRingPath ) )
2015-08-31 22:56:39 +02:00
{
qWarning ( " CheckSignature: Failed to copy the key-ring file! " ) ;
return false ;
}
2014-11-21 17:11:57 +01:00
}
QProcess process ;
2014-11-21 17:54:46 +01:00
init_process ( process , QFileInfo ( file ) . absolutePath ( ) ) ;
2014-11-21 17:11:57 +01:00
QEventLoop loop ;
connect ( & process , SIGNAL ( error ( QProcess : : ProcessError ) ) , & loop , SLOT ( quit ( ) ) ) ;
2016-04-23 13:25:01 +02:00
connect ( & process , SIGNAL ( finished ( int , QProcess : : ExitStatus ) ) , & loop , SLOT ( quit ( ) ) ) ;
2014-11-21 17:11:57 +01:00
connect ( & process , SIGNAL ( readyRead ( ) ) , & loop , SLOT ( quit ( ) ) ) ;
2016-04-23 13:25:01 +02:00
2015-08-31 22:56:39 +02:00
process . start ( m_binaryGnuPG , QStringList ( ) < < " --homedir " < < " . " < < " --keyring " < < QFileInfo ( keyRingPath ) . fileName ( ) < < QFileInfo ( signature ) . fileName ( ) < < QFileInfo ( file ) . fileName ( ) ) ;
2014-11-21 17:11:57 +01:00
2016-04-23 13:25:01 +02:00
if ( ! process . waitForStarted ( ) )
2014-11-21 17:11:57 +01:00
{
2016-04-23 13:25:01 +02:00
if ( removeKeyring )
2015-08-31 22:56:39 +02:00
{
remove_file ( keyRingPath ) ;
}
2014-11-21 17:11:57 +01:00
return false ;
}
2016-04-23 13:25:01 +02:00
while ( process . state ( ) = = QProcess : : Running )
2014-11-21 17:11:57 +01:00
{
loop . exec ( ) ;
2016-04-23 13:25:01 +02:00
while ( process . canReadLine ( ) )
2014-11-21 17:11:57 +01:00
{
log ( QString : : fromLatin1 ( process . readLine ( ) ) . simplified ( ) ) ;
}
}
2016-04-23 13:25:01 +02:00
if ( removeKeyring )
2015-08-31 22:56:39 +02:00
{
remove_file ( keyRingPath ) ;
}
2014-11-21 17:11:57 +01:00
log ( QString ( ) . sprintf ( " Exited with code %d " , process . exitCode ( ) ) ) ;
return ( process . exitCode ( ) = = 0 ) ;
}
////////////////////////////////////////////////////////////
// SLOTS
////////////////////////////////////////////////////////////
/*NONE*/
////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////
/*NONE*/