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