Prevent WebUI tables from being highlighted
[qBittorrent.git] / src / app / main.cpp
blob77e6110ccc919a5d05148966b93c161577230366
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
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.
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.
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.
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.
30 #include <cstdlib>
32 #include <QDebug>
33 #include <QScopedPointer>
34 #include <QThread>
36 #ifndef DISABLE_GUI
37 // GUI-only includes
38 #include <QFont>
39 #include <QMessageBox>
40 #include <QPainter>
41 #include <QPen>
42 #include <QPushButton>
43 #include <QSplashScreen>
45 #ifdef QBT_STATIC_QT
46 #include <QtPlugin>
47 Q_IMPORT_PLUGIN(QICOPlugin)
48 #endif // QBT_STATIC_QT
50 #else
51 // NoGUI-only includes
52 #include <cstdio>
53 #ifdef Q_OS_UNIX
54 #include "unistd.h"
55 #endif
56 #endif // DISABLE_GUI
58 #include <signal.h>
59 #ifdef STACKTRACE
60 #ifdef Q_OS_UNIX
61 #include "stacktrace.h"
62 #else
63 #include "stacktrace_win.h"
64 #include "stacktracedialog.h"
65 #endif // Q_OS_UNIX
66 #endif //STACKTRACE
68 #include "base/preferences.h"
69 #include "base/profile.h"
70 #include "base/utils/misc.h"
71 #include "application.h"
72 #include "cmdoptions.h"
73 #include "upgrade.h"
75 // Signal handlers
76 void sigNormalHandler(int signum);
77 #ifdef STACKTRACE
78 void sigAbnormalHandler(int signum);
79 #endif
80 // sys_signame[] is only defined in BSD
81 const char *sysSigName[] = {
82 #if defined(Q_OS_WIN)
83 "", "", "SIGINT", "", "SIGILL", "", "SIGABRT_COMPAT", "", "SIGFPE", "",
84 "", "SIGSEGV", "", "", "", "SIGTERM", "", "", "", "",
85 "", "SIGBREAK", "SIGABRT", "", "", "", "", "", "", "",
86 "", ""
87 #else
88 "", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL",
89 "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP",
90 "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
91 "SIGPWR", "SIGUNUSED"
92 #endif
95 #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
96 void reportToUser(const char *str);
97 #endif
99 void displayVersion();
100 bool userAgreesWithLegalNotice();
101 void displayBadArgMessage(const QString &message);
103 #if !defined(DISABLE_GUI)
104 void showSplashScreen();
105 #endif // DISABLE_GUI
107 // Main
108 int main(int argc, char *argv[])
110 // We must save it here because QApplication constructor may change it
111 bool isOneArg = (argc == 2);
113 #ifdef Q_OS_MAC
114 // On macOS 10.12 Sierra, Apple changed the behaviour of CFPreferencesSetValue() https://bugreports.qt.io/browse/QTBUG-56344
115 // Due to this, we have to move from native plist to IniFormat
116 macMigratePlists();
117 #endif
119 try {
120 // Create Application
121 QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
122 QScopedPointer<Application> app(new Application(appId, argc, argv));
124 #ifndef DISABLE_GUI
125 // after the application object creation because we need a profile to be set already
126 // for the migration
127 migrateRSS();
128 #endif
130 const QBtCommandLineParameters params = app->commandLineArgs();
132 if (!params.unknownParameter.isEmpty()) {
133 throw CommandLineParameterError(QObject::tr("%1 is an unknown command line parameter.",
134 "--random-parameter is an unknown command line parameter.")
135 .arg(params.unknownParameter));
137 #ifndef Q_OS_WIN
138 if (params.showVersion) {
139 if (isOneArg) {
140 displayVersion();
141 return EXIT_SUCCESS;
143 throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
144 .arg(QLatin1String("-v (or --version)")));
146 #endif
147 if (params.showHelp) {
148 if (isOneArg) {
149 displayUsage(argv[0]);
150 return EXIT_SUCCESS;
152 throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
153 .arg(QLatin1String("-h (or --help)")));
156 // Set environment variable
157 if (!qputenv("QBITTORRENT", QBT_VERSION))
158 fprintf(stderr, "Couldn't set environment variable...\n");
160 #ifndef DISABLE_GUI
161 if (!userAgreesWithLegalNotice())
162 return EXIT_SUCCESS;
163 #else
164 if (!params.shouldDaemonize
165 && isatty(fileno(stdin))
166 && isatty(fileno(stdout))
167 && !userAgreesWithLegalNotice())
168 return EXIT_SUCCESS;
169 #endif
171 // Check if qBittorrent is already running for this user
172 if (app->isRunning()) {
173 #ifdef DISABLE_GUI
174 if (params.shouldDaemonize) {
175 throw CommandLineParameterError(QObject::tr("You cannot use %1: qBittorrent is already running for this user.")
176 .arg(QLatin1String("-d (or --daemon)")));
178 else
179 #endif
180 qDebug("qBittorrent is already running for this user.");
182 QThread::msleep(300);
183 app->sendParams(params.paramList());
185 return EXIT_SUCCESS;
188 #if defined(Q_OS_WIN)
189 // This affects only Windows apparently and Qt5.
190 // When QNetworkAccessManager is instantiated it regularly starts polling
191 // the network interfaces to see what's available and their status.
192 // This polling creates jitter and high ping with wifi interfaces.
193 // So here we disable it for lack of better measure.
194 // It will also spew this message in the console: QObject::startTimer: Timers cannot have negative intervals
195 // For more info see:
196 // 1. https://github.com/qbittorrent/qBittorrent/issues/4209
197 // 2. https://bugreports.qt.io/browse/QTBUG-40332
198 // 3. https://bugreports.qt.io/browse/QTBUG-46015
200 qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
201 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
202 // this is the default in Qt6
203 app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
204 #endif
205 #endif // Q_OS_WIN
207 #if defined(Q_OS_MAC)
208 // Since Apple made difficult for users to set PATH, we set here for convenience.
209 // Users are supposed to install Homebrew Python for search function.
210 // For more info see issue #5571.
211 QByteArray path = "/usr/local/bin:";
212 path += qgetenv("PATH");
213 qputenv("PATH", path.constData());
215 // On OS X the standard is to not show icons in the menus
216 app->setAttribute(Qt::AA_DontShowIconsInMenus);
217 #endif
219 #ifndef DISABLE_GUI
220 if (!upgrade()) return EXIT_FAILURE;
221 #else
222 if (!upgrade(!params.shouldDaemonize
223 && isatty(fileno(stdin))
224 && isatty(fileno(stdout)))) return EXIT_FAILURE;
225 #endif
226 #ifdef DISABLE_GUI
227 if (params.shouldDaemonize) {
228 app.reset(); // Destroy current application
229 if (daemon(1, 0) == 0) {
230 app.reset(new Application(appId, argc, argv));
231 if (app->isRunning()) {
232 // Another instance had time to start.
233 return EXIT_FAILURE;
236 else {
237 qCritical("Something went wrong while daemonizing, exiting...");
238 return EXIT_FAILURE;
241 #else
242 if (!(params.noSplash || Preferences::instance()->isSplashScreenDisabled()))
243 showSplashScreen();
244 #endif
246 signal(SIGINT, sigNormalHandler);
247 signal(SIGTERM, sigNormalHandler);
248 #ifdef STACKTRACE
249 signal(SIGABRT, sigAbnormalHandler);
250 signal(SIGSEGV, sigAbnormalHandler);
251 #endif
253 return app->exec(params.paramList());
255 catch (CommandLineParameterError &er) {
256 displayBadArgMessage(er.messageForUser());
257 return EXIT_FAILURE;
261 #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
262 void reportToUser(const char *str)
264 const size_t strLen = strlen(str);
265 if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
266 auto dummy = write(STDOUT_FILENO, str, strLen);
267 Q_UNUSED(dummy);
270 #endif
272 void sigNormalHandler(int signum)
274 #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
275 const char msg1[] = "Catching signal: ";
276 const char msg2[] = "\nExiting cleanly\n";
277 reportToUser(msg1);
278 reportToUser(sysSigName[signum]);
279 reportToUser(msg2);
280 #endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
281 signal(signum, SIG_DFL);
282 qApp->exit(); // unsafe, but exit anyway
285 #ifdef STACKTRACE
286 void sigAbnormalHandler(int signum)
288 const char *sigName = sysSigName[signum];
289 #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
290 const char msg[] = "\n\n*************************************************************\n"
291 "Please file a bug report at http://bug.qbittorrent.org and provide the following information:\n\n"
292 "qBittorrent version: " QBT_VERSION "\n\n"
293 "Caught signal: ";
294 reportToUser(msg);
295 reportToUser(sigName);
296 reportToUser("\n");
297 print_stacktrace(); // unsafe
298 #endif
300 #if defined Q_OS_WIN
301 StacktraceDialog dlg; // unsafe
302 dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
303 dlg.exec();
304 #endif
306 signal(signum, SIG_DFL);
307 raise(signum);
309 #endif // STACKTRACE
311 #if !defined(DISABLE_GUI)
312 void showSplashScreen()
314 QPixmap splashImg(":/icons/skin/splash.png");
315 QPainter painter(&splashImg);
316 QString version = QBT_VERSION;
317 painter.setPen(QPen(Qt::white));
318 painter.setFont(QFont("Arial", 22, QFont::Black));
319 painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
320 QSplashScreen *splash = new QSplashScreen(splashImg);
321 splash->show();
322 QTimer::singleShot(1500, splash, &QObject::deleteLater);
323 qApp->processEvents();
325 #endif // DISABLE_GUI
327 void displayVersion()
329 printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
332 void displayBadArgMessage(const QString &message)
334 QString help = QObject::tr("Run application with -h option to read about command line parameters.");
335 #ifdef Q_OS_WIN
336 QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
337 message + QLatin1Char('\n') + help, QMessageBox::Ok);
338 msgBox.show(); // Need to be shown or to moveToCenter does not work
339 msgBox.move(Utils::Misc::screenCenter(&msgBox));
340 msgBox.exec();
341 #else
342 const QString errMsg = QObject::tr("Bad command line: ") + '\n'
343 + message + '\n'
344 + help + '\n';
345 fprintf(stderr, "%s", qUtf8Printable(errMsg));
346 #endif
349 bool userAgreesWithLegalNotice()
351 Preferences *const pref = Preferences::instance();
352 if (pref->getAcceptedLegal()) // Already accepted once
353 return true;
355 #ifdef DISABLE_GUI
356 const QString eula = QString("\n*** %1 ***\n").arg(QObject::tr("Legal Notice"))
357 + QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + "\n\n"
358 + QObject::tr("No further notices will be issued.") + "\n\n"
359 + QObject::tr("Press %1 key to accept and continue...").arg("'y'") + '\n';
360 printf("%s", qUtf8Printable(eula));
362 char ret = getchar(); // Read pressed key
363 if ((ret == 'y') || (ret == 'Y')) {
364 // Save the answer
365 pref->setAcceptedLegal(true);
366 return true;
368 #else
369 QMessageBox msgBox;
370 msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
371 msgBox.setWindowTitle(QObject::tr("Legal notice"));
372 msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
373 QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
374 msgBox.show(); // Need to be shown or to moveToCenter does not work
375 msgBox.move(Utils::Misc::screenCenter(&msgBox));
376 msgBox.exec();
377 if (msgBox.clickedButton() == agreeButton) {
378 // Save the answer
379 pref->setAcceptedLegal(true);
380 return true;
382 #endif // DISABLE_GUI
384 return false;