2022-08-09 17:19:46 +01:00
# include "stdafx.h"
# include "OnlineUpdater.h"
# include "../MiscHelpers/Common/Common.h"
2022-11-20 16:23:15 +00:00
# include "../MiscHelpers/Common/OtherFunctions.h"
2022-08-09 17:19:46 +01:00
# include "SandMan.h"
# include "Windows/SettingsWindow.h"
# include <QUrlQuery>
# include <QJsonDocument>
2022-11-20 16:23:15 +00:00
# include <QJsonObject>
2022-08-09 17:19:46 +01:00
# include "../MiscHelpers/Common/CheckableMessageBox.h"
# include <QMessageBox>
2023-05-05 06:52:37 +01:00
# include "../../SandboxieTools/UpdUtil/UpdUtil.h"
2022-11-20 16:23:15 +00:00
# include <QCryptographicHash>
# include "Helpers/WinAdmin.h"
2022-08-09 17:19:46 +01:00
# include <windows.h>
2023-07-01 17:54:53 +01:00
# include <QRandomGenerator>
2022-08-09 17:19:46 +01:00
2022-11-20 16:23:15 +00:00
# ifdef _DEBUG
2022-08-09 17:19:46 +01:00
2022-11-20 16:23:15 +00:00
// mess with a dummy installation when debugging
# undef VERSION_MJR
# define VERSION_MJR 1
# undef VERSION_MIN
2023-07-01 17:54:53 +01:00
# define VERSION_MIN 9
2022-11-20 16:23:15 +00:00
# undef VERSION_REV
2023-08-15 08:49:03 +01:00
# define VERSION_REV 7
2022-11-20 16:23:15 +00:00
# undef VERSION_UPD
# define VERSION_UPD 0
2023-05-05 15:21:11 +01:00
# define DUMMY_PATH "C:\\Projects\\Sandboxie\\SandboxieTools\\x64\\Debug\\Test"
2022-11-20 16:23:15 +00:00
# endif
DWORD GetIdleTime ( ) // in seconds
2022-08-09 17:19:46 +01:00
{
2022-11-20 16:23:15 +00:00
LASTINPUTINFO lastInPut ;
GetLastInputInfo ( & lastInPut ) ;
return ( GetTickCount ( ) - lastInPut . dwTime ) / 1000 ;
2022-08-09 17:19:46 +01:00
}
2023-08-15 08:49:03 +01:00
COnlineUpdater : : COnlineUpdater ( QObject * parent ) : QObject ( parent )
2022-08-09 17:19:46 +01:00
{
2022-11-20 16:23:15 +00:00
m_IgnoredUpdates = theConf - > GetStringList ( " Options/IgnoredUpdates " ) ;
m_RequestManager = NULL ;
m_pUpdaterUtil = NULL ;
2023-08-15 08:49:03 +01:00
LoadState ( ) ;
}
void COnlineUpdater : : StartJob ( CUpdatesJob * pJob , const QUrl & Url )
{
if ( m_RequestManager = = NULL )
m_RequestManager = new CNetworkAccessManager ( 30 * 1000 , this ) ;
QNetworkRequest Request = QNetworkRequest ( Url ) ;
//Request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
Request . setAttribute ( QNetworkRequest : : RedirectPolicyAttribute , QNetworkRequest : : NoLessSafeRedirectPolicy ) ;
//Request.setRawHeader("Accept-Encoding", "gzip");
QNetworkReply * pReply = m_RequestManager - > get ( Request ) ;
connect ( pReply , SIGNAL ( finished ( ) ) , this , SLOT ( OnRequestFinished ( ) ) ) ;
connect ( pReply , SIGNAL ( downloadProgress ( qint64 , qint64 ) ) , pJob , SLOT ( OnDownloadProgress ( qint64 , qint64 ) ) ) ;
connect ( pJob - > m_pProgress . data ( ) , & CSbieProgress : : Canceled , pReply , & QNetworkReply : : abort ) ;
m_JobQueue . insert ( pReply , pJob ) ;
}
void COnlineUpdater : : OnRequestFinished ( )
{
QNetworkReply * pReply = qobject_cast < QNetworkReply * > ( sender ( ) ) ;
CUpdatesJob * pJob = m_JobQueue . take ( pReply ) ;
if ( pJob ) {
pJob - > Finish ( pReply ) ;
pJob - > deleteLater ( ) ;
}
pReply - > deleteLater ( ) ;
}
2023-10-20 16:01:26 +01:00
quint64 COnlineUpdater : : GetRandID ( )
{
quint64 RandID = 0 ;
theAPI - > GetSecureParam ( " RandID " , & RandID , sizeof ( RandID ) ) ;
if ( ! RandID ) {
RandID = QRandomGenerator64 : : global ( ) - > generate ( ) ;
theAPI - > SetSecureParam ( " RandID " , & RandID , sizeof ( RandID ) ) ;
}
return RandID ;
}
2023-08-15 08:49:03 +01:00
SB_PROGRESS COnlineUpdater : : GetUpdates ( QObject * receiver , const char * member , const QVariantMap & Params )
{
QUrlQuery Query ;
Query . addQueryItem ( " action " , " update " ) ;
Query . addQueryItem ( " software " , " sandboxie-plus " ) ;
//QString Branch = theConf->GetString("Options/ReleaseBranch");
//if (!Branch.isEmpty())
// Query.addQueryItem("branch", Branch);
//Query.addQueryItem("version", theGUI->GetVersion());
//Query.addQueryItem("version", QString::number(VERSION_MJR) + "." + QString::number(VERSION_MIN) + "." + QString::number(VERSION_REV) + "." + QString::number(VERSION_UPD));
# ifdef INSIDER_BUILD
Query . addQueryItem ( " version " , QString ( __DATE__ ) ) ;
# else
Query . addQueryItem ( " version " , QString : : number ( VERSION_MJR ) + " . " + QString : : number ( VERSION_MIN ) + " . " + QString : : number ( VERSION_REV ) ) ;
# endif
Query . addQueryItem ( " system " , " windows- " + QSysInfo : : kernelVersion ( ) + " - " + QSysInfo : : currentCpuArchitecture ( ) ) ;
Query . addQueryItem ( " language " , QLocale : : system ( ) . name ( ) ) ;
QString UpdateKey = GetArguments ( g_Certificate , L ' \n ' , L ' : ' ) . value ( " UPDATEKEY " ) ;
//if (UpdateKey.isEmpty())
// UpdateKey = theAPI->GetGlobalSettings()->GetText("UpdateKey"); // theConf->GetString("Options/UpdateKey");
//if (UpdateKey.isEmpty())
// UpdateKey = "00000000000000000000000000000000";
if ( ! UpdateKey . isEmpty ( ) )
UpdateKey + = " - " ;
2023-10-20 16:01:26 +01:00
quint64 RandID = COnlineUpdater : : GetRandID ( ) ;
2023-08-15 08:49:03 +01:00
quint32 Hash = theAPI - > GetUserSettings ( ) - > GetName ( ) . mid ( 13 ) . toInt ( NULL , 16 ) ;
quint64 HashID = RandID ^ ( quint64 ( ( Hash & 0xFFFF ) ^ ( ( Hash > > 16 ) & 0xFFFF ) ) < < 48 ) ; // fold the hash in half and xor it with the first 16 bit of RandID
UpdateKey + = QString : : number ( HashID , 16 ) . rightJustified ( 16 , ' 0 ' ) . toUpper ( ) ;
Query . addQueryItem ( " update_key " , UpdateKey ) ;
if ( Params . contains ( " channel " ) )
Query . addQueryItem ( " channel " , Params [ " channel " ] . toString ( ) ) ;
else {
QString ReleaseChannel = theConf - > GetString ( " Options/ReleaseChannel " , " stable " ) ;
Query . addQueryItem ( " channel " , ReleaseChannel ) ;
}
Query . addQueryItem ( " auto " , Params [ " manual " ] . toBool ( ) ? " 0 " : " 1 " ) ;
if ( ! Params [ " manual " ] . toBool ( ) ) {
int UpdateInterval = theConf - > GetInt ( " Options/UpdateInterval " , UPDATE_INTERVAL ) ; // in seconds
Query . addQueryItem ( " interval " , QString : : number ( UpdateInterval ) ) ;
}
# ifdef _DEBUG
QString Test = Query . toString ( ) ;
# endif
QUrl Url ( " https://sandboxie-plus.com/update.php " ) ;
Url . setQuery ( Query ) ;
CUpdatesJob * pJob = new CGetUpdatesJob ( Params , this ) ;
StartJob ( pJob , Url ) ;
QObject : : connect ( pJob , SIGNAL ( UpdateData ( const QVariantMap & , const QVariantMap & ) ) , receiver , member , Qt : : QueuedConnection ) ;
return SB_PROGRESS ( OP_ASYNC , pJob - > m_pProgress ) ;
}
void CGetUpdatesJob : : Finish ( QNetworkReply * pReply )
{
QByteArray Reply = pReply - > readAll ( ) ;
m_pProgress - > Finish ( SB_OK ) ;
QVariantMap Data = QJsonDocument : : fromJson ( Reply ) . toVariant ( ) . toMap ( ) ;
emit UpdateData ( Data , m_Params ) ;
}
SB_PROGRESS COnlineUpdater : : DownloadFile ( const QString & Url , QObject * receiver , const char * member , const QVariantMap & Params )
{
CUpdatesJob * pJob = new CGetFileJob ( Params , this ) ;
StartJob ( pJob , Url ) ;
QObject : : connect ( pJob , SIGNAL ( Download ( const QString & , const QVariantMap & ) ) , receiver , member , Qt : : QueuedConnection ) ;
return SB_PROGRESS ( OP_ASYNC , pJob - > m_pProgress ) ;
}
void CGetFileJob : : Finish ( QNetworkReply * pReply )
{
quint64 Size = pReply - > bytesAvailable ( ) ;
m_pProgress - > SetProgress ( - 1 ) ;
QString FilePath = m_Params [ " path " ] . toString ( ) ;
if ( FilePath . isEmpty ( ) ) {
QString Name = pReply - > request ( ) . url ( ) . fileName ( ) ;
if ( Name . isEmpty ( ) )
Name = " unnamed_download.tmp " ;
FilePath = ( ( COnlineUpdater * ) parent ( ) ) - > GetUpdateDir ( true ) + " / " + Name ;
}
QFile File ( FilePath ) ;
if ( File . open ( QFile : : WriteOnly ) ) {
while ( pReply - > bytesAvailable ( ) > 0 )
File . write ( pReply - > read ( 4096 ) ) ;
File . flush ( ) ;
QDateTime Date = m_Params [ " setDate " ] . toDateTime ( ) ;
if ( Date . isValid ( ) )
File . setFileTime ( Date , QFileDevice : : FileModificationTime ) ;
File . close ( ) ;
}
m_pProgress - > Finish ( SB_OK ) ;
if ( File . size ( ) ! = Size ) {
QMessageBox : : critical ( theGUI , " Sandboxie-Plus " , tr ( " Failed to download file from: %1 " ) . arg ( pReply - > request ( ) . url ( ) . toString ( ) ) ) ;
return ;
}
emit Download ( FilePath , m_Params ) ;
}
SB_PROGRESS COnlineUpdater : : GetSupportCert ( const QString & Serial , QObject * receiver , const char * member , const QVariantMap & Params )
{
QString UpdateKey = GetArguments ( g_Certificate , L ' \n ' , L ' : ' ) . value ( " UPDATEKEY " ) ;
QUrlQuery Query ;
if ( ! Serial . isEmpty ( ) ) {
Query . addQueryItem ( " SN " , Serial ) ;
if ( Serial . length ( ) > 5 & & Serial . at ( 4 ) . toUpper ( ) = = ' N ' ) { // node locked business use
wchar_t uuid_str [ 40 ] ;
theAPI - > GetDriverInfo ( - 2 , uuid_str , sizeof ( uuid_str ) ) ;
Query . addQueryItem ( " HwId " , QString : : fromWCharArray ( uuid_str ) ) ;
}
}
if ( ! UpdateKey . isEmpty ( ) )
Query . addQueryItem ( " UpdateKey " , UpdateKey ) ;
# ifdef _DEBUG
QString Test = Query . toString ( ) ;
# endif
QUrl Url ( " https://sandboxie-plus.com/get_cert.php " ) ;
Url . setQuery ( Query ) ;
CUpdatesJob * pJob = new CGetCertJob ( Params , this ) ;
StartJob ( pJob , Url ) ;
QObject : : connect ( pJob , SIGNAL ( Certificate ( const QByteArray & , const QVariantMap & ) ) , receiver , member , Qt : : QueuedConnection ) ;
return SB_PROGRESS ( OP_ASYNC , pJob - > m_pProgress ) ;
}
void CGetCertJob : : Finish ( QNetworkReply * pReply )
{
QByteArray Reply = pReply - > readAll ( ) ;
m_pProgress - > Finish ( SB_OK ) ;
if ( Reply . left ( 1 ) = = " { " ) { // error
QVariantMap Data = QJsonDocument : : fromJson ( Reply ) . toVariant ( ) . toMap ( ) ;
Reply . clear ( ) ;
m_Params [ " error " ] = Data [ " errorMsg " ] . toString ( ) ;
}
emit Certificate ( Reply , m_Params ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Update Handling
//
void COnlineUpdater : : LoadState ( )
{
2022-11-20 16:23:15 +00:00
m_CheckMode = eInit ;
2022-12-16 14:26:23 +00:00
int iUpdate = 0 ;
QString UpdateStr = ParseVersionStr ( theConf - > GetString ( " Updater/PendingUpdate " ) , & iUpdate ) ;
if ( ! IsVersionNewer ( UpdateStr ) & & ( UpdateStr ! = GetCurrentVersion ( ) | | iUpdate < = GetCurrentUpdate ( ) ) )
2022-12-17 20:06:15 +00:00
theConf - > SetValue ( " Updater/PendingUpdate " , " " ) ; // it seems update has been applied
2022-12-16 14:26:23 +00:00
2022-11-20 16:23:15 +00:00
bool bIsInstallerReady = false ;
QString FilePath = theConf - > GetString ( " Updater/InstallerPath " ) ;
if ( ! FilePath . isEmpty ( ) & & QFile : : exists ( FilePath ) ) {
QString ReleaseStr = ParseVersionStr ( theConf - > GetString ( " Updater/InstallerVersion " ) ) ;
if ( IsVersionNewer ( ReleaseStr ) ) {
bIsInstallerReady = true ;
}
}
2023-09-02 08:10:24 +01:00
QString OnNewRelease = GetOnNewReleaseOption ( ) ;
2022-11-20 16:23:15 +00:00
bool bCanRunInstaller = OnNewRelease = = " install " ;
bool bIsUpdateReady = false ;
2023-02-02 08:03:01 +00:00
QVariantMap Update = QJsonDocument : : fromJson ( ReadFileAsString ( GetUpdateDir ( ) + " / " UPDATE_FILE ) . toUtf8 ( ) ) . toVariant ( ) . toMap ( ) ;
2022-11-20 16:23:15 +00:00
if ( ! Update . isEmpty ( ) ) {
int iUpdate = 0 ;
QString UpdateStr = ParseVersionStr ( theConf - > GetString ( " Updater/UpdateVersion " ) , & iUpdate ) ;
if ( IsVersionNewer ( UpdateStr ) | | ( UpdateStr = = GetCurrentVersion ( ) & & iUpdate > GetCurrentUpdate ( ) ) ) {
if ( ScanUpdateFiles ( Update ) = = eNone ) // check if this update has already been applied
theConf - > SetValue ( " Updater/CurrentUpdate " , MakeVersionStr ( Update ) ) ; // cache result
else
bIsUpdateReady = true ;
}
}
2023-09-02 08:10:24 +01:00
QString OnNewUpdate = GetOnNewUpdateOption ( ) ;
2022-11-20 16:23:15 +00:00
bool bCanApplyUpdate = OnNewUpdate = = " install " ;
if ( bIsInstallerReady & & bCanRunInstaller )
m_CheckMode = ePendingInstall ;
else if ( bIsUpdateReady & & bCanApplyUpdate )
m_CheckMode = ePendingUpdate ;
2022-08-09 17:19:46 +01:00
}
2023-09-02 08:10:24 +01:00
QString COnlineUpdater : : GetOnNewUpdateOption ( ) const
{
2023-10-26 15:27:08 +01:00
QString ReleaseChannel = theConf - > GetString ( " Options/ReleaseChannel " , " stable " ) ;
2023-11-29 20:12:46 +00:00
if ( ReleaseChannel ! = " preview " & & ( ! g_CertInfo . active | | g_CertInfo . expired ) ) // allow revisions for preview channel
2023-10-21 13:10:45 +01:00
return " ignore " ; // this service requires a valid certificate
2023-09-02 08:10:24 +01:00
return theConf - > GetString ( " Options/OnNewUpdate " , " ignore " ) ;
}
QString COnlineUpdater : : GetOnNewReleaseOption ( ) const
{
QString OnNewRelease = theConf - > GetString ( " Options/OnNewRelease " , " download " ) ;
if ( ( g_CertInfo . active & & g_CertInfo . expired ) & & OnNewRelease = = " install " )
return " download " ; // disable auto update on an active but expired personal certificate
return OnNewRelease ;
}
bool COnlineUpdater : : ShowCertWarningIfNeeded ( )
{
//
2023-10-21 13:10:45 +01:00
// This function checks if this installation uses a expired personal
2023-09-02 08:10:24 +01:00
// certificate which is active for the current build
2023-10-21 13:10:45 +01:00
// in which case it shows a warning that updating to the latest build
2023-09-02 08:10:24 +01:00
// will deactivate the certificate
//
if ( ! ( g_CertInfo . active & & g_CertInfo . expired ) )
return true ;
2023-09-29 19:33:27 +01:00
QString Message = tr ( " Your Sandboxie-Plus supporter certificate is expired, however for the current build you are using it remains active, when you update to a newer build exclusive supporter features will be disabled. \n \n "
2023-09-02 08:10:24 +01:00
" Do you still want to update? " ) ;
int Ret = QMessageBox ( " Sandboxie-Plus " , Message , QMessageBox : : Warning , QMessageBox : : Yes , QMessageBox : : No | QMessageBox : : Escape | QMessageBox : : Default , QMessageBox : : Cancel , theGUI ) . exec ( ) ;
if ( Ret = = QMessageBox : : Cancel ) {
QTimer : : singleShot ( 10 , this , [ = ] {
theConf - > DelValue ( " Updater/InstallerPath " ) ;
theConf - > DelValue ( " Updater/UpdateVersion " ) ;
theGUI - > UpdateLabel ( ) ;
} ) ;
}
return Ret = = QMessageBox : : Yes ;
}
2022-08-09 17:19:46 +01:00
void COnlineUpdater : : Process ( )
{
2022-11-20 16:23:15 +00:00
int iCheckUpdates = theConf - > GetInt ( " Options/CheckForUpdates " , 2 ) ;
if ( iCheckUpdates ! = 0 )
{
time_t NextUpdateCheck = theConf - > GetUInt64 ( " Options/NextCheckForUpdates " , 0 ) ;
2023-07-02 09:22:38 +01:00
if ( NextUpdateCheck = = 0 ) // no check made yet
theConf - > SetValue ( " Options/NextCheckForUpdates " , QDateTime : : currentDateTime ( ) . addDays ( 7 ) . toSecsSinceEpoch ( ) ) ;
2022-11-20 16:23:15 +00:00
else if ( QDateTime : : currentDateTime ( ) . toSecsSinceEpoch ( ) > = NextUpdateCheck )
{
if ( iCheckUpdates = = 2 )
{
bool bCheck = false ;
iCheckUpdates = CCheckableMessageBox : : question ( theGUI , " Sandboxie-Plus " , tr ( " Do you want to check if there is a new version of Sandboxie-Plus? " )
, tr ( " Don't show this message again. " ) , & bCheck , QDialogButtonBox : : Yes | QDialogButtonBox : : No , QDialogButtonBox : : Yes , QMessageBox : : Information ) = = QDialogButtonBox : : Ok ? 1 : 0 ;
2022-08-09 17:19:46 +01:00
2022-11-20 16:23:15 +00:00
if ( bCheck )
theConf - > SetValue ( " Options/CheckForUpdates " , iCheckUpdates ) ;
}
2023-07-02 09:22:38 +01:00
if ( iCheckUpdates = = 0 ) // no clicked on prompt
2022-11-20 16:23:15 +00:00
theConf - > SetValue ( " Options/NextCheckForUpdates " , QDateTime : : currentDateTime ( ) . addDays ( 7 ) . toSecsSinceEpoch ( ) ) ;
else
{
2023-07-02 09:22:38 +01:00
// schedule next check in 12 h in case this one fails
theConf - > SetValue ( " Options/NextCheckForUpdates " , QDateTime : : currentDateTime ( ) . addSecs ( 12 * 60 * 60 ) . toSecsSinceEpoch ( ) ) ;
2022-11-20 16:23:15 +00:00
CheckForUpdates ( false ) ;
}
}
}
if ( m_CheckMode = = ePendingUpdate | | m_CheckMode = = ePendingInstall )
{
// When auto install/apply is active wait for the user to be idle
# ifndef _DEBUG
if ( GetIdleTime ( ) > theConf - > GetInt ( " Options/UpdateIdleTime " , 30 * 60 ) ) // default 30 minutes
# endif
// and wait for no processes running in the boxes
if ( theAPI - > IsConnected ( ) & & theAPI - > GetAllProcesses ( ) . isEmpty ( ) )
{
if ( m_CheckMode = = ePendingUpdate )
ApplyUpdate ( true ) ;
else if ( m_CheckMode = = ePendingInstall )
RunInstaller ( true ) ;
m_CheckMode = eInit ;
2022-08-09 17:19:46 +01:00
}
}
}
2022-11-20 16:23:15 +00:00
void COnlineUpdater : : CheckForUpdates ( bool bManual )
2022-06-08 16:23:19 +01:00
{
2023-08-15 08:49:03 +01:00
if ( m_CheckMode = = eManual | | m_CheckMode = = eAuto )
return ; // already in progress
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
# ifdef _DEBUG
if ( QApplication : : keyboardModifiers ( ) & Qt : : ControlModifier )
bManual = false ;
# endif
// clean up old check result
m_UpdateData . clear ( ) ;
m_CheckMode = bManual ? eManual : eAuto ;
2022-08-11 23:09:35 +01:00
2023-08-15 08:49:03 +01:00
QVariantMap Params ;
SB_PROGRESS Status = GetUpdates ( this , SLOT ( OnUpdateData ( const QVariantMap & , const QVariantMap & ) ) , Params ) ;
if ( bManual & & Status . GetStatus ( ) = = OP_ASYNC ) {
theGUI - > AddAsyncOp ( Status . GetValue ( ) ) ;
Status . GetValue ( ) - > ShowMessage ( tr ( " Checking for updates... " ) ) ;
}
2022-11-20 16:23:15 +00:00
}
2022-08-11 23:09:35 +01:00
2022-11-20 16:23:15 +00:00
void COnlineUpdater : : OnUpdateData ( const QVariantMap & Data , const QVariantMap & Params )
{
if ( Data . isEmpty ( ) | | Data [ " error " ] . toBool ( ) ) {
2022-06-08 16:23:19 +01:00
QString Error = Data . isEmpty ( ) ? tr ( " server not reachable " ) : Data [ " errorMsg " ] . toString ( ) ;
2022-11-20 16:23:15 +00:00
theGUI - > OnLogMessage ( tr ( " Failed to check for updates, error: %1 " ) . arg ( Error ) , m_CheckMode ! = eManual ) ;
if ( m_CheckMode = = eManual )
2022-08-09 17:19:46 +01:00
QMessageBox : : critical ( theGUI , " Sandboxie-Plus " , tr ( " Failed to check for updates, error: %1 " ) . arg ( Error ) ) ;
2023-08-15 08:49:03 +01:00
m_CheckMode = eInit ;
2022-06-08 16:23:19 +01:00
return ;
}
bool bNothing = true ;
2022-11-20 16:23:15 +00:00
if ( HandleUserMessage ( Data ) )
bNothing = false ;
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
m_UpdateData = Data ;
2023-07-01 20:24:12 +01:00
m_LastUpdate = QDateTime : : currentDateTime ( ) ;
2022-11-20 16:23:15 +00:00
bool PendingUpdate = HandleUpdate ( ) ;
theGUI - > UpdateLabel ( ) ;
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
if ( PendingUpdate ) {
bNothing = false ;
}
2022-06-08 16:23:19 +01:00
2023-07-02 09:22:38 +01:00
if ( m_CheckMode ! = eManual ) {
int UpdateInterval = theConf - > GetInt ( " Options/UpdateInterval " , UPDATE_INTERVAL ) ; // in seconds
theConf - > SetValue ( " Options/NextCheckForUpdates " , QDateTime : : currentDateTime ( ) . addSecs ( UpdateInterval ) . toSecsSinceEpoch ( ) ) ;
}
else if ( bNothing ) {
QMessageBox : : information ( theGUI , " Sandboxie-Plus " , tr ( " No new updates found, your Sandboxie-Plus is up-to-date. \n "
" \n Note: The update check is often behind the latest GitHub release to ensure that only tested updates are offered. " ) ) ;
2022-11-20 16:23:15 +00:00
}
}
bool COnlineUpdater : : HandleUpdate ( )
{
2022-12-16 14:26:23 +00:00
QString PendingUpdate ;
2023-12-03 19:05:40 +00:00
QString OnNewRelease = GetOnNewReleaseOption ( ) ;
2022-11-20 16:23:15 +00:00
bool bNewRelease = false ;
QVariantMap Release = m_UpdateData [ " release " ] . toMap ( ) ;
QString ReleaseStr = Release [ " version " ] . toString ( ) ;
if ( IsVersionNewer ( ReleaseStr ) ) {
if ( m_CheckMode = = eManual | | ! m_IgnoredUpdates . contains ( ReleaseStr ) ) {
2022-12-16 14:26:23 +00:00
PendingUpdate = ReleaseStr ;
2022-11-20 16:23:15 +00:00
bNewRelease = true ;
}
}
2023-09-02 08:10:24 +01:00
QString OnNewUpdate = GetOnNewUpdateOption ( ) ;
2022-11-20 16:23:15 +00:00
bool bNewUpdate = false ;
QVariantMap Update = m_UpdateData [ " update " ] . toMap ( ) ;
QString UpdateStr = Update [ " version " ] . toString ( ) ;
bool bNewer ;
if ( ( bNewer = IsVersionNewer ( UpdateStr ) ) | | UpdateStr = = GetCurrentVersion ( ) ) {
int iUpdate = Update [ " update " ] . toInt ( ) ;
2022-11-20 17:16:13 +00:00
if ( iUpdate ) UpdateStr + = QChar ( ' a ' + ( iUpdate - 1 ) ) ;
2022-11-20 16:23:15 +00:00
if ( bNewer | | iUpdate > GetCurrentUpdate ( ) ) {
if ( ScanUpdateFiles ( Update ) = = eNone ) // check if this update has already been applied
theConf - > SetValue ( " Updater/CurrentUpdate " , MakeVersionStr ( Update ) ) ; // cache result
2022-12-20 08:18:21 +00:00
else if ( OnNewUpdate ! = " ignore " )
2022-06-08 16:23:19 +01:00
{
2022-12-16 14:26:23 +00:00
if ( PendingUpdate . isEmpty ( ) )
PendingUpdate = UpdateStr ;
2022-11-20 16:23:15 +00:00
if ( m_CheckMode = = eManual | | ! m_IgnoredUpdates . contains ( UpdateStr ) ) {
bNewUpdate = true ;
}
2022-06-08 16:23:19 +01:00
}
}
}
2022-12-16 14:26:23 +00:00
theConf - > SetValue ( " Updater/PendingUpdate " , PendingUpdate ) ;
2022-11-20 16:23:15 +00:00
//
2022-12-17 20:06:15 +00:00
// special case: updates allowed be to installed, but releases only allowed to be downloaded
2022-11-20 16:23:15 +00:00
// solution: apply updates silently, then prompt to install new release, else prioritize installing new releases over updating the existing one
//
2022-08-09 17:19:46 +01:00
2023-12-03 19:05:40 +00:00
bool bAllowAuto = g_CertInfo . active & & ! g_CertInfo . expired ; // To use automatic updates a valid certificate is required
//
// if we allow for version updates but not for automatic instalation/download of new release
// ignore the release and install it using the version updater
//
if ( bNewUpdate & & bNewRelease & & ! bAllowAuto )
bNewRelease = false ;
2022-11-20 16:23:15 +00:00
bool bCanRunInstaller = ( m_CheckMode = = eAuto & & OnNewRelease = = " install " ) ;
bool bIsInstallerReady = false ;
if ( bNewRelease )
2022-06-08 16:23:19 +01:00
{
2022-11-20 16:23:15 +00:00
if ( theConf - > GetString ( " Updater/InstallerVersion " ) = = MakeVersionStr ( Release ) )
2022-06-08 16:23:19 +01:00
{
2022-11-20 16:23:15 +00:00
QString FilePath = theConf - > GetString ( " Updater/InstallerPath " ) ;
bIsInstallerReady = ( ! FilePath . isEmpty ( ) & & QFile : : exists ( FilePath ) ) ;
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
if ( ! bIsInstallerReady )
{
// clear when not up to date
theConf - > DelValue ( " Updater/InstallerVersion " ) ;
2022-06-08 16:23:19 +01:00
2023-12-03 19:05:40 +00:00
if ( ( bAllowAuto & & ( bCanRunInstaller | | ( m_CheckMode = = eAuto & & OnNewRelease = = " download " ) ) ) | | AskDownload ( Release , bAllowAuto ) )
2022-08-22 20:06:43 +01:00
{
2022-11-20 16:23:15 +00:00
if ( DownloadInstaller ( Release , m_CheckMode = = eManual ) )
return true ;
2022-08-22 20:06:43 +01:00
}
2022-11-20 16:23:15 +00:00
}
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
bool bCanApplyUpdate = ( m_CheckMode = = eAuto & & OnNewUpdate = = " install " ) ;
2022-12-03 19:25:04 +00:00
if ( bNewUpdate )
2022-11-20 16:23:15 +00:00
{
2022-12-20 08:18:21 +00:00
if ( ( ! bNewRelease | | ( bCanApplyUpdate & & ! bCanRunInstaller ) ) )
2022-11-20 16:23:15 +00:00
{
2022-12-03 19:25:04 +00:00
bool bIsUpdateReady = false ;
if ( theConf - > GetString ( " Updater/UpdateVersion " ) = = MakeVersionStr ( Update ) )
2023-02-02 08:03:01 +00:00
bIsUpdateReady = QFile : : exists ( GetUpdateDir ( ) + " / " UPDATE_FILE ) ;
2022-06-08 16:23:19 +01:00
2022-12-03 19:25:04 +00:00
if ( ! bIsUpdateReady )
2022-11-20 16:23:15 +00:00
{
2022-12-03 19:25:04 +00:00
// clear when not up to date
theConf - > DelValue ( " Updater/UpdateVersion " ) ;
2023-12-03 19:05:40 +00:00
if ( ( bCanApplyUpdate | | ( m_CheckMode = = eAuto & & OnNewUpdate = = " download " ) ) | | AskDownload ( Update , true ) )
2022-12-03 19:25:04 +00:00
{
if ( DownloadUpdate ( Update , m_CheckMode = = eManual ) )
return true ;
}
}
else if ( m_CheckMode = = eManual ) {
if ( ApplyUpdate ( false ) )
2022-11-20 16:23:15 +00:00
return true ;
2022-06-08 16:23:19 +01:00
}
2022-12-03 19:25:04 +00:00
else if ( bCanApplyUpdate )
m_CheckMode = ePendingUpdate ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
}
if ( bIsInstallerReady )
{
if ( m_CheckMode = = eManual ) {
if ( RunInstaller ( false ) )
return true ;
}
else if ( bCanRunInstaller )
m_CheckMode = ePendingInstall ;
}
2023-08-15 08:49:03 +01:00
if ( m_CheckMode ! = ePendingUpdate & & m_CheckMode ! = ePendingInstall )
m_CheckMode = eInit ;
2022-11-20 16:23:15 +00:00
return bNewRelease | | bNewUpdate ;
}
2023-12-03 19:05:40 +00:00
bool COnlineUpdater : : AskDownload ( const QVariantMap & Data , bool bAuto )
2022-11-20 16:23:15 +00:00
{
QString VersionStr = MakeVersionStr ( Data ) ;
QString UpdateMsg = Data [ " infoMsg " ] . toString ( ) ;
QString UpdateUrl = Data [ " infoUrl " ] . toString ( ) ;
QString FullMessage = ! UpdateMsg . isEmpty ( ) ? UpdateMsg :
tr ( " <p>There is a new version of Sandboxie-Plus available.<br /><font color='red'><b>New version:</b></font> <b>%1</b></p> " ) . arg ( VersionStr ) ;
QVariantMap Installer = Data [ " installer " ] . toMap ( ) ;
QString DownloadUrl = Installer [ " downloadUrl " ] . toString ( ) ;
2023-12-03 19:05:40 +00:00
enum EAction
{
eNone = 0 ,
eDownload ,
eNotify ,
} Action = eNone ;
if ( bAuto & & ! DownloadUrl . isEmpty ( ) ) {
Action = eDownload ;
2022-11-20 16:23:15 +00:00
FullMessage + = tr ( " <p>Do you want to download the installer?</p> " ) ;
2023-12-03 19:05:40 +00:00
}
else if ( bAuto & & Data . contains ( " files " ) ) {
Action = eDownload ;
2022-11-20 16:23:15 +00:00
FullMessage + = tr ( " <p>Do you want to download the updates?</p> " ) ;
2023-12-03 19:05:40 +00:00
}
else if ( ! UpdateUrl . isEmpty ( ) ) {
Action = eNotify ;
FullMessage + = tr ( " <p>Do you want to go to the <a href= \" %1 \" >download page</a>?</p> " ) . arg ( UpdateUrl ) ;
}
2022-11-20 16:23:15 +00:00
CCheckableMessageBox mb ( theGUI ) ;
mb . setWindowTitle ( " Sandboxie-Plus " ) ;
QIcon ico ( QLatin1String ( " :/SandMan.png " ) ) ;
mb . setIconPixmap ( ico . pixmap ( 64 , 64 ) ) ;
//mb.setTextFormat(Qt::RichText);
mb . setText ( FullMessage ) ;
mb . setCheckBoxText ( tr ( " Don't show this update anymore. " ) ) ;
mb . setCheckBoxVisible ( m_CheckMode ! = eManual ) ;
2023-12-03 19:05:40 +00:00
if ( Action ! = eNone ) {
2023-04-07 16:22:54 +01:00
mb . setStandardButtons ( QDialogButtonBox : : Yes | QDialogButtonBox : : No | QDialogButtonBox : : Cancel ) ;
2022-11-20 16:23:15 +00:00
mb . setDefaultButton ( QDialogButtonBox : : Yes ) ;
2023-12-03 19:05:40 +00:00
} else
2022-11-20 16:23:15 +00:00
mb . setStandardButtons ( QDialogButtonBox : : Ok ) ;
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
mb . exec ( ) ;
if ( mb . clickedStandardButton ( ) = = QDialogButtonBox : : Yes )
2022-06-08 16:23:19 +01:00
{
2023-12-03 19:05:40 +00:00
if ( Action = = eDownload )
{
2022-11-20 16:23:15 +00:00
m_CheckMode = eManual ;
return true ;
}
else
QDesktopServices : : openUrl ( UpdateUrl ) ;
}
2023-04-07 16:22:54 +01:00
else
{
2023-12-03 19:05:40 +00:00
if ( mb . clickedStandardButton ( ) = = QDialogButtonBox : : Cancel )
{
2023-04-07 16:22:54 +01:00
theConf - > SetValue ( " Updater/PendingUpdate " , " " ) ;
theGUI - > UpdateLabel ( ) ;
}
if ( mb . isChecked ( ) )
theConf - > SetValue ( " Options/IgnoredUpdates " , m_IgnoredUpdates < < VersionStr ) ;
}
2022-11-20 16:23:15 +00:00
return false ;
}
COnlineUpdater : : EUpdateScope COnlineUpdater : : GetFileScope ( const QString & Path )
{
static const WCHAR CoreFiles [ ] = SCOPE_CORE_FILES ;
static const WCHAR LangFiles [ ] = SCOPE_LANG_FILES ;
static const WCHAR TmplFiles [ ] = SCOPE_TMPL_FILES ;
auto WildMatch = [ Path ] ( const WCHAR * pFiles ) {
for ( const WCHAR * pFile = pFiles ; * pFile ; pFile + = wcslen ( pFile ) + 1 ) {
QString WC = QRegularExpression : : wildcardToRegularExpression ( QString : : fromWCharArray ( pFile ) ) ;
QRegularExpression RegExp ( WC , QRegularExpression : : CaseInsensitiveOption ) ;
if ( RegExp . match ( Path ) . hasMatch ( ) )
return true ;
}
return false ;
} ;
if ( WildMatch ( CoreFiles ) )
return eCore ;
if ( WildMatch ( TmplFiles ) | | WildMatch ( LangFiles ) )
return eMeta ;
2022-11-21 18:22:33 +00:00
// unknown files are handled the same as known Plus files
2022-11-20 16:23:15 +00:00
return eFull ;
}
COnlineUpdater : : EUpdateScope COnlineUpdater : : ScanUpdateFiles ( const QVariantMap & Update )
{
QString AppDir = QApplication : : applicationDirPath ( ) ;
# ifdef DUMMY_PATH
AppDir = DUMMY_PATH ;
# endif
EUpdateScope Scope = eNone ;
foreach ( const QVariant vFile , Update [ " files " ] . toList ( ) ) {
QVariantMap File = vFile . toMap ( ) ;
QCryptographicHash qHash ( QCryptographicHash : : Sha256 ) ;
QFile qFile ( AppDir + " \\ " + File [ " path " ] . toString ( ) ) ;
if ( qFile . open ( QFile : : ReadOnly ) ) {
qHash . addData ( & qFile ) ;
qFile . close ( ) ;
}
if ( qHash . result ( ) = = QByteArray : : fromHex ( File [ " hash " ] . toByteArray ( ) ) )
continue ; // file did not change
EUpdateScope CurScope = GetFileScope ( File [ " path " ] . toString ( ) ) ;
if ( Scope < CurScope )
Scope = CurScope ;
}
return Scope ;
}
bool COnlineUpdater : : DownloadUpdate ( const QVariantMap & Update , bool bAndApply )
{
QJsonDocument doc ( QJsonValue : : fromVariant ( Update ) . toObject ( ) ) ;
WriteStringToFile ( GetUpdateDir ( true ) + " / " UPDATE_FILE , doc . toJson ( ) ) ;
theConf - > DelValue ( " Updater/UpdateVersion " ) ;
QStringList Params ;
Params . append ( " update " ) ;
Params . append ( " sandboxie-plus " ) ;
Params . append ( " /step:prepare " ) ;
Params . append ( " /embedded " ) ;
Params . append ( " /temp: " + GetUpdateDir ( ) . replace ( " / " , " \\ " ) ) ;
# ifdef DUMMY_PATH
Params . append ( " /path: " DUMMY_PATH ) ;
# endif
m_pUpdaterUtil = new QProcess ( this ) ;
m_pUpdaterUtil - > setProperty ( " apply " , bAndApply ) ;
m_pUpdaterUtil - > setProperty ( " version " , MakeVersionStr ( Update ) ) ;
m_pUpdaterUtil - > setProgram ( QApplication : : applicationDirPath ( ) + " /UpdUtil.exe " ) ;
m_pUpdaterUtil - > setArguments ( Params ) ;
connect ( m_pUpdaterUtil , SIGNAL ( finished ( int , QProcess : : ExitStatus ) ) , this , SLOT ( OnPrepareFinished ( int , QProcess : : ExitStatus ) ) ) ;
connect ( m_pUpdaterUtil , SIGNAL ( readyReadStandardOutput ( ) ) , this , SLOT ( OnPrepareOutput ( ) ) ) ;
connect ( m_pUpdaterUtil , SIGNAL ( readyReadStandardError ( ) ) , this , SLOT ( OnPrepareError ( ) ) ) ;
m_pUpdaterUtil - > start ( ) ;
if ( m_pUpdaterUtil - > state ( ) ! = QProcess : : Running )
return false ;
m_pUpdateProgress = CSbieProgressPtr ( new CSbieProgress ( ) ) ;
2023-08-15 08:49:03 +01:00
connect ( m_pUpdateProgress . data ( ) , & CSbieProgress : : Canceled , this , [ & ] ( ) {
if ( m_pUpdaterUtil & & m_pUpdaterUtil - > state ( ) = = QProcess : : Running )
m_pUpdaterUtil - > terminate ( ) ;
} ) ;
2022-11-20 16:23:15 +00:00
theGUI - > AddAsyncOp ( m_pUpdateProgress ) ;
m_pUpdateProgress - > ShowMessage ( tr ( " Downloading updates... " ) ) ;
return true ;
}
void COnlineUpdater : : OnPrepareOutput ( )
{
QProcess * pProcess = ( QProcess * ) sender ( ) ;
QByteArray Text = pProcess - > readAllStandardOutput ( ) ;
qDebug ( ) < < " UPD-OUT: \t " < < Text ;
if ( ! m_pUpdateProgress . isNull ( ) )
m_pUpdateProgress - > ShowMessage ( Text . trimmed ( ) ) ;
}
void COnlineUpdater : : OnPrepareError ( )
{
QProcess * pProcess = ( QProcess * ) sender ( ) ;
QByteArray Text = pProcess - > readAllStandardOutput ( ) ;
qDebug ( ) < < " UPD-ERR: \t " < < Text ;
}
QString GetUpdErrorStr ( int exitCode )
{
switch ( exitCode )
{
case ERROR_INVALID : return COnlineUpdater : : tr ( " invalid parameter " ) ;
2022-11-21 18:22:33 +00:00
case ERROR_GET : return COnlineUpdater : : tr ( " failed to download updated information " ) ;
case ERROR_LOAD : return COnlineUpdater : : tr ( " failed to load updated json file " ) ;
2022-11-20 16:23:15 +00:00
case ERROR_DOWNLOAD : return COnlineUpdater : : tr ( " failed to download a particular file " ) ;
case ERROR_SCAN : return COnlineUpdater : : tr ( " failed to scan existing installation " ) ;
2022-11-21 18:22:33 +00:00
case ERROR_SIGN : return COnlineUpdater : : tr ( " updated signature is invalid !!! " ) ;
2022-11-20 16:23:15 +00:00
case ERROR_HASH : return COnlineUpdater : : tr ( " downloaded file is corrupted " ) ;
case ERROR_INTERNAL : return COnlineUpdater : : tr ( " internal error " ) ;
default : return COnlineUpdater : : tr ( " unknown error " ) ;
}
}
void COnlineUpdater : : OnPrepareFinished ( int exitCode , QProcess : : ExitStatus exitStatus )
{
QProcess * pProcess = ( QProcess * ) sender ( ) ;
if ( pProcess ! = m_pUpdaterUtil ) {
pProcess - > deleteLater ( ) ;
return ;
}
bool bAndApply = pProcess - > property ( " apply " ) . toBool ( ) ;
QString VersionStr = pProcess - > property ( " version " ) . toString ( ) ;
m_pUpdaterUtil - > deleteLater ( ) ;
m_pUpdaterUtil = NULL ;
if ( m_pUpdateProgress . isNull ( ) )
return ; // canceled
m_pUpdateProgress - > Finish ( SB_OK ) ;
m_pUpdateProgress . clear ( ) ;
if ( exitCode < 0 ) {
QMessageBox : : critical ( theGUI , " Sandboxie-Plus " , tr ( " Failed to download updates from server, error %1 " ) . arg ( GetUpdErrorStr ( exitCode ) ) ) ;
return ; // failed
}
theConf - > SetValue ( " Updater/UpdateVersion " , VersionStr ) ;
if ( bAndApply )
ApplyUpdate ( false ) ;
else
{
HandleUpdate ( ) ;
2022-08-22 20:06:43 +01:00
theGUI - > UpdateLabel ( ) ;
2022-11-20 16:23:15 +00:00
}
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
bool COnlineUpdater : : ApplyUpdate ( bool bSilent )
{
2023-09-02 08:10:24 +01:00
if ( ! ShowCertWarningIfNeeded ( ) )
return false ;
2022-11-20 16:23:15 +00:00
if ( ! bSilent )
{
QString Message = tr ( " <p>Updates for Sandboxie-Plus have been downloaded.</p><p>Do you want to apply these updates? If any programs are running sandboxed, they will be terminated.</p> " ) ;
int Ret = QMessageBox ( " Sandboxie-Plus " , Message , QMessageBox : : Information , QMessageBox : : Yes | QMessageBox : : Default , QMessageBox : : No | QMessageBox : : Escape , QMessageBox : : Cancel , theGUI ) . exec ( ) ;
if ( Ret = = QMessageBox : : Cancel ) {
theConf - > DelValue ( " Updater/UpdateVersion " ) ;
theGUI - > UpdateLabel ( ) ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
if ( Ret ! = QMessageBox : : Yes )
return false ;
}
2023-02-02 08:03:01 +00:00
QVariantMap Update = QJsonDocument : : fromJson ( ReadFileAsString ( GetUpdateDir ( ) + " / " UPDATE_FILE ) . toUtf8 ( ) ) . toVariant ( ) . toMap ( ) ;
2022-11-20 16:23:15 +00:00
EUpdateScope Scope = ScanUpdateFiles ( Update ) ;
if ( Scope = = eNone )
2022-12-07 10:58:37 +00:00
return true ; // nothing to do
2022-11-20 16:23:15 +00:00
if ( Scope ! = eMeta )
theAPI - > TerminateAll ( ) ;
QStringList Params ;
Params . append ( " update " ) ;
Params . append ( " sandboxie-plus " ) ;
Params . append ( " /step:apply " ) ;
if ( Scope = = eMeta )
Params . append ( " /scope:meta " ) ;
else
Params . append ( " /restart " ) ;
# ifndef _DEBUG
Params . append ( " /embedded " ) ;
# else
Params . append ( " /pause " ) ;
# endif
Params . append ( " /temp: " + GetUpdateDir ( ) . replace ( " / " , " \\ " ) ) ;
# ifdef DUMMY_PATH
Params . append ( " /path: " DUMMY_PATH ) ;
# endif
if ( Scope = = eFull )
Params . append ( " /open:sandman.exe " ) ;
2023-08-02 07:25:47 +01:00
SB_RESULT ( int ) status = RunUpdater ( Params , true , Scope ! = eFull ) ;
2023-07-30 13:28:35 +01:00
if ( ! status . IsError ( ) ) {
2022-11-20 16:23:15 +00:00
if ( bSilent )
theConf - > DelValue ( " Updater/UpdateVersion " ) ;
if ( Scope = = eMeta )
theAPI - > ReloadConfig ( ) ;
else if ( Scope = = eFull )
QApplication : : quit ( ) ;
else
theGUI - > ConnectSbie ( ) ;
return true ;
}
return false ;
}
2023-07-30 13:28:35 +01:00
SB_RESULT ( int ) COnlineUpdater : : RunUpdater ( const QStringList & Params , bool bSilent , bool Wait )
2022-11-20 16:23:15 +00:00
{
if ( bSilent ) {
SB_RESULT ( int ) Result = theAPI - > RunUpdateUtility ( Params , 2 , Wait ) ;
if ( ! Result . IsError ( ) )
2023-07-30 13:28:35 +01:00
return Result ;
2022-11-20 16:23:15 +00:00
// else fallback to ShellExecuteEx
if ( theConf - > GetBool ( " Options/UpdateNoFallback " , false ) )
2023-07-30 13:28:35 +01:00
return Result ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
std : : wstring wFile = QString ( QApplication : : applicationDirPath ( ) + " /UpdUtil.exe " ) . replace ( " / " , " \\ " ) . toStdWString ( ) ;
std : : wstring wParams ;
foreach ( const QString & Param , Params ) {
if ( ! wParams . empty ( ) ) wParams . push_back ( L ' ' ) ;
wParams + = L " \" " + Param . toStdWString ( ) + L " \" " ;
}
2023-07-30 13:28:35 +01:00
int ExitCode = RunElevated ( wFile , wParams , Wait ? INFINITE : 0 ) ;
if ( ExitCode = = STATUS_PENDING & & ! Wait )
ExitCode = 0 ;
return CSbieResult < int > ( ExitCode ) ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
bool COnlineUpdater : : DownloadInstaller ( const QVariantMap & Release , bool bAndRun )
2022-06-08 16:23:19 +01:00
{
2022-11-20 16:23:15 +00:00
if ( m_RequestManager = = NULL )
m_RequestManager = new CNetworkAccessManager ( 30 * 1000 , this ) ;
QVariantMap Installer = Release [ " installer " ] . toMap ( ) ;
QString DownloadUrl = Installer [ " downloadUrl " ] . toString ( ) ;
if ( DownloadUrl . isEmpty ( ) )
return false ;
// clean up old installer if present
QString FilePath = theConf - > GetString ( " Updater/InstallerPath " ) ;
if ( ! FilePath . isEmpty ( ) ) {
QFile : : remove ( FilePath ) ;
QFile : : remove ( FilePath + " .sig " ) ;
theConf - > DelValue ( " Updater/InstallerPath " ) ;
}
2023-07-01 17:54:53 +01:00
QVariantMap Params ;
Params [ " run " ] = bAndRun ;
Params [ " version " ] = MakeVersionStr ( Release ) ;
Params [ " signature " ] = Installer [ " signature " ] ;
2023-08-15 08:49:03 +01:00
SB_PROGRESS Status = DownloadFile ( DownloadUrl , this , SLOT ( OnInstallerDownload ( const QString & , const QVariantMap & ) ) , Params ) ;
if ( Status . GetStatus ( ) = = OP_ASYNC ) {
theGUI - > AddAsyncOp ( Status . GetValue ( ) ) ;
Status . GetValue ( ) - > ShowMessage ( tr ( " Downloading installer... " ) ) ;
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
return true ;
2022-06-08 16:23:19 +01:00
}
2023-07-01 17:54:53 +01:00
void COnlineUpdater : : OnInstallerDownload ( const QString & Path , const QVariantMap & Params )
2022-06-08 16:23:19 +01:00
{
2023-07-01 17:54:53 +01:00
bool bAndRun = Params [ " run " ] . toBool ( ) ;
QString VersionStr = Params [ " version " ] . toString ( ) ;
QByteArray Signature = Params [ " signature " ] . toByteArray ( ) ;
2022-06-08 16:23:19 +01:00
2023-07-01 17:54:53 +01:00
QFile SigFile ( Path + " .sig " ) ;
2022-11-20 16:23:15 +00:00
if ( SigFile . open ( QFile : : WriteOnly ) ) {
SigFile . write ( QByteArray : : fromBase64 ( Signature ) ) ;
SigFile . close ( ) ;
}
theConf - > SetValue ( " Updater/InstallerVersion " , VersionStr ) ;
2023-07-01 17:54:53 +01:00
theConf - > SetValue ( " Updater/InstallerPath " , Path ) ;
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
if ( bAndRun )
RunInstaller ( false ) ;
else
{
HandleUpdate ( ) ;
theGUI - > UpdateLabel ( ) ;
}
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
bool COnlineUpdater : : RunInstaller ( bool bSilent )
2022-08-22 20:06:43 +01:00
{
2023-09-02 08:10:24 +01:00
if ( ! ShowCertWarningIfNeeded ( ) )
return false ;
2022-11-20 16:23:15 +00:00
QString FilePath = theConf - > GetString ( " Updater/InstallerPath " ) ;
if ( FilePath . isEmpty ( ) | | ! QFile : : exists ( FilePath ) ) {
theConf - > DelValue ( " Updater/InstallerPath " ) ;
theConf - > DelValue ( " Updater/InstallerVersion " ) ;
return false ;
}
if ( ! bSilent ) {
QString Message = tr ( " <p>A new Sandboxie-Plus installer has been downloaded to the following location:</p><p><a href= \" %2 \" >%1</a></p><p>Do you want to begin the installation? If any programs are running sandboxed, they will be terminated.</p> " )
. arg ( FilePath ) . arg ( " File:/// " + Split2 ( FilePath , " / " , true ) . first ) ;
int Ret = QMessageBox ( " Sandboxie-Plus " , Message , QMessageBox : : Information , QMessageBox : : Yes | QMessageBox : : Default , QMessageBox : : No | QMessageBox : : Escape , QMessageBox : : Cancel , theGUI ) . exec ( ) ;
if ( Ret = = QMessageBox : : Cancel ) {
QFile : : remove ( FilePath ) ;
QFile : : remove ( FilePath + " .sig " ) ;
theConf - > DelValue ( " Updater/InstallerPath " ) ;
theGUI - > UpdateLabel ( ) ;
}
if ( Ret ! = QMessageBox : : Yes )
return false ;
}
theAPI - > TerminateAll ( ) ;
2023-07-18 21:38:32 +01:00
if ( RunInstaller2 ( FilePath , true ) ) {
2022-11-20 16:23:15 +00:00
if ( bSilent )
theConf - > DelValue ( " Updater/InstallerVersion " ) ;
QApplication : : quit ( ) ;
2022-08-22 20:06:43 +01:00
return true ;
2022-11-20 16:23:15 +00:00
}
return false ;
}
bool COnlineUpdater : : RunInstaller2 ( const QString & FilePath , bool bSilent )
{
2023-11-27 18:14:16 +00:00
if ( bSilent & & ! theGUI - > IsFullyPortable ( ) )
2022-11-20 16:23:15 +00:00
{
QStringList Params ;
Params . append ( " run_setup " ) ;
Params . append ( QString ( FilePath ) . replace ( " / " , " \\ " ) ) ;
2023-07-01 17:54:53 +01:00
# ifndef _DEBUG_
2022-11-20 16:23:15 +00:00
Params . append ( " /embedded " ) ;
# else
Params . append ( " /pause " ) ;
2022-08-22 20:06:43 +01:00
# endif
2022-11-20 16:23:15 +00:00
SB_RESULT ( int ) Result = theAPI - > RunUpdateUtility ( Params , 1 ) ;
if ( ! Result . IsError ( ) )
return true ;
// else fallback to ShellExecuteEx
if ( theConf - > GetBool ( " Options/UpdateNoFallback " , false ) )
return false ;
}
2022-08-22 20:06:43 +01:00
2022-11-20 16:23:15 +00:00
std : : wstring wFile = QString ( FilePath ) . replace ( " / " , " \\ " ) . toStdWString ( ) ;
std : : wstring wParams ;
2023-11-27 18:14:16 +00:00
if ( theGUI - > IsFullyPortable ( ) )
wParams = L " /PORTABLE=1 " ;
2022-11-20 16:23:15 +00:00
# ifndef _DEBUG
2023-11-27 18:14:16 +00:00
else
wParams = L " /SILENT " ;
2022-11-20 16:23:15 +00:00
# endif
2022-08-22 20:06:43 +01:00
2022-11-20 16:23:15 +00:00
return RunElevated ( wFile , wParams ) = = 0 ;
2022-08-22 20:06:43 +01:00
}
2022-11-20 16:23:15 +00:00
bool COnlineUpdater : : HandleUserMessage ( const QVariantMap & Data )
2022-08-22 20:06:43 +01:00
{
2022-11-20 16:23:15 +00:00
QString UserMsg = Data [ " userMsg " ] . toString ( ) ;
if ( ! UserMsg . isEmpty ( ) )
{
QString MsgHash = QCryptographicHash : : hash ( Data [ " userMsg " ] . toByteArray ( ) , QCryptographicHash : : Md5 ) . toHex ( ) . left ( 8 ) ;
if ( ! m_IgnoredUpdates . contains ( MsgHash ) )
{
QString FullMessage = UserMsg ;
QString InfoUrl = Data [ " infoUrl " ] . toString ( ) ;
if ( ! InfoUrl . isEmpty ( ) )
FullMessage + = tr ( " <p>Do you want to go to the <a href= \" %1 \" >info page</a>?</p> " ) . arg ( InfoUrl ) ;
CCheckableMessageBox mb ( theGUI ) ;
mb . setWindowTitle ( " Sandboxie-Plus " ) ;
QIcon ico ( QLatin1String ( " :/SandMan.png " ) ) ;
mb . setIconPixmap ( ico . pixmap ( 64 , 64 ) ) ;
//mb.setTextFormat(Qt::RichText);
mb . setText ( UserMsg ) ;
mb . setCheckBoxText ( tr ( " Don't show this announcement in the future. " ) ) ;
if ( ! InfoUrl . isEmpty ( ) ) {
mb . setStandardButtons ( QDialogButtonBox : : Yes | QDialogButtonBox : : No ) ;
mb . setDefaultButton ( QDialogButtonBox : : Yes ) ;
}
else
mb . setStandardButtons ( QDialogButtonBox : : Ok ) ;
mb . exec ( ) ;
if ( mb . isChecked ( ) )
theConf - > SetValue ( " Options/IgnoredUpdates " , m_IgnoredUpdates < < MsgHash ) ;
if ( mb . clickedStandardButton ( ) = = QDialogButtonBox : : Yes )
{
QDesktopServices : : openUrl ( InfoUrl ) ;
}
return true ;
}
}
return false ;
2022-08-22 20:06:43 +01:00
}
2022-11-20 16:23:15 +00:00
QString COnlineUpdater : : GetUpdateDir ( bool bCreate )
2022-08-22 20:06:43 +01:00
{
2022-11-20 16:23:15 +00:00
QString TempDir = QStandardPaths : : writableLocation ( QStandardPaths : : TempLocation ) ;
if ( TempDir . right ( 1 ) ! = " / " )
TempDir + = " / " ;
TempDir + = " sandboxie-updater " ;
// Note: must not end with a /
if ( bCreate )
QDir ( ) . mkpath ( TempDir ) ;
return TempDir ;
2022-08-22 20:06:43 +01:00
}
2022-11-20 16:23:15 +00:00
QString COnlineUpdater : : MakeVersionStr ( const QVariantMap & Data )
2022-06-08 16:23:19 +01:00
{
2022-11-20 16:23:15 +00:00
QString Str = Data [ " version " ] . toString ( ) ;
int iUpdate = Data [ " update " ] . toInt ( ) ;
2022-11-20 17:16:13 +00:00
if ( iUpdate ) Str + = QChar ( ' a ' + ( iUpdate - 1 ) ) ;
2022-11-20 16:23:15 +00:00
return Str ;
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
QString COnlineUpdater : : ParseVersionStr ( const QString & Str , int * pUpdate )
{
int pos = Str . indexOf ( QRegularExpression ( " [a-zA-Z] " ) ) ;
if ( pos = = - 1 )
return Str ;
QString Ver = Str . left ( pos ) ;
if ( pUpdate ) {
QString Tmp = Str . mid ( pos ) ;
* pUpdate = Tmp [ 0 ] . toLatin1 ( ) - ' a ' + 1 ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
return Ver ;
}
2022-06-08 16:23:19 +01:00
2022-11-20 16:23:15 +00:00
QString COnlineUpdater : : GetCurrentVersion ( )
{
return QString : : number ( VERSION_MJR ) + " . " + QString : : number ( VERSION_MIN ) + " . " + QString : : number ( VERSION_REV ) ;
2022-06-08 16:23:19 +01:00
}
2022-11-20 16:23:15 +00:00
int COnlineUpdater : : GetCurrentUpdate ( )
{
int iUpdate = 0 ;
QString Version = ParseVersionStr ( theConf - > GetString ( " Updater/CurrentUpdate " , 0 ) , & iUpdate ) ;
if ( Version ! = GetCurrentVersion ( ) | | iUpdate < VERSION_UPD )
iUpdate = VERSION_UPD ;
return iUpdate ;
}
2023-07-01 17:54:53 +01:00
quint32 COnlineUpdater : : CurrentVersion ( )
2022-11-20 16:23:15 +00:00
{
//quint8 myVersion[4] = { VERSION_UPD, VERSION_REV, VERSION_MIN, VERSION_MJR }; // ntohl
quint8 myVersion [ 4 ] = { 0 , VERSION_REV , VERSION_MIN , VERSION_MJR } ; // ntohl
quint32 MyVersion = * ( quint32 * ) & myVersion ;
2023-07-01 17:54:53 +01:00
return MyVersion ;
}
2022-11-20 16:23:15 +00:00
2023-07-01 17:54:53 +01:00
quint32 COnlineUpdater : : VersionToInt ( const QString & VersionStr )
{
2022-11-20 16:23:15 +00:00
quint32 Version = 0 ;
QStringList Nums = VersionStr . split ( " . " ) ;
for ( int i = 0 , Bits = 24 ; i < Nums . count ( ) & & Bits > = 0 ; i + + , Bits - = 8 )
Version | = ( Nums [ i ] . toInt ( ) & 0xFF ) < < Bits ;
2023-07-01 17:54:53 +01:00
return Version ;
}
2022-11-20 16:23:15 +00:00
2023-07-01 17:54:53 +01:00
bool COnlineUpdater : : IsVersionNewer ( const QString & VersionStr )
{
if ( VersionStr . isEmpty ( ) )
return false ;
# ifdef INSIDER_BUILD
QString sVersion = VersionStr ;
if ( sVersion [ 4 ] = = ' ' ) sVersion [ 4 ] = ' 0 ' ;
QDateTime VersionDate = QDateTime : : fromString ( sVersion , " MMM dd yyyy " ) ;
sVersion = QString ( __DATE__ ) ;
if ( sVersion [ 4 ] = = ' ' ) sVersion [ 4 ] = ' 0 ' ;
QDateTime BuildDate = QDateTime : : fromString ( sVersion , " MMM dd yyyy " ) ;
return ( VersionDate > BuildDate ) ;
# else
return VersionToInt ( VersionStr ) > CurrentVersion ( ) ;
# endif
2022-11-20 16:23:15 +00:00
}