49 #include <QLibraryInfo>
53 #include <QMessageBox>
54 #include <QPixmapCache>
56 #include <QSessionManager>
57 #include <QSharedMemory>
60 #include <QFileOpenEvent>
84 #include "base/version.h"
102 #define SETTINGS_KEY(name) "Application/" name
103 #define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY("FileLogger/") name)
114 #if !defined(DISABLE_GUI)
132 qRegisterMetaType<Log::Msg>(
"Log::Msg");
133 qRegisterMetaType<Log::Peer>(
"Log::Peer");
135 setApplicationName(
"qBittorrent");
136 setOrganizationDomain(
"qbittorrent.org");
137 #if !defined(DISABLE_GUI)
138 setDesktopFileName(
"org.qbittorrent.qBittorrent");
139 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
140 setAttribute(Qt::AA_UseHighDpiPixmaps,
true);
142 setQuitOnLastWindowClosed(
false);
149 const QString profileDir = portableModeEnabled
168 #if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
169 connect(
this, &QGuiApplication::commitDataRequest,
this, &Application::shutdownCleanup, Qt::DirectConnection);
176 if (portableModeEnabled)
274 return std::min(std::max(val, 1), 365);
285 return ((val < 0) || (val > 2)) ? 1 : val;
295 const QStringList params = message.split(
PARAMS_SEPARATOR, Qt::SkipEmptyParts);
306 #if defined(Q_OS_WIN)
307 const auto chopPathSep = [](
const QString &str) -> QString
309 if (str.endsWith(
'\\'))
310 return str.mid(0, (str.length() -1));
317 for (
int i = (program.length() - 2); i >= 0; --i)
319 if (program[i] != QLatin1Char(
'%'))
322 const ushort specifier = program[i + 1].unicode();
326 program.replace(i, 2, QString::number(torrent->
filesCount()));
329 #if defined(Q_OS_WIN)
336 #if defined(Q_OS_WIN)
343 program.replace(i, 2, torrent->
tags().
join(QLatin1String(
",")));
352 program.replace(i, 2, torrent->
id().
toString());
355 program.replace(i, 2, torrent->
category());
358 program.replace(i, 2, torrent->
name());
361 #if defined(Q_OS_WIN)
371 program.replace(i, 2, QString::number(torrent->
totalSize()));
382 LogMsg(tr(
"Torrent: %1, running external program, command: %2").arg(torrent->
name(), program));
384 #if defined(Q_OS_WIN)
385 auto programWchar = std::make_unique<wchar_t[]>(program.length() + 1);
386 program.toWCharArray(programWchar.get());
392 std::unique_ptr<LPWSTR[], decltype(&::LocalFree)>
args {::CommandLineToArgvW(programWchar.get(), &argCount), ::LocalFree};
395 for (
int i = 1; i < argCount; ++i)
396 argList += QString::fromWCharArray(
args[i]);
399 proc.setProgram(QString::fromWCharArray(
args[0]));
400 proc.setArguments(argList);
401 proc.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments *
args)
405 args->flags |= CREATE_NEW_CONSOLE;
406 args->flags &= ~(CREATE_NO_WINDOW | DETACHED_PROCESS);
410 args->flags |= CREATE_NO_WINDOW;
411 args->flags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
413 args->inheritHandles =
false;
414 args->startupInfo->dwFlags &= ~STARTF_USESTDHANDLES;
415 ::CloseHandle(
args->startupInfo->hStdInput);
416 ::CloseHandle(
args->startupInfo->hStdOutput);
417 ::CloseHandle(
args->startupInfo->hStdError);
418 args->startupInfo->hStdInput =
nullptr;
419 args->startupInfo->hStdOutput =
nullptr;
420 args->startupInfo->hStdError =
nullptr;
422 proc.startDetached();
428 QStringList
args = QProcess::splitCommand(program);
432 const QString command =
args.takeFirst();
433 QProcess::startDetached(command,
args);
440 const QString content = tr(
"Torrent name: %1").arg(torrent->
name()) +
'\n'
442 + tr(
"Save path: %1").arg(torrent->
savePath()) +
"\n\n"
443 + tr(
"The torrent was downloaded in %1.",
"The torrent was downloaded in 1 hour and 20 seconds")
445 + tr(
"Thank you for using qBittorrent.") +
'\n';
452 tr(
"[qBittorrent] '%1' has finished downloading").arg(torrent->
name()),
480 bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
481 if (!haveAction)
return;
486 else if (isHibernate)
506 qDebug(
"Preparing for auto-shutdown because all downloads are complete!");
515 qDebug(
"Exiting the application");
531 if (params.isEmpty())
538 std::optional<bool> skipTorrentDialog;
540 for (QString param : params)
542 param = param.trimmed();
546 if (param.startsWith(QLatin1String(
"@savePath=")))
548 torrentParams.
savePath = param.mid(10);
552 if (param.startsWith(QLatin1String(
"@addPaused=")))
554 torrentParams.
addPaused = (QStringView(param).mid(11).toInt() != 0);
558 if (param == QLatin1String(
"@skipChecking"))
564 if (param.startsWith(QLatin1String(
"@category=")))
566 torrentParams.
category = param.mid(10);
570 if (param == QLatin1String(
"@sequential"))
576 if (param == QLatin1String(
"@firstLastPiecePriority"))
582 if (param.startsWith(QLatin1String(
"@skipDialog=")))
584 skipTorrentDialog = (QStringView(param).mid(12).toInt() != 0);
595 if (showDialogForThisTorrent)
618 #ifndef DISABLE_WEBUI
633 fprintf(stderr,
"%s", qPrintable(err.
message()));
636 msgBox.setIcon(QMessageBox::Critical);
637 msgBox.setText(tr(
"Application failed to start."));
638 msgBox.setInformativeText(err.
message());
647 #ifndef DISABLE_WEBUI
651 const auto url = QString::fromLatin1(
"%1://localhost:%2\n").arg(scheme, QString::number(pref->
getWebUiPort()));
652 const QString mesg = QString::fromLatin1(
"\n******** %1 ********\n").arg(tr(
"Information"))
653 + tr(
"To control qBittorrent, access the WebUI at: %1").arg(url);
654 printf(
"%s\n", qUtf8Printable(mesg));
656 if (pref->
getWebUIPassword() ==
"ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==")
658 const QString warning = tr(
"The Web UI administrator username is: %1").arg(pref->
getWebUiUsername()) +
'\n'
659 + tr(
"The Web UI administrator password has not been changed from the default: %1").arg(
"adminadmin") +
'\n'
660 + tr(
"This is a security risk, please change your password in program preferences.") +
'\n';
661 printf(
"%s", qUtf8Printable(warning));
680 return BaseApplication::exec();
690 bool Application::event(QEvent *ev)
692 if (ev->type() == QEvent::FileOpen)
694 QString path =
static_cast<QFileOpenEvent *
>(ev)->
file();
697 path =
static_cast<QFileOpenEvent *
>(ev)->url().toString();
698 qDebug(
"Received a mac file open event: %s", qUtf8Printable(path));
707 return BaseApplication::event(ev);
717 const QString localeStr = pref->
getLocale();
719 if (
m_qtTranslator.load(QLatin1String(
"qtbase_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath)) ||
720 m_qtTranslator.load(QLatin1String(
"qt_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
721 qDebug(
"Qt %s locale recognized, using translation.", qUtf8Printable(localeStr));
723 qDebug(
"Qt %s locale unrecognized, using default (en).", qUtf8Printable(localeStr));
727 if (
m_translator.load(QLatin1String(
":/lang/qbittorrent_") + localeStr))
728 qDebug(
"%s locale recognized, using translation.", qUtf8Printable(localeStr));
730 qDebug(
"%s locale unrecognized, using default (en).", qUtf8Printable(localeStr));
734 if (localeStr.startsWith(
"ar") || localeStr.startsWith(
"he"))
736 qDebug(
"Right to Left mode");
737 setLayoutDirection(Qt::RightToLeft);
741 setLayoutDirection(Qt::LeftToRight);
746 #if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
747 void Application::shutdownCleanup(QSessionManager &manager)
770 QTimer::singleShot(0, qApp, &QCoreApplication::quit);
777 static QAtomicInt alreadyDone;
778 if (!alreadyDone.testAndSetAcquire(0, 1))
791 ::ShutdownBlockReasonCreate(
reinterpret_cast<HWND
>(
m_window->effectiveWinId())
792 , tr(
"Saving torrent progress...").toStdWString().c_str());
806 #ifndef DISABLE_WEBUI
830 ::ShutdownBlockReasonDestroy(
reinterpret_cast<HWND
>(
m_window->effectiveWinId()));
841 qDebug() <<
"Sending computer shutdown/suspend/hibernate signal...";
#define FILELOGGER_SETTINGS_KEY(name)
QApplication BaseApplication
static void show(const QString &source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
void setFileLoggerDeleteOld(bool value)
QStringList m_paramsQueue
int fileLoggerAge() const
QPointer< FileLogger > m_fileLogger
void setFileLoggerBackup(bool value)
SettingValue< bool > m_storeFileLoggerDeleteOld
int fileLoggerAgeType() const
bool isFileLoggerEnabled() const
QBtCommandLineParameters m_commandLineArgs
bool isFileLoggerDeleteOld() const
QPointer< MainWindow > m_window
SettingValue< QString > m_storeFileLoggerPath
void sendNotificationEmail(const BitTorrent::Torrent *torrent)
SettingValue< bool > m_storeFileLoggerEnabled
bool isFileLoggerBackup() const
SettingValue< bool > m_storeFileLoggerBackup
QPointer< MainWindow > mainWindow()
SettingValue< int > m_storeFileLoggerAge
void setFileLoggerAgeType(int value)
SettingValue< int > m_storeFileLoggerMaxSize
void setFileLoggerAge(int value)
Application(int &argc, char **argv)
void torrentFinished(BitTorrent::Torrent *const torrent)
int exec(const QStringList ¶ms)
void allTorrentsFinished()
QString fileLoggerPath() const
void processParams(const QStringList ¶ms)
void processMessage(const QString &message)
SettingValue< int > m_storeFileLoggerAgeType
ApplicationInstanceManager * m_instanceManager
QTranslator m_qtTranslator
const QBtCommandLineParameters & commandLineArgs() const
void setFileLoggerEnabled(bool value)
void initializeTranslation()
void setFileLoggerMaxSize(int bytes)
int fileLoggerMaxSize() const
void setFileLoggerPath(const QString &path)
void runExternalProgram(const BitTorrent::Torrent *torrent) const
ShutdownDialogAction m_shutdownAct
bool sendParams(const QStringList ¶ms)
void messageReceived(const QString &message)
bool isFirstInstance() const
bool sendMessage(const QString &message, int timeout=5000)
virtual int filesCount() const =0
static Session * instance()
static void initInstance()
void torrentFinished(Torrent *torrent)
bool addTorrent(const QString &source, const AddTorrentParams ¶ms=AddTorrentParams())
void allTorrentsFinished()
static void freeInstance()
virtual qlonglong totalSize() const =0
virtual TagSet tags() const =0
virtual QString currentTracker() const =0
virtual QString category() const =0
virtual qlonglong activeTime() const =0
virtual InfoHash infoHash() const =0
virtual QString savePath() const =0
virtual QString rootPath() const =0
virtual qlonglong wantedSize() const =0
virtual QString name() const =0
virtual QString contentPath() const =0
QString message() const noexcept
static void freeInstance()
static void initInstance()
static void initInstance()
void addMessage(const QString &message, const Log::MsgType &type=Log::NORMAL)
static Logger * instance()
static void freeInstance()
static void freeInstance()
static void initInstance()
static void initInstance()
static void freeInstance()
static void initInstance()
static void freeInstance()
QString join(const QString &separator) const
bool shutdownqBTWhenDownloadsComplete() const
QString getAutoRunProgram() const
static Preferences * instance()
bool shutdownWhenDownloadsComplete() const
void setSuspendWhenDownloadsComplete(bool suspend)
QString getMailNotificationEmail() const
bool suspendWhenDownloadsComplete() const
QString getMailNotificationSender() const
static void initInstance()
QByteArray getWebUIPassword() const
static void freeInstance()
bool dontConfirmAutoExit() const
QString getLocale() const
void setShutdownWhenDownloadsComplete(bool shutdown)
void setWebUiPort(quint16 port)
quint16 getWebUiPort() const
QString getWebUiUsername() const
void setHibernateWhenDownloadsComplete(bool hibernate)
bool isMailNotificationEnabled() const
bool isWebUiHttpsEnabled() const
bool isAutoRunEnabled() const
bool hibernateWhenDownloadsComplete() const
static void freeInstance()
QString location(SpecialFolder folder) const
static void initInstance(const QString &rootProfilePath, const QString &configurationName, bool convertPathsToProfileRelative)
static const Profile * instance()
static AutoDownloader * instance()
static Session * instance()
static void freeInstance()
T get(const T &defaultValue={}) const
static void freeInstance()
static void initInstance()
static bool askForConfirmation(QWidget *parent, const ShutdownDialogAction &action)
static void initInstance()
static void freeInstance()
static void freeInstance()
static void initInstance()
QBtCommandLineParameters parseCommandLine(const QStringList &args)
void LogMsg(const QString &message, const Log::MsgType &type)
void removeDirRecursive(const QString &path)
QString toNativePath(const QString &path)
QPoint screenCenter(const QWidget *w)
void shutdownComputer(const ShutdownDialogAction &action)
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap=-1)
QString friendlyUnit(qint64 bytes, bool isSpeed=false)
const int MAX_FILELOG_SIZE
const QChar PARAMS_SEPARATOR
const int MIN_FILELOG_SIZE
const int PIXMAP_CACHE_SIZE
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR
const int DEFAULT_FILELOG_SIZE
T value(const QString &key, const T &defaultValue={})
QString specialFolderLocation(const SpecialFolder folder)
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts") set_source_files_properties($
bool firstLastPiecePriority
std::optional< bool > addPaused
bool relativeFastresumePaths
QString configurationName