1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5
6 #include "mumble_pch.hpp"
7
8 #include "Overlay.h"
9 #include "MainWindow.h"
10 #include "ServerHandler.h"
11 #include "AudioInput.h"
12 #include "AudioOutput.h"
13 #include "AudioWizard.h"
14 #include "Cert.h"
15 #include "Database.h"
16 #include "Log.h"
17 #include "Plugins.h"
18 #include "LogEmitter.h"
19 #include "DeveloperConsole.h"
20 #include "Global.h"
21 #include "LCD.h"
22 #ifdef USE_BONJOUR
23 #include "BonjourClient.h"
24 #endif
25 #ifdef USE_DBUS
26 #include "DBus.h"
27 #endif
28 #ifdef USE_VLD
29 #include "vld.h"
30 #endif
31 #include "VersionCheck.h"
32 #include "NetworkConfig.h"
33 #include "CrashReporter.h"
34 #include "SocketRPC.h"
35 #include "SSL.h"
36 #include "MumbleApplication.h"
37 #include "ApplicationPalette.h"
38 #include "Themes.h"
39 #include "UserLockFile.h"
40 #include "License.h"
41 #include "EnvUtils.h"
42
43 #if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
44 #include <shellapi.h> // For CommandLineToArgvW()
45 #endif
46
47 #if defined(USE_STATIC_QT_PLUGINS) && QT_VERSION < 0x050000
48 Q_IMPORT_PLUGIN(qtaccessiblewidgets)
49 # ifdef Q_OS_WIN
50 Q_IMPORT_PLUGIN(qico)
51 # endif
52 Q_IMPORT_PLUGIN(qsvg)
53 Q_IMPORT_PLUGIN(qsvgicon)
54 # ifdef Q_OS_MAC
55 Q_IMPORT_PLUGIN(qicnsicon)
56 # endif
57 #endif
58
59 #ifdef BOOST_NO_EXCEPTIONS
60 namespace boost {
throw_exception(std::exception const &)61 void throw_exception(std::exception const &) {
62 qFatal("Boost exception caught!");
63 }
64 }
65 #endif
66
67 extern void os_init();
68 extern char *os_lang;
69
70 #ifdef Q_OS_WIN
71 // from os_early_win.cpp
72 extern int os_early_init();
73 // from os_win.cpp
74 extern HWND mumble_mw_hwnd;
75 #endif // Q_OS_WIN
76
77 #if defined(Q_OS_WIN) && !defined(QT_NO_DEBUG)
main(int argc,char ** argv)78 extern "C" __declspec(dllexport) int main(int argc, char **argv) {
79 #else
80 int main(int argc, char **argv) {
81 #endif
82 int res = 0;
83
84 #if defined(Q_OS_WIN)
85 int ret = os_early_init();
86 if (ret != 0) {
87 return ret;
88 }
89 #endif
90
91 QT_REQUIRE_VERSION(argc, argv, "4.4.0");
92
93 #if defined(Q_OS_WIN)
94 SetDllDirectory(L"");
95 #else
96 #ifndef Q_OS_MAC
97 EnvUtils::setenv(QLatin1String("AVAHI_COMPAT_NOWARN"), QLatin1String("1"));
98 #endif
99 #endif
100
101 // Initialize application object.
102 MumbleApplication a(argc, argv);
103 a.setApplicationName(QLatin1String("Mumble"));
104 a.setOrganizationName(QLatin1String("Mumble"));
105 a.setOrganizationDomain(QLatin1String("mumble.sourceforge.net"));
106 a.setQuitOnLastWindowClosed(false);
107
108 #if QT_VERSION >= 0x050100
109 a.setAttribute(Qt::AA_UseHighDpiPixmaps);
110 #endif
111
112 #if QT_VERSION >= 0x050000 && defined(Q_OS_WIN)
113 a.installNativeEventFilter(&a);
114 #endif
115
116 MumbleSSL::initialize();
117
118 #ifdef USE_SBCELT
119 {
120 QDir d(a.applicationVersionRootPath());
121 QString helper = d.absoluteFilePath(QString::fromLatin1("sbcelt-helper"));
122 EnvUtils::setenv(QLatin1String("SBCELT_HELPER_BINARY"), helper.toUtf8().constData());
123 }
124 #endif
125
126 Global::g_global_struct = new Global();
127
128 qsrand(QDateTime::currentDateTime().toTime_t());
129
130 g.le = QSharedPointer<LogEmitter>(new LogEmitter());
131 g.c = new DeveloperConsole();
132
133 os_init();
134
135 SSL_library_init();
136 SSL_load_error_strings();
137
138 bool bAllowMultiple = false;
139 bool suppressIdentity = false;
140 bool customJackClientName = false;
141 bool bRpcMode = false;
142 QString rpcCommand;
143 QUrl url;
144 if (a.arguments().count() > 1) {
145 QStringList args = a.arguments();
146 for (int i = 1; i < args.count(); ++i) {
147 if (args.at(i) == QLatin1String("-h") || args.at(i) == QLatin1String("--help")
148 #if defined(Q_OS_WIN)
149 || args.at(i) == QLatin1String("/?")
150 #endif
151 ) {
152 QString helpMessage = MainWindow::tr(
153 "Usage: mumble [options] [<url>]\n"
154 "\n"
155 "<url> specifies a URL to connect to after startup instead of showing\n"
156 "the connection window, and has the following form:\n"
157 "mumble://[<username>[:<password>]@]<host>[:<port>][/<channel>[/<subchannel>...]][?version=<x.y.z>]\n"
158 "\n"
159 "The version query parameter has to be set in order to invoke the\n"
160 "correct client version. It currently defaults to 1.2.0.\n"
161 "\n"
162 "Valid options are:\n"
163 " -h, --help Show this help text and exit.\n"
164 " -m, --multiple\n"
165 " Allow multiple instances of the client to be started.\n"
166 " -n, --noidentity\n"
167 " Suppress loading of identity files (i.e., certificates.)\n"
168 " -jn, --jackname\n"
169 " Set custom Jack client name.\n"
170 " --license\n"
171 " Show the Mumble license.\n"
172 " --authors\n"
173 " Show the Mumble authors.\n"
174 " --third-party-licenses\n"
175 " Show licenses for third-party software used by Mumble.\n"
176 "\n"
177 );
178 QString rpcHelpBanner = MainWindow::tr(
179 "Remote controlling Mumble:\n"
180 "\n"
181 );
182 QString rpcHelpMessage = MainWindow::tr(
183 "Usage: mumble rpc <action> [options]\n"
184 "\n"
185 "It is possible to remote control a running instance of Mumble by using\n"
186 "the 'mumble rpc' command.\n"
187 "\n"
188 "Valid actions are:\n"
189 " mute\n"
190 " Mute self\n"
191 " unmute\n"
192 " Unmute self\n"
193 " togglemute\n"
194 " Toggle self-mute status\n"
195 " deaf\n"
196 " Deafen self\n"
197 " undeaf\n"
198 " Undeafen self\n"
199 " toggledeaf\n"
200 " Toggle self-deafen status\n"
201 "\n"
202 );
203
204 QString helpOutput = helpMessage + rpcHelpBanner + rpcHelpMessage;
205 if (bRpcMode) {
206 helpOutput = rpcHelpMessage;
207 }
208
209 #if defined(Q_OS_WIN)
210 QMessageBox::information(NULL, MainWindow::tr("Invocation"), helpOutput);
211 #else
212 printf("%s", qPrintable(helpOutput));
213 #endif
214 return 1;
215 } else if (args.at(i) == QLatin1String("-m") || args.at(i) == QLatin1String("--multiple")) {
216 bAllowMultiple = true;
217 } else if (args.at(i) == QLatin1String("-n") || args.at(i) == QLatin1String("--noidentity")) {
218 suppressIdentity = true;
219 g.s.bSuppressIdentity = true;
220 } else if (args.at(i) == QLatin1String("-jn") || args.at(i) == QLatin1String("--jackname")) {
221 g.s.qsJackClientName = QString(args.at(i+1));
222 customJackClientName = true;
223 } else if (args.at(i) == QLatin1String("-license") || args.at(i) == QLatin1String("--license")) {
224 printf("%s\n", qPrintable(License::license()));
225 return 0;
226 } else if (args.at(i) == QLatin1String("-authors") || args.at(i) == QLatin1String("--authors")) {
227 printf("%s\n", qPrintable(License::authors()));
228 return 0;
229 } else if (args.at(i) == QLatin1String("-third-party-licenses") || args.at(i) == QLatin1String("--third-party-licenses")) {
230 printf("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
231 return 0;
232 } else if (args.at(i) == QLatin1String("rpc")) {
233 bRpcMode = true;
234 if (args.count() - 1 > i) {
235 rpcCommand = QString(args.at(i + 1));
236 }
237 else {
238 QString rpcError = MainWindow::tr("Error: No RPC command specified");
239 #if defined(Q_OS_WIN)
240 QMessageBox::information(NULL, MainWindow::tr("RPC"), rpcError);
241 #else
242 printf("%s\n", qPrintable(rpcError));
243 #endif
244 return 1;
245 }
246 } else {
247 if (!bRpcMode) {
248 QUrl u = QUrl::fromEncoded(args.at(i).toUtf8());
249 if (u.isValid() && (u.scheme() == QLatin1String("mumble"))) {
250 url = u;
251 } else {
252 QFile f(args.at(i));
253 if (f.exists()) {
254 url = QUrl::fromLocalFile(f.fileName());
255 }
256 }
257 }
258 }
259 }
260 }
261
262 #ifdef USE_DBUS
263 #ifdef Q_OS_WIN
264 // By default, windbus expects the path to dbus-daemon to be in PATH, and the path
265 // should contain bin\\, and the path to the config is hardcoded as ..\etc
266
267 {
268 size_t reqSize;
269 if (_wgetenv_s(&reqSize, NULL, 0, L"PATH") != 0)) {
270 qWarning() << "Failed to get PATH. Not adding application directory to PATH. DBus bindings may not work.";
271 } else if (reqSize > 0) {
272 STACKVAR(wchar_t, buff, reqSize+1);
273 if (_wgetenv_s(&reqSize, buff, reqSize, L"PATH") != 0) {
274 qWarning() << "Failed to get PATH. Not adding application directory to PATH. DBus bindings may not work.";
275 } else {
276 QString path = QString::fromLatin1("%1;%2").arg(QDir::toNativeSeparators(MumbleApplication::instance()->applicationVersionRootPath())).arg(QString::fromWCharArray(buff));
277 STACKVAR(wchar_t, buffout, path.length() + 1);
278 path.toWCharArray(buffout);
279 if (_wputenv_s(L"PATH", buffout) != 0) {
280 qWarning() << "Failed to set PATH. DBus bindings may not work.";
281 }
282 }
283 }
284 }
285 #endif
286 #endif
287
288 if (bRpcMode) {
289 bool sent = false;
290 QMap<QString, QVariant> param;
291 param.insert(rpcCommand, rpcCommand);
292 sent = SocketRPC::send(QLatin1String("Mumble"), QLatin1String("self"), param);
293 if (sent) {
294 return 0;
295 } else {
296 return 1;
297 }
298 }
299
300 if (! bAllowMultiple) {
301 if (url.isValid()) {
302 #ifndef USE_DBUS
303 QMap<QString, QVariant> param;
304 param.insert(QLatin1String("href"), url);
305 #endif
306 bool sent = false;
307 #ifdef USE_DBUS
308 QDBusInterface qdbi(QLatin1String("net.sourceforge.mumble.mumble"), QLatin1String("/"), QLatin1String("net.sourceforge.mumble.Mumble"));
309
310 QDBusMessage reply=qdbi.call(QLatin1String("openUrl"), QLatin1String(url.toEncoded()));
311 sent = (reply.type() == QDBusMessage::ReplyMessage);
312 #else
313 sent = SocketRPC::send(QLatin1String("Mumble"), QLatin1String("url"), param);
314 #endif
315 if (sent)
316 return 0;
317 } else {
318 bool sent = false;
319 #ifdef USE_DBUS
320 QDBusInterface qdbi(QLatin1String("net.sourceforge.mumble.mumble"), QLatin1String("/"), QLatin1String("net.sourceforge.mumble.Mumble"));
321
322 QDBusMessage reply=qdbi.call(QLatin1String("focus"));
323 sent = (reply.type() == QDBusMessage::ReplyMessage);
324 #else
325 sent = SocketRPC::send(QLatin1String("Mumble"), QLatin1String("focus"));
326 #endif
327 if (sent)
328 return 0;
329
330 }
331 }
332
333 #ifdef Q_OS_WIN
334 // The code above this block is somewhat racy, in that it might not
335 // be possible to do RPC/DBus if two processes start at almost the
336 // same time.
337 //
338 // In order to be completely sure we don't open multiple copies of
339 // Mumble, we open a lock file. The file is opened without any sharing
340 // modes enabled. This gives us exclusive access to the file.
341 // If another Mumble instance attempts to open the file, it will fail,
342 // and that instance will know to terminate itself.
343 UserLockFile userLockFile(g.qdBasePath.filePath(QLatin1String("mumble.lock")));
344 if (! bAllowMultiple) {
345 if (!userLockFile.acquire()) {
346 qWarning("Another process has already acquired the lock file at '%s'. Terminating...", qPrintable(userLockFile.path()));
347 return 1;
348 }
349 }
350 #endif
351
352 // Load preferences
353 g.s.load();
354
355 // Check whether we need to enable accessibility features
356 #ifdef Q_OS_WIN
357 // Only windows for now. Could not find any information on how to query this for osx or linux
358 {
359 HIGHCONTRAST hc;
360 hc.cbSize = sizeof(HIGHCONTRAST);
361 SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0);
362
363 if (hc.dwFlags & HCF_HIGHCONTRASTON)
364 g.s.bHighContrast = true;
365
366 }
367 #endif
368
369 DeferInit::run_initializers();
370
371 ApplicationPalette applicationPalette;
372
373 Themes::apply();
374
375 QString qsSystemLocale = QLocale::system().name();
376
377 #ifdef Q_OS_MAC
378 if (os_lang) {
379 qWarning("Using Mac OS X system language as locale name");
380 qsSystemLocale = QLatin1String(os_lang);
381 }
382 #endif
383
384 const QString locale = g.s.qsLanguage.isEmpty() ? qsSystemLocale : g.s.qsLanguage;
385 qWarning("Locale is \"%s\" (System: \"%s\")", qPrintable(locale), qPrintable(qsSystemLocale));
386
387 QTranslator translator;
388 if (translator.load(QLatin1String(":mumble_") + locale))
389 a.installTranslator(&translator);
390
391 QTranslator loctranslator;
392 if (loctranslator.load(QLatin1String("mumble_") + locale, a.applicationDirPath()))
393 a.installTranslator(&loctranslator); // Can overwrite strings from bundled mumble translation
394
395 // With modularization of Qt 5 some - but not all - of the qt_<locale>.ts files have become
396 // so-called meta catalogues which no longer contain actual translations but refer to other
397 // more specific ts files like qtbase_<locale>.ts . To successfully load a meta catalogue all
398 // of its referenced translations must be available. As we do not want to bundle them all
399 // we now try to load the old qt_<locale>.ts file first and then fall back to loading
400 // qtbase_<locale>.ts if that failed.
401 //
402 // See http://doc.qt.io/qt-5/linguist-programmers.html#deploying-translations for more information
403 QTranslator qttranslator;
404 if (qttranslator.load(QLatin1String("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { // Try system qt translations
405 a.installTranslator(&qttranslator);
406 } else if (qttranslator.load(QLatin1String("qtbase_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
407 a.installTranslator(&qttranslator);
408 } else if (qttranslator.load(QLatin1String(":qt_") + locale)) { // Try bundled translations
409 a.installTranslator(&qttranslator);
410 } else if (qttranslator.load(QLatin1String(":qtbase_") + locale)) {
411 a.installTranslator(&qttranslator);
412 }
413
414 // Initialize proxy settings
415 NetworkConfig::SetupProxy();
416
417 g.nam = new QNetworkAccessManager();
418
419 #ifndef NO_CRASH_REPORT
420 CrashReporter *cr = new CrashReporter();
421 cr->run();
422 delete cr;
423 #endif
424
425 // Initialize logger
426 g.l = new Log();
427
428 // Initialize database
429 g.db = new Database(QLatin1String("main"));
430
431 #ifdef USE_BONJOUR
432 // Initialize bonjour
433 g.bc = new BonjourClient();
434 #endif
435
436 g.o = new Overlay();
437 g.o->setActive(g.s.os.bEnable);
438
439 g.lcd = new LCD();
440
441 // Process any waiting events before initializing our MainWindow.
442 // The mumble:// URL support for Mac OS X happens through AppleEvents,
443 // so we need to loop a little before we begin.
444 a.processEvents();
445
446 // Main Window
447 g.mw=new MainWindow(NULL);
448 g.mw->show();
449
450 #ifdef Q_OS_WIN
451 // Set mumble_mw_hwnd in os_win.cpp.
452 // Used by APIs in ASIOInput, DirectSound and GlobalShortcut_win that require a HWND.
453 mumble_mw_hwnd = GetForegroundWindow();
454 #endif
455
456 #ifdef USE_DBUS
457 new MumbleDBus(g.mw);
458 QDBusConnection::sessionBus().registerObject(QLatin1String("/"), g.mw);
459 QDBusConnection::sessionBus().registerService(QLatin1String("net.sourceforge.mumble.mumble"));
460 #endif
461
462 SocketRPC *srpc = new SocketRPC(QLatin1String("Mumble"));
463
464 g.l->log(Log::Information, MainWindow::tr("Welcome to Mumble."));
465
466 // Plugins
467 g.p = new Plugins(NULL);
468 g.p->rescanPlugins();
469
470 Audio::start();
471
472 a.setQuitOnLastWindowClosed(false);
473
474 // Configuration updates
475 bool runaudiowizard = false;
476 if (g.s.uiUpdateCounter == 0) {
477 // Previous version was an pre 1.2.3 release or this is the first run
478 runaudiowizard = true;
479
480 } else if (g.s.uiUpdateCounter == 1) {
481 // Previous versions used old idle action style, convert it
482
483 if (g.s.iIdleTime == 5 * 60) { // New default
484 g.s.iaeIdleAction = Settings::Nothing;
485 } else {
486 g.s.iIdleTime = 60 * qRound(g.s.iIdleTime / 60.); // Round to minutes
487 g.s.iaeIdleAction = Settings::Deafen; // Old behavior
488 }
489 }
490
491 if (runaudiowizard) {
492 AudioWizard *aw = new AudioWizard(g.mw);
493 aw->exec();
494 delete aw;
495 }
496
497 g.s.uiUpdateCounter = 2;
498
499 if (! CertWizard::validateCert(g.s.kpCertificate)) {
500 #if QT_VERSION >= 0x050000
501 QDir qd(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
502 #else
503 QDir qd(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation));
504 #endif
505
506 QFile qf(qd.absoluteFilePath(QLatin1String("MumbleAutomaticCertificateBackup.p12")));
507 if (qf.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
508 Settings::KeyPair kp = CertWizard::importCert(qf.readAll());
509 qf.close();
510 if (CertWizard::validateCert(kp))
511 g.s.kpCertificate = kp;
512 }
513 if (! CertWizard::validateCert(g.s.kpCertificate)) {
514 CertWizard *cw = new CertWizard(g.mw);
515 cw->exec();
516 delete cw;
517
518 if (! CertWizard::validateCert(g.s.kpCertificate)) {
519 g.s.kpCertificate = CertWizard::generateNewCert();
520 if (qf.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Unbuffered)) {
521 qf.write(CertWizard::exportCert(g.s.kpCertificate));
522 qf.close();
523 }
524 }
525 }
526 }
527
528 if (QDateTime::currentDateTime().daysTo(g.s.kpCertificate.first.first().expiryDate()) < 14)
529 g.l->log(Log::Warning, CertWizard::tr("<b>Certificate Expiry:</b> Your certificate is about to expire. You need to renew it, or you will no longer be able to connect to servers you are registered on."));
530
531 #ifdef QT_NO_DEBUG
532 #ifndef SNAPSHOT_BUILD
533 if (g.s.bUpdateCheck)
534 #endif
535 new VersionCheck(true, g.mw);
536 #ifdef SNAPSHOT_BUILD
537 new VersionCheck(false, g.mw, true);
538 #endif
539 #else
540 g.mw->msgBox(MainWindow::tr("Skipping version check in debug mode."));
541 #endif
542 if (g.s.bPluginCheck) {
543 g.p->checkUpdates();
544 }
545
546 if (url.isValid()) {
547 OpenURLEvent *oue = new OpenURLEvent(url);
548 qApp->postEvent(g.mw, oue);
549 #ifdef Q_OS_MAC
550 } else if (! a.quLaunchURL.isEmpty()) {
551 OpenURLEvent *oue = new OpenURLEvent(a.quLaunchURL);
552 qApp->postEvent(g.mw, oue);
553 #endif
554 } else {
555 g.mw->on_qaServerConnect_triggered(true);
556 }
557
558 if (! g.bQuit)
559 res=a.exec();
560
561 g.s.save();
562
563 url.clear();
564
565 ServerHandlerPtr sh = g.sh;
566 if (sh && sh->isRunning()) {
567 url = sh->getServerURL();
568 g.db->setShortcuts(g.sh->qbaDigest, g.s.qlShortcuts);
569 }
570
571 Audio::stop();
572
573 if (sh)
574 sh->disconnect();
575
576 delete srpc;
577
578 g.sh.reset();
579 while (sh && ! sh.unique())
580 QThread::yieldCurrentThread();
581 sh.reset();
582
583 delete g.mw;
584
585 delete g.nam;
586 delete g.lcd;
587
588 delete g.db;
589 delete g.p;
590 delete g.l;
591
592 #ifdef USE_BONJOUR
593 delete g.bc;
594 #endif
595
596 delete g.o;
597
598 delete g.c;
599 g.le.clear();
600
601 DeferInit::run_destroyers();
602
603 delete Global::g_global_struct;
604 Global::g_global_struct = NULL;
605
606 #ifndef QT_NO_DEBUG
607 // Hide Qt memory leak.
608 QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
609 // Release global protobuf memory allocations.
610 #if (GOOGLE_PROTOBUF_VERSION >= 2001000)
611 google::protobuf::ShutdownProtobufLibrary();
612 #endif
613 #endif
614
615 #ifdef Q_OS_WIN
616 // Release the userLockFile.
617 //
618 // It is important that we release it before we attempt to
619 // restart Mumble (if requested). If we do not release it
620 // before that, the new instance might not be able to start
621 // correctly.
622 userLockFile.release();
623 #endif
624
625 // Tear down OpenSSL state.
626 MumbleSSL::destroy();
627
628 // At this point termination of our process is immenent. We can safely
629 // launch another version of Mumble. The reason we do an actual
630 // restart instead of re-creating our data structures is that making
631 // sure we won't leave state is quite tricky. Mumble has quite a
632 // few spots which might not consider seeing to basic initializations.
633 // Until we invest the time to verify this, rather be safe (and a bit slower)
634 // than sorry (and crash/bug out). Also take care to reconnect if possible.
635 if (res == MUMBLE_EXIT_CODE_RESTART) {
636 QStringList arguments;
637
638 if (bAllowMultiple) arguments << QLatin1String("--multiple");
639 if (suppressIdentity) arguments << QLatin1String("--noidentity");
640 if (customJackClientName) arguments << QLatin1String("--jackname ") + g.s.qsJackClientName;
641 if (!url.isEmpty()) arguments << url.toString();
642
643 qWarning() << "Triggering restart of Mumble with arguments: " << arguments;
644
645 #ifdef Q_OS_WIN
646 // Work around bug related to QTBUG-7645. Mumble has uiaccess=true set
647 // on windows which makes normal CreateProcess calls (like Qt uses in
648 // startDetached) fail unless they specifically enable additional priviledges.
649 // Note that we do not actually require user interaction by UAC nor full admin
650 // rights but only the right token on launch. Here we use ShellExecuteEx
651 // which handles this transparently for us.
652 const std::wstring applicationFilePath = qApp->applicationFilePath().toStdWString();
653 const std::wstring argumentsString = arguments.join(QLatin1String(" ")).toStdWString();
654
655 SHELLEXECUTEINFO si;
656 ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));
657 si.cbSize = sizeof(SHELLEXECUTEINFO);
658 si.lpFile = applicationFilePath.data();
659 si.lpParameters = argumentsString.data();
660
661 bool ok = (ShellExecuteEx(&si) == TRUE);
662 #else
663 bool ok = QProcess::startDetached(qApp->applicationFilePath(), arguments);
664 #endif
665 if(!ok) {
666 QMessageBox::warning(NULL,
667 QApplication::tr("Failed to restart mumble"),
668 QApplication::tr("Mumble failed to restart itself. Please restart it manually.")
669 );
670 return 1;
671 }
672 return 0;
673 }
674 return res;
675 }
676
677 #if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
678 extern "C" __declspec(dllexport) int MumbleMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdArg, int cmdShow) {
679 Q_UNUSED(instance)
680 Q_UNUSED(prevInstance)
681 Q_UNUSED(cmdArg)
682 Q_UNUSED(cmdShow)
683
684 int argc;
685 wchar_t **argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
686 if (argvW == Q_NULLPTR) {
687 return -1;
688 }
689
690 QVector<QByteArray> argvS;
691 argvS.reserve(argc);
692
693 QVector<char *> argvV(argc, Q_NULLPTR);
694 for (int i = 0; i < argc; ++i) {
695 argvS.append(QString::fromWCharArray(argvW[i]).toLocal8Bit());
696 argvV[i] = argvS.back().data();
697 }
698
699 LocalFree(argvW);
700
701 return main(argc, argvV.data());
702 }
703 #endif
704