2014-12-20 20:29:17 +03:00
/*
* Bittorrent Client using Qt and libtorrent .
2015-01-22 15:56:16 +03:00
* Copyright ( C ) 2015 Vladimir Golovnev < glassez @ yandex . ru >
2014-12-20 20:29:17 +03:00
* Copyright ( C ) 2006 Christophe Dumez
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* In addition , as a special exception , the copyright holders give permission to
* link this program with the OpenSSL project ' s " OpenSSL " library ( or with
* modified versions of it that use the same license as the " OpenSSL " library ) ,
* and distribute the linked executables . You must obey the GNU General Public
* License in all respects for all of the code used other than " OpenSSL " . If you
* modify file ( s ) , you may extend this exception to your version of the file ( s ) ,
* but you are not obligated to do so . If you do not wish to do so , delete this
* exception statement from your version .
*/
2018-03-13 19:47:00 +03:00
# include "application.h"
2019-01-14 03:04:06 +03:00
# include <QtGlobal>
2018-03-13 19:47:00 +03:00
2019-01-14 03:04:06 +03:00
# include <algorithm>
2018-03-13 19:47:00 +03:00
2019-08-28 10:07:18 +03:00
# ifdef DISABLE_GUI
# include <cstdio>
# endif
2018-05-31 12:19:07 +03:00
# ifdef Q_OS_WIN
# include <memory>
2019-01-14 03:04:06 +03:00
# include <Windows.h>
2018-05-31 12:19:07 +03:00
# include <Shellapi.h>
# endif
2015-01-28 12:03:22 +03:00
2019-01-14 03:04:06 +03:00
# include <QAtomicInt>
# include <QDebug>
# include <QLibraryInfo>
# include <QProcess>
2015-01-22 15:56:16 +03:00
# ifndef DISABLE_GUI
2018-11-29 17:25:38 +03:00
# include <QMessageBox>
2019-08-28 10:07:18 +03:00
# include <QPixmapCache>
2015-01-20 18:00:37 +03:00
# ifdef Q_OS_WIN
2015-04-16 00:47:59 +03:00
# include <QSessionManager>
2018-03-13 19:47:00 +03:00
# include <QSharedMemory>
2015-01-22 15:56:16 +03:00
# endif // Q_OS_WIN
2019-09-05 15:11:33 +03:00
# ifdef Q_OS_MACOS
2015-01-22 15:56:16 +03:00
# include <QFileOpenEvent>
2019-09-05 15:11:33 +03:00
# endif // Q_OS_MACOS
2019-08-28 10:07:18 +03:00
# endif
2014-12-20 20:29:17 +03:00
2020-04-12 18:08:19 +03:00
# include "base/bittorrent/infohash.h"
2018-05-31 12:19:07 +03:00
# include "base/bittorrent/session.h"
2021-01-06 15:12:40 +03:00
# include "base/bittorrent/torrent.h"
2018-11-29 17:25:38 +03:00
# include "base/exceptions.h"
2022-03-01 16:42:25 +03:00
# include "base/global.h"
2018-05-31 12:19:07 +03:00
# include "base/iconprovider.h"
# include "base/logger.h"
# include "base/net/downloadmanager.h"
# include "base/net/geoipmanager.h"
# include "base/net/proxyconfigurationmanager.h"
# include "base/net/smtp.h"
# include "base/preferences.h"
# include "base/profile.h"
# include "base/rss/rss_autodownloader.h"
# include "base/rss/rss_session.h"
2018-03-08 09:10:53 +03:00
# include "base/search/searchpluginmanager.h"
2018-05-31 12:19:07 +03:00
# include "base/settingsstorage.h"
2021-04-23 12:02:25 +03:00
# include "base/torrentfileswatcher.h"
2021-04-05 08:02:28 +03:00
# include "base/utils/compare.h"
2018-05-31 12:19:07 +03:00
# include "base/utils/fs.h"
2022-02-08 06:03:48 +03:00
# include "base/path.h"
2018-05-31 12:19:07 +03:00
# include "base/utils/misc.h"
2021-01-04 10:02:13 +03:00
# include "base/version.h"
2019-02-08 11:15:09 +03:00
# include "applicationinstancemanager.h"
2018-05-31 12:19:07 +03:00
# include "filelogger.h"
2018-03-31 09:58:30 +03:00
2019-08-28 10:07:18 +03:00
# ifndef DISABLE_GUI
2020-04-30 10:53:43 +03:00
# include "gui/addnewtorrentdialog.h"
2019-08-28 10:07:18 +03:00
# include "gui/uithememanager.h"
# include "gui/utils.h"
2020-04-30 10:53:43 +03:00
# include "gui/mainwindow.h"
# include "gui/shutdownconfirmdialog.h"
2019-08-28 10:07:18 +03:00
# endif // DISABLE_GUI
2015-01-28 12:03:22 +03:00
# ifndef DISABLE_WEBUI
2015-04-19 18:17:47 +03:00
# include "webui/webui.h"
2015-01-28 13:33:00 +03:00
# endif
2016-03-14 15:39:13 +03:00
namespace
{
# define SETTINGS_KEY(name) "Application / " name
2021-11-08 08:23:33 +03:00
# define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY("FileLogger / ") name)
2016-03-14 15:39:13 +03:00
2018-07-21 08:28:13 +03:00
const QString LOG_FOLDER = QStringLiteral ( " logs " ) ;
2021-11-08 08:23:33 +03:00
const QChar PARAMS_SEPARATOR = QLatin1Char ( ' | ' ) ;
2016-05-03 22:45:06 +03:00
2022-02-08 06:03:48 +03:00
const Path DEFAULT_PORTABLE_MODE_PROFILE_DIR { QStringLiteral ( " profile " ) } ;
2018-01-25 04:45:32 +03:00
const int MIN_FILELOG_SIZE = 1024 ; // 1KiB
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024 ; // 1000MiB
const int DEFAULT_FILELOG_SIZE = 65 * 1024 ; // 65KiB
2019-08-28 10:07:18 +03:00
2019-09-05 15:40:47 +03:00
# if !defined(DISABLE_GUI)
2019-08-28 10:07:18 +03:00
const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024 ; // 64MiB
2019-09-05 15:40:47 +03:00
# endif
2016-03-14 15:39:13 +03:00
}
2014-12-20 20:29:17 +03:00
2020-02-27 08:42:47 +03:00
Application : : Application ( int & argc , char * * argv )
2019-02-08 11:15:09 +03:00
: BaseApplication ( argc , argv )
2015-01-22 15:56:16 +03:00
, m_running ( false )
2016-04-12 21:33:17 +03:00
, m_shutdownAct ( ShutdownDialogAction : : Exit )
2016-05-03 22:45:06 +03:00
, m_commandLineArgs ( parseCommandLine ( this - > arguments ( ) ) )
2022-03-01 16:42:25 +03:00
# ifdef Q_OS_WIN
, m_storeMemoryWorkingSetLimit ( SETTINGS_KEY ( " MemoryWorkingSetLimit " ) )
# endif
2021-11-08 08:23:33 +03:00
, m_storeFileLoggerEnabled ( FILELOGGER_SETTINGS_KEY ( " Enabled " ) )
, m_storeFileLoggerBackup ( FILELOGGER_SETTINGS_KEY ( " Backup " ) )
, m_storeFileLoggerDeleteOld ( FILELOGGER_SETTINGS_KEY ( " DeleteOld " ) )
, m_storeFileLoggerMaxSize ( FILELOGGER_SETTINGS_KEY ( " MaxSizeBytes " ) )
, m_storeFileLoggerAge ( FILELOGGER_SETTINGS_KEY ( " Age " ) )
, m_storeFileLoggerAgeType ( FILELOGGER_SETTINGS_KEY ( " AgeType " ) )
, m_storeFileLoggerPath ( FILELOGGER_SETTINGS_KEY ( " Path " ) )
2014-12-20 20:29:17 +03:00
{
2016-05-27 20:24:02 +03:00
qRegisterMetaType < Log : : Msg > ( " Log::Msg " ) ;
2020-04-15 19:48:00 +03:00
qRegisterMetaType < Log : : Peer > ( " Log::Peer " ) ;
2016-05-27 20:24:02 +03:00
2016-05-03 22:45:06 +03:00
setApplicationName ( " qBittorrent " ) ;
2019-03-01 00:41:08 +03:00
setOrganizationDomain ( " qbittorrent.org " ) ;
2019-08-28 10:00:14 +03:00
# if !defined(DISABLE_GUI)
setDesktopFileName ( " org.qbittorrent.qBittorrent " ) ;
2021-10-12 19:35:55 +03:00
# if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2019-08-28 10:00:14 +03:00
setAttribute ( Qt : : AA_UseHighDpiPixmaps , true ) ; // opt-in to the high DPI pixmap support
2021-10-12 19:35:55 +03:00
# endif
2019-08-28 10:00:14 +03:00
setQuitOnLastWindowClosed ( false ) ;
2019-08-28 10:07:18 +03:00
QPixmapCache : : setCacheLimit ( PIXMAP_CACHE_SIZE ) ;
2019-08-28 10:00:14 +03:00
# endif
2022-02-08 06:03:48 +03:00
const auto portableProfilePath = Path ( QCoreApplication : : applicationDirPath ( ) ) / DEFAULT_PORTABLE_MODE_PROFILE_DIR ;
const bool portableModeEnabled = m_commandLineArgs . profileDir . isEmpty ( ) & & portableProfilePath . exists ( ) ;
2016-05-03 22:45:06 +03:00
2022-02-08 06:03:48 +03:00
const Path profileDir = portableModeEnabled
? portableProfilePath
2016-05-03 22:45:06 +03:00
: m_commandLineArgs . profileDir ;
2020-02-11 05:56:04 +03:00
Profile : : initInstance ( profileDir , m_commandLineArgs . configurationName ,
2019-12-08 06:00:44 +03:00
( m_commandLineArgs . relativeFastresumePaths | | portableModeEnabled ) ) ;
2016-05-03 22:45:06 +03:00
2022-02-08 06:03:48 +03:00
m_instanceManager = new ApplicationInstanceManager ( Profile : : instance ( ) - > location ( SpecialFolder : : Config ) , this ) ;
2021-10-30 12:33:58 +03:00
2015-04-19 18:17:47 +03:00
Logger : : initInstance ( ) ;
2016-02-09 11:55:02 +03:00
SettingsStorage : : initInstance ( ) ;
2015-04-19 18:17:47 +03:00
Preferences : : initInstance ( ) ;
2014-12-20 20:29:17 +03:00
initializeTranslation ( ) ;
2017-03-16 16:58:43 +03:00
2019-08-28 10:00:14 +03:00
if ( m_commandLineArgs . webUiPort > 0 ) // it will be -1 when user did not set any value
Preferences : : instance ( ) - > setWebUiPort ( m_commandLineArgs . webUiPort ) ;
2017-03-16 16:58:43 +03:00
2019-08-28 10:00:14 +03:00
connect ( this , & QCoreApplication : : aboutToQuit , this , & Application : : cleanup ) ;
connect ( m_instanceManager , & ApplicationInstanceManager : : messageReceived , this , & Application : : processMessage ) ;
2017-03-16 16:58:43 +03:00
# if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
2018-04-18 16:59:41 +03:00
connect ( this , & QGuiApplication : : commitDataRequest , this , & Application : : shutdownCleanup , Qt : : DirectConnection ) ;
2017-03-16 16:58:43 +03:00
# endif
2015-01-22 15:56:16 +03:00
2016-03-14 15:39:13 +03:00
if ( isFileLoggerEnabled ( ) )
m_fileLogger = new FileLogger ( fileLoggerPath ( ) , isFileLoggerBackup ( ) , fileLoggerMaxSize ( ) , isFileLoggerDeleteOld ( ) , fileLoggerAge ( ) , static_cast < FileLogger : : FileLogAgeType > ( fileLoggerAgeType ( ) ) ) ;
2017-03-03 11:42:13 +03:00
Logger : : instance ( ) - > addMessage ( tr ( " qBittorrent %1 started " , " qBittorrent v3.2.0alpha started " ) . arg ( QBT_VERSION ) ) ;
2020-11-16 10:02:11 +03:00
if ( portableModeEnabled )
{
2022-02-08 06:03:48 +03:00
Logger : : instance ( ) - > addMessage ( tr ( " Running in portable mode. Auto detected profile folder at: %1 " ) . arg ( profileDir . toString ( ) ) ) ;
2019-12-08 06:00:44 +03:00
if ( m_commandLineArgs . relativeFastresumePaths )
Logger : : instance ( ) - > addMessage ( tr ( " Redundant command line flag detected: \" %1 \" . Portable mode implies relative fastresume. " ) . arg ( " --relative-fastresume " ) , Log : : WARNING ) ; // to avoid translating the `--relative-fastresume` string
}
2020-11-16 10:02:11 +03:00
else
{
2022-02-08 06:03:48 +03:00
Logger : : instance ( ) - > addMessage ( tr ( " Using config directory: %1 " ) . arg ( Profile : : instance ( ) - > location ( SpecialFolder : : Config ) . toString ( ) ) ) ;
2019-12-08 06:00:44 +03:00
}
2015-01-22 15:56:16 +03:00
}
2017-11-05 15:56:13 +03:00
Application : : ~ Application ( )
{
// we still need to call cleanup()
// in case the App failed to start
cleanup ( ) ;
}
2016-04-17 22:56:51 +03:00
# ifndef DISABLE_GUI
QPointer < MainWindow > Application : : mainWindow ( )
{
return m_window ;
}
# endif
2016-05-03 22:45:06 +03:00
const QBtCommandLineParameters & Application : : commandLineArgs ( ) const
{
return m_commandLineArgs ;
}
2022-03-01 16:42:25 +03:00
# ifdef Q_OS_WIN
int Application : : memoryWorkingSetLimit ( ) const
{
return m_storeMemoryWorkingSetLimit . get ( 512 ) ;
}
void Application : : setMemoryWorkingSetLimit ( const int size )
{
if ( size = = memoryWorkingSetLimit ( ) )
return ;
m_storeMemoryWorkingSetLimit = size ;
applyMemoryWorkingSetLimit ( ) ;
}
# endif
2016-03-14 15:39:13 +03:00
bool Application : : isFileLoggerEnabled ( ) const
2016-01-25 02:06:06 +03:00
{
2021-11-08 08:23:33 +03:00
return m_storeFileLoggerEnabled . get ( true ) ;
2016-03-14 15:39:13 +03:00
}
2016-01-25 02:06:06 +03:00
2019-02-09 18:40:14 +03:00
void Application : : setFileLoggerEnabled ( const bool value )
2016-03-14 15:39:13 +03:00
{
if ( value & & ! m_fileLogger )
m_fileLogger = new FileLogger ( fileLoggerPath ( ) , isFileLoggerBackup ( ) , fileLoggerMaxSize ( ) , isFileLoggerDeleteOld ( ) , fileLoggerAge ( ) , static_cast < FileLogger : : FileLogAgeType > ( fileLoggerAgeType ( ) ) ) ;
else if ( ! value )
2016-01-25 02:06:06 +03:00
delete m_fileLogger ;
2021-11-08 08:23:33 +03:00
m_storeFileLoggerEnabled = value ;
2016-03-14 15:39:13 +03:00
}
2022-02-08 06:03:48 +03:00
Path Application : : fileLoggerPath ( ) const
2016-03-14 15:39:13 +03:00
{
2022-02-08 06:03:48 +03:00
return m_storeFileLoggerPath . get ( specialFolderLocation ( SpecialFolder : : Data ) / Path ( LOG_FOLDER ) ) ;
2016-03-14 15:39:13 +03:00
}
2022-02-08 06:03:48 +03:00
void Application : : setFileLoggerPath ( const Path & path )
2016-03-14 15:39:13 +03:00
{
if ( m_fileLogger )
2017-05-09 08:55:24 +03:00
m_fileLogger - > changePath ( path ) ;
2021-11-08 08:23:33 +03:00
m_storeFileLoggerPath = path ;
2016-03-14 15:39:13 +03:00
}
bool Application : : isFileLoggerBackup ( ) const
{
2021-11-08 08:23:33 +03:00
return m_storeFileLoggerBackup . get ( true ) ;
2016-03-14 15:39:13 +03:00
}
2019-02-09 18:40:14 +03:00
void Application : : setFileLoggerBackup ( const bool value )
2016-03-14 15:39:13 +03:00
{
if ( m_fileLogger )
m_fileLogger - > setBackup ( value ) ;
2021-11-08 08:23:33 +03:00
m_storeFileLoggerBackup = value ;
2016-03-14 15:39:13 +03:00
}
bool Application : : isFileLoggerDeleteOld ( ) const
{
2021-11-08 08:23:33 +03:00
return m_storeFileLoggerDeleteOld . get ( true ) ;
2016-03-14 15:39:13 +03:00
}
2019-02-09 18:40:14 +03:00
void Application : : setFileLoggerDeleteOld ( const bool value )
2016-03-14 15:39:13 +03:00
{
if ( value & & m_fileLogger )
m_fileLogger - > deleteOld ( fileLoggerAge ( ) , static_cast < FileLogger : : FileLogAgeType > ( fileLoggerAgeType ( ) ) ) ;
2021-11-08 08:23:33 +03:00
m_storeFileLoggerDeleteOld = value ;
2016-03-14 15:39:13 +03:00
}
int Application : : fileLoggerMaxSize ( ) const
{
2021-11-08 08:23:33 +03:00
const int val = m_storeFileLoggerMaxSize . get ( DEFAULT_FILELOG_SIZE ) ;
2018-01-25 04:45:32 +03:00
return std : : min ( std : : max ( val , MIN_FILELOG_SIZE ) , MAX_FILELOG_SIZE ) ;
2016-03-14 15:39:13 +03:00
}
2018-01-25 04:45:32 +03:00
void Application : : setFileLoggerMaxSize ( const int bytes )
2016-03-14 15:39:13 +03:00
{
2019-02-09 18:40:14 +03:00
const int clampedValue = std : : min ( std : : max ( bytes , MIN_FILELOG_SIZE ) , MAX_FILELOG_SIZE ) ;
2016-03-14 15:39:13 +03:00
if ( m_fileLogger )
2018-01-25 04:45:32 +03:00
m_fileLogger - > setMaxSize ( clampedValue ) ;
2021-11-08 08:23:33 +03:00
m_storeFileLoggerMaxSize = clampedValue ;
2016-03-14 15:39:13 +03:00
}
int Application : : fileLoggerAge ( ) const
{
2021-11-08 08:23:33 +03:00
const int val = m_storeFileLoggerAge . get ( 1 ) ;
2018-01-25 04:45:32 +03:00
return std : : min ( std : : max ( val , 1 ) , 365 ) ;
2016-03-14 15:39:13 +03:00
}
void Application : : setFileLoggerAge ( const int value )
{
2021-11-08 08:23:33 +03:00
m_storeFileLoggerAge = std : : min ( std : : max ( value , 1 ) , 365 ) ;
2016-03-14 15:39:13 +03:00
}
int Application : : fileLoggerAgeType ( ) const
{
2021-11-08 08:23:33 +03:00
const int val = m_storeFileLoggerAgeType . get ( 1 ) ;
2018-05-31 12:19:07 +03:00
return ( ( val < 0 ) | | ( val > 2 ) ) ? 1 : val ;
2016-03-14 15:39:13 +03:00
}
void Application : : setFileLoggerAgeType ( const int value )
{
2021-11-08 08:23:33 +03:00
m_storeFileLoggerAgeType = ( ( value < 0 ) | | ( value > 2 ) ) ? 1 : value ;
2016-01-25 02:06:06 +03:00
}
2015-01-22 15:56:16 +03:00
void Application : : processMessage ( const QString & message )
{
2021-05-25 19:58:43 +03:00
const QStringList params = message . split ( PARAMS_SEPARATOR , Qt : : SkipEmptyParts ) ;
2015-01-22 15:56:16 +03:00
// If Application is not running (i.e., other
// components are not ready) store params
if ( m_running )
processParams ( params ) ;
else
m_paramsQueue . append ( params ) ;
}
2021-01-06 15:12:40 +03:00
void Application : : runExternalProgram ( const BitTorrent : : Torrent * torrent ) const
2016-05-24 12:18:41 +03:00
{
2018-05-11 19:52:00 +03:00
# if defined(Q_OS_WIN)
const auto chopPathSep = [ ] ( const QString & str ) - > QString
{
if ( str . endsWith ( ' \\ ' ) )
return str . mid ( 0 , ( str . length ( ) - 1 ) ) ;
return str ;
} ;
2021-03-09 10:05:27 +03:00
# endif
QString program = Preferences : : instance ( ) - > getAutoRunProgram ( ) . trimmed ( ) ;
for ( int i = ( program . length ( ) - 2 ) ; i > = 0 ; - - i )
{
if ( program [ i ] ! = QLatin1Char ( ' % ' ) )
continue ;
const ushort specifier = program [ i + 1 ] . unicode ( ) ;
switch ( specifier )
{
case u ' C ' :
program . replace ( i , 2 , QString : : number ( torrent - > filesCount ( ) ) ) ;
break ;
case u ' D ' :
# if defined(Q_OS_WIN)
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , chopPathSep ( torrent - > savePath ( ) . toString ( ) ) ) ;
2018-05-11 19:52:00 +03:00
# else
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , torrent - > savePath ( ) . toString ( ) ) ;
2018-05-11 19:52:00 +03:00
# endif
2021-03-09 10:05:27 +03:00
break ;
case u ' F ' :
# if defined(Q_OS_WIN)
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , chopPathSep ( torrent - > contentPath ( ) . toString ( ) ) ) ;
2021-03-09 10:05:27 +03:00
# else
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , torrent - > contentPath ( ) . toString ( ) ) ;
2021-03-09 10:05:27 +03:00
# endif
break ;
case u ' G ' :
2021-04-02 08:45:50 +03:00
program . replace ( i , 2 , torrent - > tags ( ) . join ( QLatin1String ( " , " ) ) ) ;
2021-03-09 10:05:27 +03:00
break ;
case u ' I ' :
2021-06-25 20:44:23 +03:00
program . replace ( i , 2 , ( torrent - > infoHash ( ) . v1 ( ) . isValid ( ) ? torrent - > infoHash ( ) . v1 ( ) . toString ( ) : QLatin1String ( " - " ) ) ) ;
break ;
case u ' J ' :
program . replace ( i , 2 , ( torrent - > infoHash ( ) . v2 ( ) . isValid ( ) ? torrent - > infoHash ( ) . v2 ( ) . toString ( ) : QLatin1String ( " - " ) ) ) ;
break ;
case u ' K ' :
2021-03-09 10:05:27 +03:00
program . replace ( i , 2 , torrent - > id ( ) . toString ( ) ) ;
break ;
case u ' L ' :
program . replace ( i , 2 , torrent - > category ( ) ) ;
break ;
case u ' N ' :
program . replace ( i , 2 , torrent - > name ( ) ) ;
break ;
case u ' R ' :
# if defined(Q_OS_WIN)
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , chopPathSep ( torrent - > rootPath ( ) . toString ( ) ) ) ;
2021-03-09 10:05:27 +03:00
# else
2022-02-08 06:03:48 +03:00
program . replace ( i , 2 , torrent - > rootPath ( ) . toString ( ) ) ;
2021-03-09 10:05:27 +03:00
# endif
break ;
case u ' T ' :
program . replace ( i , 2 , torrent - > currentTracker ( ) ) ;
break ;
case u ' Z ' :
program . replace ( i , 2 , QString : : number ( torrent - > totalSize ( ) ) ) ;
break ;
default :
// do nothing
break ;
}
// decrement `i` to avoid unwanted replacement, example pattern: "%%N"
- - i ;
}
LogMsg ( tr ( " Torrent: %1, running external program, command: %2 " ) . arg ( torrent - > name ( ) , program ) ) ;
2016-05-24 12:18:41 +03:00
2018-05-11 19:52:00 +03:00
# if defined(Q_OS_WIN)
2019-12-15 18:03:02 +03:00
auto programWchar = std : : make_unique < wchar_t [ ] > ( program . length ( ) + 1 ) ;
2018-03-31 09:58:30 +03:00
program . toWCharArray ( programWchar . get ( ) ) ;
// Need to split arguments manually because QProcess::startDetached(QString)
// will strip off empty parameters.
// E.g. `python.exe "1" "" "3"` will become `python.exe "1" "3"`
int argCount = 0 ;
2019-12-15 18:03:02 +03:00
std : : unique_ptr < LPWSTR [ ] , decltype ( & : : LocalFree ) > args { : : CommandLineToArgvW ( programWchar . get ( ) , & argCount ) , : : LocalFree } ;
2018-03-31 09:58:30 +03:00
QStringList argList ;
for ( int i = 1 ; i < argCount ; + + i )
argList + = QString : : fromWCharArray ( args [ i ] ) ;
2019-12-15 18:08:49 +03:00
QProcess proc ;
proc . setProgram ( QString : : fromWCharArray ( args [ 0 ] ) ) ;
proc . setArguments ( argList ) ;
proc . setCreateProcessArgumentsModifier ( [ ] ( QProcess : : CreateProcessArguments * args )
{
2020-11-16 10:02:11 +03:00
if ( Preferences : : instance ( ) - > isAutoRunConsoleEnabled ( ) )
{
2019-12-15 18:08:49 +03:00
args - > flags | = CREATE_NEW_CONSOLE ;
args - > flags & = ~ ( CREATE_NO_WINDOW | DETACHED_PROCESS ) ;
}
2020-11-16 10:02:11 +03:00
else
{
2019-12-15 18:08:49 +03:00
args - > flags | = CREATE_NO_WINDOW ;
args - > flags & = ~ ( CREATE_NEW_CONSOLE | DETACHED_PROCESS ) ;
}
args - > inheritHandles = false ;
args - > startupInfo - > dwFlags & = ~ STARTF_USESTDHANDLES ;
: : CloseHandle ( args - > startupInfo - > hStdInput ) ;
: : CloseHandle ( args - > startupInfo - > hStdOutput ) ;
: : CloseHandle ( args - > startupInfo - > hStdError ) ;
args - > startupInfo - > hStdInput = nullptr ;
args - > startupInfo - > hStdOutput = nullptr ;
args - > startupInfo - > hStdError = nullptr ;
} ) ;
proc . startDetached ( ) ;
# else // Q_OS_WIN
2019-07-18 17:36:40 +03:00
// Cannot give users shell environment by default, as doing so could
// enable command injection via torrent name and other arguments
// (especially when some automated download mechanism has been setup).
// See: https://github.com/qbittorrent/qBittorrent/issues/10925
2020-06-04 10:13:50 +03:00
QStringList args = QProcess : : splitCommand ( program ) ;
if ( args . isEmpty ( ) )
return ;
const QString command = args . takeFirst ( ) ;
QProcess : : startDetached ( command , args ) ;
# endif
2016-05-24 12:18:41 +03:00
}
2021-01-06 15:12:40 +03:00
void Application : : sendNotificationEmail ( const BitTorrent : : Torrent * torrent )
2015-04-19 18:17:47 +03:00
{
// Prepare mail content
2018-05-31 12:19:07 +03:00
const QString content = tr ( " Torrent name: %1 " ) . arg ( torrent - > name ( ) ) + ' \n '
+ tr ( " Torrent size: %1 " ) . arg ( Utils : : Misc : : friendlyUnit ( torrent - > wantedSize ( ) ) ) + ' \n '
2022-02-08 06:03:48 +03:00
+ tr ( " Save path: %1 " ) . arg ( torrent - > savePath ( ) . toString ( ) ) + " \n \n "
2017-10-25 15:58:01 +03:00
+ tr ( " The torrent was downloaded in %1. " , " The torrent was downloaded in 1 hour and 20 seconds " )
. arg ( Utils : : Misc : : userFriendlyDuration ( torrent - > activeTime ( ) ) ) + " \n \n \n "
2018-05-31 12:19:07 +03:00
+ tr ( " Thank you for using qBittorrent. " ) + ' \n ' ;
2015-04-19 18:17:47 +03:00
// Send the notification email
2017-10-25 15:58:01 +03:00
const Preferences * pref = Preferences : : instance ( ) ;
2019-02-13 18:12:02 +03:00
auto * smtp = new Net : : Smtp ( this ) ;
2017-10-25 15:58:01 +03:00
smtp - > sendMail ( pref - > getMailNotificationSender ( ) ,
pref - > getMailNotificationEmail ( ) ,
tr ( " [qBittorrent] '%1' has finished downloading " ) . arg ( torrent - > name ( ) ) ,
2015-04-19 18:17:47 +03:00
content ) ;
}
2021-01-06 15:12:40 +03:00
void Application : : torrentFinished ( BitTorrent : : Torrent * const torrent )
2015-04-19 18:17:47 +03:00
{
Preferences * const pref = Preferences : : instance ( ) ;
// AutoRun program
2016-05-24 12:18:41 +03:00
if ( pref - > isAutoRunEnabled ( ) )
runExternalProgram ( torrent ) ;
2015-04-19 18:17:47 +03:00
// Mail notification
2020-11-16 10:02:11 +03:00
if ( pref - > isMailNotificationEnabled ( ) )
{
2016-05-24 12:18:41 +03:00
Logger : : instance ( ) - > addMessage ( tr ( " Torrent: %1, sending mail notification " ) . arg ( torrent - > name ( ) ) ) ;
2015-04-19 18:17:47 +03:00
sendNotificationEmail ( torrent ) ;
2016-05-30 19:07:08 +03:00
}
2015-04-19 18:17:47 +03:00
}
void Application : : allTorrentsFinished ( )
{
Preferences * const pref = Preferences : : instance ( ) ;
2016-04-16 08:01:29 +03:00
bool isExit = pref - > shutdownqBTWhenDownloadsComplete ( ) ;
bool isShutdown = pref - > shutdownWhenDownloadsComplete ( ) ;
bool isSuspend = pref - > suspendWhenDownloadsComplete ( ) ;
bool isHibernate = pref - > hibernateWhenDownloadsComplete ( ) ;
bool haveAction = isExit | | isShutdown | | isSuspend | | isHibernate ;
if ( ! haveAction ) return ;
ShutdownDialogAction action = ShutdownDialogAction : : Exit ;
if ( isSuspend )
action = ShutdownDialogAction : : Suspend ;
else if ( isHibernate )
action = ShutdownDialogAction : : Hibernate ;
else if ( isShutdown )
action = ShutdownDialogAction : : Shutdown ;
2016-04-16 08:50:41 +03:00
# ifndef DISABLE_GUI
2016-04-16 08:01:29 +03:00
// ask confirm
2020-11-16 10:02:11 +03:00
if ( ( action = = ShutdownDialogAction : : Exit ) & & ( pref - > dontConfirmAutoExit ( ) ) )
{
2016-04-16 08:01:29 +03:00
// do nothing & skip confirm
}
2020-11-16 10:02:11 +03:00
else
{
2018-06-14 12:54:23 +03:00
if ( ! ShutdownConfirmDialog : : askForConfirmation ( m_window , action ) ) return ;
2016-04-16 08:01:29 +03:00
}
2016-04-16 08:50:41 +03:00
# endif // DISABLE_GUI
2015-04-19 18:17:47 +03:00
2016-04-16 08:01:29 +03:00
// Actually shut down
2020-11-16 10:02:11 +03:00
if ( action ! = ShutdownDialogAction : : Exit )
{
2016-04-16 08:01:29 +03:00
qDebug ( " Preparing for auto-shutdown because all downloads are complete! " ) ;
// Disabling it for next time
pref - > setShutdownWhenDownloadsComplete ( false ) ;
pref - > setSuspendWhenDownloadsComplete ( false ) ;
pref - > setHibernateWhenDownloadsComplete ( false ) ;
// Make sure preferences are synced before exiting
m_shutdownAct = action ;
2015-04-19 18:17:47 +03:00
}
2016-04-16 08:01:29 +03:00
qDebug ( " Exiting the application " ) ;
exit ( ) ;
2015-04-19 18:17:47 +03:00
}
2015-01-22 15:56:16 +03:00
bool Application : : sendParams ( const QStringList & params )
{
2019-02-08 11:15:09 +03:00
return m_instanceManager - > sendMessage ( params . join ( PARAMS_SEPARATOR ) ) ;
2015-01-22 15:56:16 +03:00
}
// As program parameters, we can get paths or urls.
// This function parse the parameters and call
// the right addTorrent function, considering
// the parameter type.
void Application : : processParams ( const QStringList & params )
{
# ifndef DISABLE_GUI
2020-11-16 10:02:11 +03:00
if ( params . isEmpty ( ) )
{
2015-01-22 15:56:16 +03:00
m_window - > activate ( ) ; // show UI
return ;
}
# endif
2016-07-15 05:15:10 +03:00
BitTorrent : : AddTorrentParams torrentParams ;
2021-01-02 16:55:17 +03:00
std : : optional < bool > skipTorrentDialog ;
2015-01-22 15:56:16 +03:00
2020-11-16 10:02:11 +03:00
for ( QString param : params )
{
2015-01-22 15:56:16 +03:00
param = param . trimmed ( ) ;
2016-07-15 05:15:10 +03:00
// Process strings indicating options specified by the user.
2015-10-26 10:45:14 +03:00
2020-11-16 10:02:11 +03:00
if ( param . startsWith ( QLatin1String ( " @savePath= " ) ) )
{
2022-02-08 06:03:48 +03:00
torrentParams . savePath = Path ( param . mid ( 10 ) ) ;
2016-07-15 05:15:10 +03:00
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param . startsWith ( QLatin1String ( " @addPaused= " ) ) )
{
2021-03-01 09:40:30 +03:00
torrentParams . addPaused = ( QStringView ( param ) . mid ( 11 ) . toInt ( ) ! = 0 ) ;
2016-07-15 05:15:10 +03:00
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param = = QLatin1String ( " @skipChecking " ) )
{
2016-07-15 05:15:10 +03:00
torrentParams . skipChecking = true ;
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param . startsWith ( QLatin1String ( " @category= " ) ) )
{
2016-07-15 05:15:10 +03:00
torrentParams . category = param . mid ( 10 ) ;
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param = = QLatin1String ( " @sequential " ) )
{
2016-07-15 05:15:10 +03:00
torrentParams . sequential = true ;
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param = = QLatin1String ( " @firstLastPiecePriority " ) )
{
2016-07-15 05:15:10 +03:00
torrentParams . firstLastPiecePriority = true ;
continue ;
}
2020-11-16 10:02:11 +03:00
if ( param . startsWith ( QLatin1String ( " @skipDialog= " ) ) )
{
2021-03-01 09:40:30 +03:00
skipTorrentDialog = ( QStringView ( param ) . mid ( 12 ) . toInt ( ) ! = 0 ) ;
2016-07-15 05:15:10 +03:00
continue ;
}
2015-01-22 15:56:16 +03:00
# ifndef DISABLE_GUI
2016-07-15 05:15:10 +03:00
// There are two circumstances in which we want to show the torrent
// dialog. One is when the application settings specify that it should
// be shown and skipTorrentDialog is undefined. The other is when
// skipTorrentDialog is false, meaning that the application setting
// should be overridden.
2021-01-02 16:55:17 +03:00
const bool showDialogForThisTorrent = ! skipTorrentDialog . value_or ( ! AddNewTorrentDialog : : isEnabled ( ) ) ;
2016-07-15 05:15:10 +03:00
if ( showDialogForThisTorrent )
AddNewTorrentDialog : : show ( param , torrentParams , m_window ) ;
2015-04-19 18:17:47 +03:00
else
2015-01-22 15:56:16 +03:00
# endif
2016-07-15 05:15:10 +03:00
BitTorrent : : Session : : instance ( ) - > addTorrent ( param , torrentParams ) ;
2015-01-22 15:56:16 +03:00
}
}
int Application : : exec ( const QStringList & params )
{
2022-03-01 16:42:25 +03:00
# ifdef Q_OS_WIN
applyMemoryWorkingSetLimit ( ) ;
# endif
2016-05-01 11:05:52 +03:00
Net : : ProxyConfigurationManager : : initInstance ( ) ;
2015-04-19 18:17:47 +03:00
Net : : DownloadManager : : initInstance ( ) ;
IconProvider : : initInstance ( ) ;
2015-05-18 17:02:48 +03:00
2020-11-16 10:02:11 +03:00
try
{
2018-11-29 17:25:38 +03:00
BitTorrent : : Session : : initInstance ( ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : torrentFinished , this , & Application : : torrentFinished ) ;
connect ( BitTorrent : : Session : : instance ( ) , & BitTorrent : : Session : : allTorrentsFinished , this , & Application : : allTorrentsFinished , Qt : : QueuedConnection ) ;
2015-04-19 18:17:47 +03:00
2018-11-29 17:25:38 +03:00
Net : : GeoIPManager : : initInstance ( ) ;
2021-04-23 12:02:25 +03:00
TorrentFilesWatcher : : initInstance ( ) ;
2015-01-22 15:56:16 +03:00
2015-01-28 12:03:22 +03:00
# ifndef DISABLE_WEBUI
2018-11-29 17:25:38 +03:00
m_webui = new WebUI ;
2017-11-05 15:56:13 +03:00
# ifdef DISABLE_GUI
2018-11-29 17:25:38 +03:00
if ( m_webui - > isErrored ( ) )
return 1 ;
connect ( m_webui , & WebUI : : fatalError , this , [ ] ( ) { QCoreApplication : : exit ( 1 ) ; } ) ;
2017-11-05 15:56:13 +03:00
# endif // DISABLE_GUI
# endif // DISABLE_WEBUI
2015-01-28 12:03:22 +03:00
2018-11-29 17:25:38 +03:00
new RSS : : Session ; // create RSS::Session singleton
new RSS : : AutoDownloader ; // create RSS::AutoDownloader singleton
}
2020-11-16 10:02:11 +03:00
catch ( const RuntimeError & err )
{
2018-11-29 17:25:38 +03:00
# ifdef DISABLE_GUI
2021-05-05 16:41:30 +03:00
fprintf ( stderr , " %s " , qPrintable ( err . message ( ) ) ) ;
2018-11-29 17:25:38 +03:00
# else
QMessageBox msgBox ;
msgBox . setIcon ( QMessageBox : : Critical ) ;
msgBox . setText ( tr ( " Application failed to start. " ) ) ;
msgBox . setInformativeText ( err . message ( ) ) ;
msgBox . show ( ) ; // Need to be shown or to moveToCenter does not work
2019-03-02 08:22:13 +03:00
msgBox . move ( Utils : : Gui : : screenCenter ( & msgBox ) ) ;
2018-11-29 17:25:38 +03:00
msgBox . exec ( ) ;
# endif
return 1 ;
}
2017-03-07 16:10:42 +03:00
2015-01-22 15:56:16 +03:00
# ifdef DISABLE_GUI
2015-01-28 12:03:22 +03:00
# ifndef DISABLE_WEBUI
2021-12-17 06:49:11 +03:00
const Preferences * pref = Preferences : : instance ( ) ;
const auto scheme = QString : : fromLatin1 ( pref - > isWebUiHttpsEnabled ( ) ? " https " : " http " ) ;
const auto url = QString : : fromLatin1 ( " %1://localhost:%2 \n " ) . arg ( scheme , QString : : number ( pref - > getWebUiPort ( ) ) ) ;
2020-03-22 13:35:16 +03:00
const QString mesg = QString : : fromLatin1 ( " \n ******** %1 ******** \n " ) . arg ( tr ( " Information " ) )
2022-01-08 07:24:49 +03:00
+ tr ( " To control qBittorrent, access the WebUI at: %1 " ) . arg ( url ) ;
printf ( " %s \n " , qUtf8Printable ( mesg ) ) ;
2018-11-21 10:15:51 +03:00
2020-11-16 10:02:11 +03:00
if ( pref - > getWebUIPassword ( ) = = " ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ== " )
{
2018-11-26 20:44:47 +03:00
const QString warning = tr ( " The Web UI administrator username is: %1 " ) . arg ( pref - > getWebUiUsername ( ) ) + ' \n '
2021-09-12 11:45:00 +03:00
+ tr ( " The Web UI administrator password has not been changed from the default: %1 " ) . arg ( " adminadmin " ) + ' \n '
+ tr ( " This is a security risk, please change your password in program preferences. " ) + ' \n ' ;
2018-03-14 19:13:47 +03:00
printf ( " %s " , qUtf8Printable ( warning ) ) ;
2015-01-22 15:56:16 +03:00
}
2015-01-28 12:03:22 +03:00
# endif // DISABLE_WEBUI
2015-01-22 15:56:16 +03:00
# else
2019-07-09 17:26:55 +03:00
UIThemeManager : : initInstance ( ) ;
2015-01-22 15:56:16 +03:00
m_window = new MainWindow ;
2015-01-28 12:03:22 +03:00
# endif // DISABLE_GUI
2015-01-22 15:56:16 +03:00
m_running = true ;
2017-11-21 00:40:09 +03:00
// Now UI is ready to process signals from Session
BitTorrent : : Session : : instance ( ) - > startUpTorrents ( ) ;
2015-01-22 15:56:16 +03:00
m_paramsQueue = params + m_paramsQueue ;
2020-11-16 10:02:11 +03:00
if ( ! m_paramsQueue . isEmpty ( ) )
{
2015-01-22 15:56:16 +03:00
processParams ( m_paramsQueue ) ;
m_paramsQueue . clear ( ) ;
}
2015-01-26 17:00:23 +03:00
return BaseApplication : : exec ( ) ;
2014-12-20 20:29:17 +03:00
}
2015-01-20 18:00:37 +03:00
bool Application : : isRunning ( )
{
2019-02-08 11:15:09 +03:00
return ! m_instanceManager - > isFirstInstance ( ) ;
2015-01-20 18:00:37 +03:00
}
2015-01-22 15:56:16 +03:00
2019-02-08 11:15:09 +03:00
# ifndef DISABLE_GUI
2019-09-05 15:11:33 +03:00
# ifdef Q_OS_MACOS
2015-01-22 15:56:16 +03:00
bool Application : : event ( QEvent * ev )
{
2020-11-16 10:02:11 +03:00
if ( ev - > type ( ) = = QEvent : : FileOpen )
{
2015-01-22 15:56:16 +03:00
QString path = static_cast < QFileOpenEvent * > ( ev ) - > file ( ) ;
if ( path . isEmpty ( ) )
// Get the url instead
path = static_cast < QFileOpenEvent * > ( ev ) - > url ( ) . toString ( ) ;
2017-08-13 13:56:03 +03:00
qDebug ( " Received a mac file open event: %s " , qUtf8Printable ( path ) ) ;
2015-04-08 05:49:45 +03:00
if ( m_running )
2015-01-22 15:56:16 +03:00
processParams ( QStringList ( path ) ) ;
else
2015-04-08 05:49:45 +03:00
m_paramsQueue . append ( path ) ;
2015-01-22 15:56:16 +03:00
return true ;
}
2020-11-16 10:02:11 +03:00
else
{
2015-01-22 15:56:16 +03:00
return BaseApplication : : event ( ev ) ;
}
}
2019-09-05 15:11:33 +03:00
# endif // Q_OS_MACOS
2015-01-22 15:56:16 +03:00
# endif // DISABLE_GUI
2015-01-20 18:00:37 +03:00
2014-12-20 20:29:17 +03:00
void Application : : initializeTranslation ( )
{
2018-05-31 12:19:07 +03:00
Preferences * const pref = Preferences : : instance ( ) ;
2014-12-20 20:29:17 +03:00
// Load translation
2019-02-09 18:40:14 +03:00
const QString localeStr = pref - > getLocale ( ) ;
2015-06-15 00:13:52 +03:00
2017-11-21 11:07:48 +03:00
if ( m_qtTranslator . load ( QLatin1String ( " qtbase_ " ) + localeStr , QLibraryInfo : : location ( QLibraryInfo : : TranslationsPath ) ) | |
m_qtTranslator . load ( QLatin1String ( " qt_ " ) + localeStr , QLibraryInfo : : location ( QLibraryInfo : : TranslationsPath ) ) )
2017-08-13 13:56:03 +03:00
qDebug ( " Qt %s locale recognized, using translation. " , qUtf8Printable ( localeStr ) ) ;
2016-12-18 09:33:59 +03:00
else
2017-08-13 13:56:03 +03:00
qDebug ( " Qt %s locale unrecognized, using default (en). " , qUtf8Printable ( localeStr ) ) ;
2017-01-19 15:10:09 +03:00
2015-01-20 18:00:37 +03:00
installTranslator ( & m_qtTranslator ) ;
2014-12-20 20:29:17 +03:00
2017-11-21 11:07:48 +03:00
if ( m_translator . load ( QLatin1String ( " :/lang/qbittorrent_ " ) + localeStr ) )
2017-08-13 13:56:03 +03:00
qDebug ( " %s locale recognized, using translation. " , qUtf8Printable ( localeStr ) ) ;
2016-12-18 09:33:59 +03:00
else
2017-08-13 13:56:03 +03:00
qDebug ( " %s locale unrecognized, using default (en). " , qUtf8Printable ( localeStr ) ) ;
2015-01-20 18:00:37 +03:00
installTranslator ( & m_translator ) ;
2014-12-20 20:29:17 +03:00
# ifndef DISABLE_GUI
2020-11-16 10:02:11 +03:00
if ( localeStr . startsWith ( " ar " ) | | localeStr . startsWith ( " he " ) )
{
2014-12-20 20:29:17 +03:00
qDebug ( " Right to Left mode " ) ;
setLayoutDirection ( Qt : : RightToLeft ) ;
}
2020-11-16 10:02:11 +03:00
else
{
2014-12-20 20:29:17 +03:00
setLayoutDirection ( Qt : : LeftToRight ) ;
}
# endif
}
2015-01-28 12:03:22 +03:00
2015-04-16 00:47:59 +03:00
# if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
void Application : : shutdownCleanup ( QSessionManager & manager )
{
2015-04-19 18:17:47 +03:00
Q_UNUSED ( manager ) ;
2015-04-16 00:47:59 +03:00
// This is only needed for a special case on Windows XP.
// (but is called for every Windows version)
// If a process takes too much time to exit during OS
// shutdown, the OS presents a dialog to the user.
// That dialog tells the user that qbt is blocking the
// shutdown, it shows a progress bar and it offers
// a "Terminate Now" button for the user. However,
// after the progress bar has reached 100% another button
// is offered to the user reading "Cancel". With this the
// user can cancel the **OS** shutdown. If we don't do
// the cleanup by handling the commitDataRequest() signal
// and the user clicks "Cancel", it will result in qbt being
// killed and the shutdown proceeding instead. Apparently
// aboutToQuit() is emitted too late in the shutdown process.
cleanup ( ) ;
// According to the qt docs we shouldn't call quit() inside a slot.
// aboutToQuit() is never emitted if the user hits "Cancel" in
// the above dialog.
2018-05-10 20:42:19 +03:00
QTimer : : singleShot ( 0 , qApp , & QCoreApplication : : quit ) ;
2015-04-16 00:47:59 +03:00
}
# endif
2022-03-01 16:42:25 +03:00
# ifdef Q_OS_WIN
void Application : : applyMemoryWorkingSetLimit ( )
{
const int UNIT_SIZE = 1024 * 1024 ; // MiB
const SIZE_T maxSize = memoryWorkingSetLimit ( ) * UNIT_SIZE ;
const SIZE_T minSize = std : : min < SIZE_T > ( ( 64 * UNIT_SIZE ) , ( maxSize / 2 ) ) ;
if ( ! : : SetProcessWorkingSetSizeEx ( : : GetCurrentProcess ( ) , minSize , maxSize , QUOTA_LIMITS_HARDWS_MAX_ENABLE ) )
{
const DWORD errorCode = : : GetLastError ( ) ;
QString message ;
LPVOID lpMsgBuf = nullptr ;
if ( : : FormatMessageW ( ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS )
, nullptr , errorCode , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , reinterpret_cast < LPWSTR > ( & lpMsgBuf ) , 0 , nullptr ) )
{
message = QString : : fromWCharArray ( reinterpret_cast < LPWSTR > ( lpMsgBuf ) ) . trimmed ( ) ;
: : LocalFree ( lpMsgBuf ) ;
}
LogMsg ( tr ( " Failed to set physical memory (RAM) usage limit. Error code: %1. Error message: \" %2 \" " )
. arg ( QString : : number ( errorCode ) , message ) , Log : : WARNING ) ;
}
}
# endif
2015-01-28 12:03:22 +03:00
void Application : : cleanup ( )
{
2015-04-16 00:47:59 +03:00
// cleanup() can be called multiple times during shutdown. We only need it once.
2016-03-09 21:00:26 +03:00
static QAtomicInt alreadyDone ;
if ( ! alreadyDone . testAndSetAcquire ( 0 , 1 ) )
2015-04-16 00:47:59 +03:00
return ;
2017-11-05 15:56:13 +03:00
# ifndef DISABLE_GUI
2020-11-16 10:02:11 +03:00
if ( m_window )
{
2018-05-31 12:19:07 +03:00
// Hide the window and don't leave it on screen as
2017-11-05 15:56:13 +03:00
// unresponsive. Also for Windows take the WinId
// after it's hidden, because hide() may cause a
// WinId change.
m_window - > hide ( ) ;
2015-04-16 00:47:59 +03:00
# ifdef Q_OS_WIN
2019-09-28 10:46:26 +03:00
: : ShutdownBlockReasonCreate ( reinterpret_cast < HWND > ( m_window - > effectiveWinId ( ) )
, tr ( " Saving torrent progress... " ) . toStdWString ( ) . c_str ( ) ) ;
2015-04-16 00:47:59 +03:00
# endif // Q_OS_WIN
2017-11-05 15:56:13 +03:00
// Do manual cleanup in MainWindow to force widgets
// to save their Preferences, stop all timers and
// delete as many widgets as possible to leave only
// a 'shell' MainWindow.
// We need a valid window handle for Windows Vista+
// otherwise the system shutdown will continue even
// though we created a ShutdownBlockReason
m_window - > cleanup ( ) ;
}
2015-04-16 00:47:59 +03:00
# endif // DISABLE_GUI
2015-01-28 12:03:22 +03:00
# ifndef DISABLE_WEBUI
delete m_webui ;
# endif
2015-04-19 18:17:47 +03:00
2017-03-07 16:10:42 +03:00
delete RSS : : AutoDownloader : : instance ( ) ;
delete RSS : : Session : : instance ( ) ;
2021-04-23 12:02:25 +03:00
TorrentFilesWatcher : : freeInstance ( ) ;
2015-04-19 18:17:47 +03:00
BitTorrent : : Session : : freeInstance ( ) ;
2015-05-18 17:02:48 +03:00
Net : : GeoIPManager : : freeInstance ( ) ;
2015-12-18 14:30:31 +03:00
Net : : DownloadManager : : freeInstance ( ) ;
2016-05-01 11:05:52 +03:00
Net : : ProxyConfigurationManager : : freeInstance ( ) ;
2015-04-19 18:17:47 +03:00
Preferences : : freeInstance ( ) ;
2016-02-09 11:55:02 +03:00
SettingsStorage : : freeInstance ( ) ;
2016-01-25 02:06:06 +03:00
delete m_fileLogger ;
2015-04-19 18:17:47 +03:00
Logger : : freeInstance ( ) ;
IconProvider : : freeInstance ( ) ;
2018-11-21 06:58:26 +03:00
SearchPluginManager : : freeInstance ( ) ;
2022-02-08 06:03:48 +03:00
Utils : : Fs : : removeDirRecursively ( Utils : : Fs : : tempPath ( ) ) ;
2016-04-16 08:50:41 +03:00
2015-04-16 00:47:59 +03:00
# ifndef DISABLE_GUI
2020-11-16 10:02:11 +03:00
if ( m_window )
{
2015-04-16 00:47:59 +03:00
# ifdef Q_OS_WIN
2019-09-28 10:46:26 +03:00
: : ShutdownBlockReasonDestroy ( reinterpret_cast < HWND > ( m_window - > effectiveWinId ( ) ) ) ;
2015-04-16 00:47:59 +03:00
# endif // Q_OS_WIN
2017-11-05 15:56:13 +03:00
delete m_window ;
2019-07-09 17:26:55 +03:00
UIThemeManager : : freeInstance ( ) ;
2017-11-05 15:56:13 +03:00
}
2016-04-16 08:50:41 +03:00
# endif // DISABLE_GUI
2020-02-11 05:56:04 +03:00
Profile : : freeInstance ( ) ;
2020-11-16 10:02:11 +03:00
if ( m_shutdownAct ! = ShutdownDialogAction : : Exit )
{
2015-04-19 18:17:47 +03:00
qDebug ( ) < < " Sending computer shutdown/suspend/hibernate signal... " ;
2015-05-06 14:53:27 +03:00
Utils : : Misc : : shutdownComputer ( m_shutdownAct ) ;
2015-04-19 18:17:47 +03:00
}
2015-01-28 12:03:22 +03:00
}