qBittorrent
application.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2015 Vladimir Golovnev <[email protected]>
4  * Copyright (C) 2006 Christophe Dumez
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give permission to
21  * link this program with the OpenSSL project's "OpenSSL" library (or with
22  * modified versions of it that use the same license as the "OpenSSL" library),
23  * and distribute the linked executables. You must obey the GNU General Public
24  * License in all respects for all of the code used other than "OpenSSL". If you
25  * modify file(s), you may extend this exception to your version of the file(s),
26  * but you are not obligated to do so. If you do not wish to do so, delete this
27  * exception statement from your version.
28  */
29 
30 #include "application.h"
31 
32 #include <QtGlobal>
33 
34 #include <algorithm>
35 
36 #ifdef DISABLE_GUI
37 #include <cstdio>
38 #endif
39 
40 #ifdef Q_OS_WIN
41 #include <memory>
42 #include <Windows.h>
43 #include <Shellapi.h>
44 #endif
45 
46 #include <QAtomicInt>
47 #include <QDebug>
48 #include <QDir>
49 #include <QLibraryInfo>
50 #include <QProcess>
51 
52 #ifndef DISABLE_GUI
53 #include <QMessageBox>
54 #include <QPixmapCache>
55 #ifdef Q_OS_WIN
56 #include <QSessionManager>
57 #include <QSharedMemory>
58 #endif // Q_OS_WIN
59 #ifdef Q_OS_MACOS
60 #include <QFileOpenEvent>
61 #endif // Q_OS_MACOS
62 #endif
63 
67 #include "base/exceptions.h"
68 #include "base/iconprovider.h"
69 #include "base/logger.h"
71 #include "base/net/geoipmanager.h"
73 #include "base/net/smtp.h"
74 #include "base/preferences.h"
75 #include "base/profile.h"
77 #include "base/rss/rss_session.h"
79 #include "base/settingsstorage.h"
81 #include "base/utils/compare.h"
82 #include "base/utils/fs.h"
83 #include "base/utils/misc.h"
84 #include "base/version.h"
86 #include "filelogger.h"
87 
88 #ifndef DISABLE_GUI
90 #include "gui/uithememanager.h"
91 #include "gui/utils.h"
92 #include "gui/mainwindow.h"
94 #endif // DISABLE_GUI
95 
96 #ifndef DISABLE_WEBUI
97 #include "webui/webui.h"
98 #endif
99 
100 namespace
101 {
102 #define SETTINGS_KEY(name) "Application/" name
103 #define FILELOGGER_SETTINGS_KEY(name) (SETTINGS_KEY("FileLogger/") name)
104 
105  const QString LOG_FOLDER = QStringLiteral("logs");
106  const QChar PARAMS_SEPARATOR = QLatin1Char('|');
107 
108  const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QStringLiteral("profile");
109 
110  const int MIN_FILELOG_SIZE = 1024; // 1KiB
111  const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
112  const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB
113 
114 #if !defined(DISABLE_GUI)
115  const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB
116 #endif
117 }
118 
119 Application::Application(int &argc, char **argv)
120  : BaseApplication(argc, argv)
121  , m_running(false)
122  , m_shutdownAct(ShutdownDialogAction::Exit)
123  , m_commandLineArgs(parseCommandLine(this->arguments()))
124  , m_storeFileLoggerEnabled(FILELOGGER_SETTINGS_KEY("Enabled"))
125  , m_storeFileLoggerBackup(FILELOGGER_SETTINGS_KEY("Backup"))
126  , m_storeFileLoggerDeleteOld(FILELOGGER_SETTINGS_KEY("DeleteOld"))
127  , m_storeFileLoggerMaxSize(FILELOGGER_SETTINGS_KEY("MaxSizeBytes"))
128  , m_storeFileLoggerAge(FILELOGGER_SETTINGS_KEY("Age"))
129  , m_storeFileLoggerAgeType(FILELOGGER_SETTINGS_KEY("AgeType"))
130  , m_storeFileLoggerPath(FILELOGGER_SETTINGS_KEY("Path"))
131 {
132  qRegisterMetaType<Log::Msg>("Log::Msg");
133  qRegisterMetaType<Log::Peer>("Log::Peer");
134 
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); // opt-in to the high DPI pixmap support
141 #endif
142  setQuitOnLastWindowClosed(false);
143  QPixmapCache::setCacheLimit(PIXMAP_CACHE_SIZE);
144 #endif
145 
146  const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty()
147  && QDir(QCoreApplication::applicationDirPath()).exists(DEFAULT_PORTABLE_MODE_PROFILE_DIR);
148 
149  const QString profileDir = portableModeEnabled
150  ? QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(DEFAULT_PORTABLE_MODE_PROFILE_DIR)
153  (m_commandLineArgs.relativeFastresumePaths || portableModeEnabled));
154 
156 
160 
162 
163  if (m_commandLineArgs.webUiPort > 0) // it will be -1 when user did not set any value
165 
166  connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
168 #if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
169  connect(this, &QGuiApplication::commitDataRequest, this, &Application::shutdownCleanup, Qt::DirectConnection);
170 #endif
171 
172  if (isFileLoggerEnabled())
174 
175  Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(QBT_VERSION));
176  if (portableModeEnabled)
177  {
178  Logger::instance()->addMessage(tr("Running in portable mode. Auto detected profile folder at: %1").arg(profileDir));
180  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
181  }
182  else
183  {
184  Logger::instance()->addMessage(tr("Using config directory: %1").arg(Profile::instance()->location(SpecialFolder::Config)));
185  }
186 }
187 
189 {
190  // we still need to call cleanup()
191  // in case the App failed to start
192  cleanup();
193 }
194 
195 #ifndef DISABLE_GUI
196 QPointer<MainWindow> Application::mainWindow()
197 {
198  return m_window;
199 }
200 #endif
201 
203 {
204  return m_commandLineArgs;
205 }
206 
208 {
209  return m_storeFileLoggerEnabled.get(true);
210 }
211 
213 {
214  if (value && !m_fileLogger)
216  else if (!value)
217  delete m_fileLogger;
219 }
220 
222 {
224 }
225 
226 void Application::setFileLoggerPath(const QString &path)
227 {
228  if (m_fileLogger)
229  m_fileLogger->changePath(path);
230  m_storeFileLoggerPath = path;
231 }
232 
234 {
235  return m_storeFileLoggerBackup.get(true);
236 }
237 
239 {
240  if (m_fileLogger)
241  m_fileLogger->setBackup(value);
243 }
244 
246 {
247  return m_storeFileLoggerDeleteOld.get(true);
248 }
249 
251 {
252  if (value && m_fileLogger)
255 }
256 
258 {
260  return std::min(std::max(val, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
261 }
262 
264 {
265  const int clampedValue = std::min(std::max(bytes, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
266  if (m_fileLogger)
267  m_fileLogger->setMaxSize(clampedValue);
268  m_storeFileLoggerMaxSize = clampedValue;
269 }
270 
272 {
273  const int val = m_storeFileLoggerAge.get(1);
274  return std::min(std::max(val, 1), 365);
275 }
276 
278 {
279  m_storeFileLoggerAge = std::min(std::max(value, 1), 365);
280 }
281 
283 {
284  const int val = m_storeFileLoggerAgeType.get(1);
285  return ((val < 0) || (val > 2)) ? 1 : val;
286 }
287 
289 {
290  m_storeFileLoggerAgeType = ((value < 0) || (value > 2)) ? 1 : value;
291 }
292 
293 void Application::processMessage(const QString &message)
294 {
295  const QStringList params = message.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts);
296  // If Application is not running (i.e., other
297  // components are not ready) store params
298  if (m_running)
299  processParams(params);
300  else
301  m_paramsQueue.append(params);
302 }
303 
305 {
306 #if defined(Q_OS_WIN)
307  const auto chopPathSep = [](const QString &str) -> QString
308  {
309  if (str.endsWith('\\'))
310  return str.mid(0, (str.length() -1));
311  return str;
312  };
313 #endif
314 
315  QString program = Preferences::instance()->getAutoRunProgram().trimmed();
316 
317  for (int i = (program.length() - 2); i >= 0; --i)
318  {
319  if (program[i] != QLatin1Char('%'))
320  continue;
321 
322  const ushort specifier = program[i + 1].unicode();
323  switch (specifier)
324  {
325  case u'C':
326  program.replace(i, 2, QString::number(torrent->filesCount()));
327  break;
328  case u'D':
329 #if defined(Q_OS_WIN)
330  program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
331 #else
332  program.replace(i, 2, Utils::Fs::toNativePath(torrent->savePath()));
333 #endif
334  break;
335  case u'F':
336 #if defined(Q_OS_WIN)
337  program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
338 #else
339  program.replace(i, 2, Utils::Fs::toNativePath(torrent->contentPath()));
340 #endif
341  break;
342  case u'G':
343  program.replace(i, 2, torrent->tags().join(QLatin1String(",")));
344  break;
345  case u'I':
346  program.replace(i, 2, (torrent->infoHash().v1().isValid() ? torrent->infoHash().v1().toString() : QLatin1String("-")));
347  break;
348  case u'J':
349  program.replace(i, 2, (torrent->infoHash().v2().isValid() ? torrent->infoHash().v2().toString() : QLatin1String("-")));
350  break;
351  case u'K':
352  program.replace(i, 2, torrent->id().toString());
353  break;
354  case u'L':
355  program.replace(i, 2, torrent->category());
356  break;
357  case u'N':
358  program.replace(i, 2, torrent->name());
359  break;
360  case u'R':
361 #if defined(Q_OS_WIN)
362  program.replace(i, 2, chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
363 #else
364  program.replace(i, 2, Utils::Fs::toNativePath(torrent->rootPath()));
365 #endif
366  break;
367  case u'T':
368  program.replace(i, 2, torrent->currentTracker());
369  break;
370  case u'Z':
371  program.replace(i, 2, QString::number(torrent->totalSize()));
372  break;
373  default:
374  // do nothing
375  break;
376  }
377 
378  // decrement `i` to avoid unwanted replacement, example pattern: "%%N"
379  --i;
380  }
381 
382  LogMsg(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program));
383 
384 #if defined(Q_OS_WIN)
385  auto programWchar = std::make_unique<wchar_t[]>(program.length() + 1);
386  program.toWCharArray(programWchar.get());
387 
388  // Need to split arguments manually because QProcess::startDetached(QString)
389  // will strip off empty parameters.
390  // E.g. `python.exe "1" "" "3"` will become `python.exe "1" "3"`
391  int argCount = 0;
392  std::unique_ptr<LPWSTR[], decltype(&::LocalFree)> args {::CommandLineToArgvW(programWchar.get(), &argCount), ::LocalFree};
393 
394  QStringList argList;
395  for (int i = 1; i < argCount; ++i)
396  argList += QString::fromWCharArray(args[i]);
397 
398  QProcess proc;
399  proc.setProgram(QString::fromWCharArray(args[0]));
400  proc.setArguments(argList);
401  proc.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments *args)
402  {
403  if (Preferences::instance()->isAutoRunConsoleEnabled())
404  {
405  args->flags |= CREATE_NEW_CONSOLE;
406  args->flags &= ~(CREATE_NO_WINDOW | DETACHED_PROCESS);
407  }
408  else
409  {
410  args->flags |= CREATE_NO_WINDOW;
411  args->flags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
412  }
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;
421  });
422  proc.startDetached();
423 #else // Q_OS_WIN
424  // Cannot give users shell environment by default, as doing so could
425  // enable command injection via torrent name and other arguments
426  // (especially when some automated download mechanism has been setup).
427  // See: https://github.com/qbittorrent/qBittorrent/issues/10925
428  QStringList args = QProcess::splitCommand(program);
429  if (args.isEmpty())
430  return;
431 
432  const QString command = args.takeFirst();
433  QProcess::startDetached(command, args);
434 #endif
435 }
436 
438 {
439  // Prepare mail content
440  const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
441  + tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\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")
444  .arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
445  + tr("Thank you for using qBittorrent.") + '\n';
446 
447  // Send the notification email
448  const Preferences *pref = Preferences::instance();
449  auto *smtp = new Net::Smtp(this);
450  smtp->sendMail(pref->getMailNotificationSender(),
451  pref->getMailNotificationEmail(),
452  tr("[qBittorrent] '%1' has finished downloading").arg(torrent->name()),
453  content);
454 }
455 
457 {
458  Preferences *const pref = Preferences::instance();
459 
460  // AutoRun program
461  if (pref->isAutoRunEnabled())
462  runExternalProgram(torrent);
463 
464  // Mail notification
465  if (pref->isMailNotificationEnabled())
466  {
467  Logger::instance()->addMessage(tr("Torrent: %1, sending mail notification").arg(torrent->name()));
468  sendNotificationEmail(torrent);
469  }
470 }
471 
473 {
474  Preferences *const pref = Preferences::instance();
475  bool isExit = pref->shutdownqBTWhenDownloadsComplete();
476  bool isShutdown = pref->shutdownWhenDownloadsComplete();
477  bool isSuspend = pref->suspendWhenDownloadsComplete();
478  bool isHibernate = pref->hibernateWhenDownloadsComplete();
479 
480  bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
481  if (!haveAction) return;
482 
484  if (isSuspend)
486  else if (isHibernate)
488  else if (isShutdown)
490 
491 #ifndef DISABLE_GUI
492  // ask confirm
494  {
495  // do nothing & skip confirm
496  }
497  else
498  {
500  }
501 #endif // DISABLE_GUI
502 
503  // Actually shut down
505  {
506  qDebug("Preparing for auto-shutdown because all downloads are complete!");
507  // Disabling it for next time
509  pref->setSuspendWhenDownloadsComplete(false);
511  // Make sure preferences are synced before exiting
513  }
514 
515  qDebug("Exiting the application");
516  exit();
517 }
518 
519 bool Application::sendParams(const QStringList &params)
520 {
521  return m_instanceManager->sendMessage(params.join(PARAMS_SEPARATOR));
522 }
523 
524 // As program parameters, we can get paths or urls.
525 // This function parse the parameters and call
526 // the right addTorrent function, considering
527 // the parameter type.
528 void Application::processParams(const QStringList &params)
529 {
530 #ifndef DISABLE_GUI
531  if (params.isEmpty())
532  {
533  m_window->activate(); // show UI
534  return;
535  }
536 #endif
537  BitTorrent::AddTorrentParams torrentParams;
538  std::optional<bool> skipTorrentDialog;
539 
540  for (QString param : params)
541  {
542  param = param.trimmed();
543 
544  // Process strings indicating options specified by the user.
545 
546  if (param.startsWith(QLatin1String("@savePath=")))
547  {
548  torrentParams.savePath = param.mid(10);
549  continue;
550  }
551 
552  if (param.startsWith(QLatin1String("@addPaused=")))
553  {
554  torrentParams.addPaused = (QStringView(param).mid(11).toInt() != 0);
555  continue;
556  }
557 
558  if (param == QLatin1String("@skipChecking"))
559  {
560  torrentParams.skipChecking = true;
561  continue;
562  }
563 
564  if (param.startsWith(QLatin1String("@category=")))
565  {
566  torrentParams.category = param.mid(10);
567  continue;
568  }
569 
570  if (param == QLatin1String("@sequential"))
571  {
572  torrentParams.sequential = true;
573  continue;
574  }
575 
576  if (param == QLatin1String("@firstLastPiecePriority"))
577  {
578  torrentParams.firstLastPiecePriority = true;
579  continue;
580  }
581 
582  if (param.startsWith(QLatin1String("@skipDialog=")))
583  {
584  skipTorrentDialog = (QStringView(param).mid(12).toInt() != 0);
585  continue;
586  }
587 
588 #ifndef DISABLE_GUI
589  // There are two circumstances in which we want to show the torrent
590  // dialog. One is when the application settings specify that it should
591  // be shown and skipTorrentDialog is undefined. The other is when
592  // skipTorrentDialog is false, meaning that the application setting
593  // should be overridden.
594  const bool showDialogForThisTorrent = !skipTorrentDialog.value_or(!AddNewTorrentDialog::isEnabled());
595  if (showDialogForThisTorrent)
596  AddNewTorrentDialog::show(param, torrentParams, m_window);
597  else
598 #endif
599  BitTorrent::Session::instance()->addTorrent(param, torrentParams);
600  }
601 }
602 
603 int Application::exec(const QStringList &params)
604 {
608 
609  try
610  {
614 
617 
618 #ifndef DISABLE_WEBUI
619  m_webui = new WebUI;
620 #ifdef DISABLE_GUI
621  if (m_webui->isErrored())
622  return 1;
623  connect(m_webui, &WebUI::fatalError, this, []() { QCoreApplication::exit(1); });
624 #endif // DISABLE_GUI
625 #endif // DISABLE_WEBUI
626 
627  new RSS::Session; // create RSS::Session singleton
628  new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
629  }
630  catch (const RuntimeError &err)
631  {
632 #ifdef DISABLE_GUI
633  fprintf(stderr, "%s", qPrintable(err.message()));
634 #else
635  QMessageBox msgBox;
636  msgBox.setIcon(QMessageBox::Critical);
637  msgBox.setText(tr("Application failed to start."));
638  msgBox.setInformativeText(err.message());
639  msgBox.show(); // Need to be shown or to moveToCenter does not work
640  msgBox.move(Utils::Gui::screenCenter(&msgBox));
641  msgBox.exec();
642 #endif
643  return 1;
644  }
645 
646 #ifdef DISABLE_GUI
647 #ifndef DISABLE_WEBUI
648  const Preferences *pref = Preferences::instance();
649 
650  const auto scheme = QString::fromLatin1(pref->isWebUiHttpsEnabled() ? "https" : "http");
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));
655 
656  if (pref->getWebUIPassword() == "ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==")
657  {
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));
662  }
663 #endif // DISABLE_WEBUI
664 #else
666  m_window = new MainWindow;
667 #endif // DISABLE_GUI
668 
669  m_running = true;
670 
671  // Now UI is ready to process signals from Session
673 
674  m_paramsQueue = params + m_paramsQueue;
675  if (!m_paramsQueue.isEmpty())
676  {
678  m_paramsQueue.clear();
679  }
680  return BaseApplication::exec();
681 }
682 
684 {
686 }
687 
688 #ifndef DISABLE_GUI
689 #ifdef Q_OS_MACOS
690 bool Application::event(QEvent *ev)
691 {
692  if (ev->type() == QEvent::FileOpen)
693  {
694  QString path = static_cast<QFileOpenEvent *>(ev)->file();
695  if (path.isEmpty())
696  // Get the url instead
697  path = static_cast<QFileOpenEvent *>(ev)->url().toString();
698  qDebug("Received a mac file open event: %s", qUtf8Printable(path));
699  if (m_running)
700  processParams(QStringList(path));
701  else
702  m_paramsQueue.append(path);
703  return true;
704  }
705  else
706  {
707  return BaseApplication::event(ev);
708  }
709 }
710 #endif // Q_OS_MACOS
711 #endif // DISABLE_GUI
712 
714 {
715  Preferences *const pref = Preferences::instance();
716  // Load translation
717  const QString localeStr = pref->getLocale();
718 
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));
722  else
723  qDebug("Qt %s locale unrecognized, using default (en).", qUtf8Printable(localeStr));
724 
725  installTranslator(&m_qtTranslator);
726 
727  if (m_translator.load(QLatin1String(":/lang/qbittorrent_") + localeStr))
728  qDebug("%s locale recognized, using translation.", qUtf8Printable(localeStr));
729  else
730  qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(localeStr));
731  installTranslator(&m_translator);
732 
733 #ifndef DISABLE_GUI
734  if (localeStr.startsWith("ar") || localeStr.startsWith("he"))
735  {
736  qDebug("Right to Left mode");
737  setLayoutDirection(Qt::RightToLeft);
738  }
739  else
740  {
741  setLayoutDirection(Qt::LeftToRight);
742  }
743 #endif
744 }
745 
746 #if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
747 void Application::shutdownCleanup(QSessionManager &manager)
748 {
749  Q_UNUSED(manager);
750 
751  // This is only needed for a special case on Windows XP.
752  // (but is called for every Windows version)
753  // If a process takes too much time to exit during OS
754  // shutdown, the OS presents a dialog to the user.
755  // That dialog tells the user that qbt is blocking the
756  // shutdown, it shows a progress bar and it offers
757  // a "Terminate Now" button for the user. However,
758  // after the progress bar has reached 100% another button
759  // is offered to the user reading "Cancel". With this the
760  // user can cancel the **OS** shutdown. If we don't do
761  // the cleanup by handling the commitDataRequest() signal
762  // and the user clicks "Cancel", it will result in qbt being
763  // killed and the shutdown proceeding instead. Apparently
764  // aboutToQuit() is emitted too late in the shutdown process.
765  cleanup();
766 
767  // According to the qt docs we shouldn't call quit() inside a slot.
768  // aboutToQuit() is never emitted if the user hits "Cancel" in
769  // the above dialog.
770  QTimer::singleShot(0, qApp, &QCoreApplication::quit);
771 }
772 #endif
773 
775 {
776  // cleanup() can be called multiple times during shutdown. We only need it once.
777  static QAtomicInt alreadyDone;
778  if (!alreadyDone.testAndSetAcquire(0, 1))
779  return;
780 
781 #ifndef DISABLE_GUI
782  if (m_window)
783  {
784  // Hide the window and don't leave it on screen as
785  // unresponsive. Also for Windows take the WinId
786  // after it's hidden, because hide() may cause a
787  // WinId change.
788  m_window->hide();
789 
790 #ifdef Q_OS_WIN
791  ::ShutdownBlockReasonCreate(reinterpret_cast<HWND>(m_window->effectiveWinId())
792  , tr("Saving torrent progress...").toStdWString().c_str());
793 #endif // Q_OS_WIN
794 
795  // Do manual cleanup in MainWindow to force widgets
796  // to save their Preferences, stop all timers and
797  // delete as many widgets as possible to leave only
798  // a 'shell' MainWindow.
799  // We need a valid window handle for Windows Vista+
800  // otherwise the system shutdown will continue even
801  // though we created a ShutdownBlockReason
802  m_window->cleanup();
803  }
804 #endif // DISABLE_GUI
805 
806 #ifndef DISABLE_WEBUI
807  delete m_webui;
808 #endif
809 
811  delete RSS::Session::instance();
812 
820  delete m_fileLogger;
825 
826 #ifndef DISABLE_GUI
827  if (m_window)
828  {
829 #ifdef Q_OS_WIN
830  ::ShutdownBlockReasonDestroy(reinterpret_cast<HWND>(m_window->effectiveWinId()));
831 #endif // Q_OS_WIN
832  delete m_window;
834  }
835 #endif // DISABLE_GUI
836 
838 
840  {
841  qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
843  }
844 }
#define FILELOGGER_SETTINGS_KEY(name)
QApplication BaseApplication
Definition: application.h:38
static void show(const QString &source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
void setFileLoggerDeleteOld(bool value)
QStringList m_paramsQueue
Definition: application.h:147
int fileLoggerAge() const
QPointer< FileLogger > m_fileLogger
Definition: application.h:143
void setFileLoggerBackup(bool value)
SettingValue< bool > m_storeFileLoggerDeleteOld
Definition: application.h:151
int fileLoggerAgeType() const
bool isFileLoggerEnabled() const
QBtCommandLineParameters m_commandLineArgs
Definition: application.h:132
bool isFileLoggerDeleteOld() const
QPointer< MainWindow > m_window
Definition: application.h:135
SettingValue< QString > m_storeFileLoggerPath
Definition: application.h:155
void sendNotificationEmail(const BitTorrent::Torrent *torrent)
WebUI * m_webui
Definition: application.h:139
~Application() override
SettingValue< bool > m_storeFileLoggerEnabled
Definition: application.h:149
QTranslator m_translator
Definition: application.h:146
bool isFileLoggerBackup() const
SettingValue< bool > m_storeFileLoggerBackup
Definition: application.h:150
QPointer< MainWindow > mainWindow()
SettingValue< int > m_storeFileLoggerAge
Definition: application.h:153
void setFileLoggerAgeType(int value)
SettingValue< int > m_storeFileLoggerMaxSize
Definition: application.h:152
void setFileLoggerAge(int value)
Application(int &argc, char **argv)
void torrentFinished(BitTorrent::Torrent *const torrent)
bool m_running
Definition: application.h:130
int exec(const QStringList &params)
void allTorrentsFinished()
QString fileLoggerPath() const
void processParams(const QStringList &params)
void processMessage(const QString &message)
SettingValue< int > m_storeFileLoggerAgeType
Definition: application.h:154
ApplicationInstanceManager * m_instanceManager
Definition: application.h:129
QTranslator m_qtTranslator
Definition: application.h:145
void cleanup()
const QBtCommandLineParameters & commandLineArgs() const
void setFileLoggerEnabled(bool value)
void initializeTranslation()
void setFileLoggerMaxSize(int bytes)
int fileLoggerMaxSize() const
bool isRunning()
void setFileLoggerPath(const QString &path)
void runExternalProgram(const BitTorrent::Torrent *torrent) const
ShutdownDialogAction m_shutdownAct
Definition: application.h:131
bool sendParams(const QStringList &params)
void messageReceived(const QString &message)
bool sendMessage(const QString &message, int timeout=5000)
virtual int filesCount() const =0
SHA1Hash v1() const
Definition: infohash.cpp:44
SHA256Hash v2() const
Definition: infohash.cpp:53
static Session * instance()
Definition: session.cpp:997
static void initInstance()
Definition: session.cpp:985
void torrentFinished(Torrent *torrent)
bool addTorrent(const QString &source, const AddTorrentParams &params=AddTorrentParams())
Definition: session.cpp:2007
static void freeInstance()
Definition: session.cpp:991
virtual qlonglong totalSize() const =0
TorrentID id() const
Definition: torrent.cpp:54
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 toString() const
Definition: digest32.h:85
bool isValid() const
Definition: digest32.h:58
QString message() const noexcept
Definition: exceptions.cpp:36
static void freeInstance()
static void initInstance()
static void initInstance()
Definition: logger.cpp:61
void addMessage(const QString &message, const Log::MsgType &type=Log::NORMAL)
Definition: logger.cpp:73
static Logger * instance()
Definition: logger.cpp:56
static void freeInstance()
Definition: logger.cpp:67
static void freeInstance()
static void initInstance()
static void initInstance()
static void freeInstance()
Definition: smtp.h:51
QString join(const QString &separator) const
Definition: orderedset.h:80
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()
Definition: profile.cpp:61
QString location(SpecialFolder folder) const
Definition: profile.cpp:72
static void initInstance(const QString &rootProfilePath, const QString &configurationName, bool convertPathsToProfileRelative)
Definition: profile.cpp:53
static const Profile * instance()
Definition: profile.cpp:67
static AutoDownloader * instance()
static Session * instance()
T get(const T &defaultValue={}) const
Definition: settingvalue.h:46
static void freeInstance()
static void initInstance()
static bool askForConfirmation(QWidget *parent, const ShutdownDialogAction &action)
static void freeInstance()
static void initInstance()
Definition: webui.h:47
bool isErrored() const
Definition: webui.cpp:156
void fatalError()
QBtCommandLineParameters parseCommandLine(const QStringList &args)
Definition: cmdoptions.cpp:399
void LogMsg(const QString &message, const Log::MsgType &type)
Definition: logger.cpp:125
@ WARNING
Definition: logger.h:47
void removeDirRecursive(const QString &path)
Definition: fs.cpp:187
QString tempPath()
Definition: fs.cpp:314
QString toNativePath(const QString &path)
Definition: fs.cpp:64
QPoint screenCenter(const QWidget *w)
Definition: utils.cpp:119
void shutdownComputer(const ShutdownDialogAction &action)
Definition: misc.cpp:110
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap=-1)
Definition: misc.cpp:353
QString friendlyUnit(qint64 bytes, bool isSpeed=false)
Definition: misc.cpp:261
T value(const QString &key, const T &defaultValue={})
Definition: preferences.cpp:64
args
Definition: tstool.py:153
action
Definition: tstool.py:143
QString specialFolderLocation(const SpecialFolder folder)
Definition: profile.cpp:131
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts") set_source_files_properties($
Definition: CMakeLists.txt:5
std::optional< bool > addPaused
ShutdownDialogAction
Definition: types.h:36