340 lines
9.0 KiB
C++
340 lines
9.0 KiB
C++
// Copyright 2007-2023 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "LogEmitter.h"
|
|
#include "MumbleApplication.h"
|
|
|
|
#ifdef _MSC_VER
|
|
# include "Utils.h"
|
|
#endif
|
|
|
|
// Must be included before other Windows stuff
|
|
#include "win.h"
|
|
|
|
#include "versionhelpers.h"
|
|
|
|
#include "Version.h"
|
|
|
|
#include "Global.h"
|
|
|
|
#include <cfloat>
|
|
#include <cmath>
|
|
|
|
#include <dbghelp.h>
|
|
#include <tlhelp32.h>
|
|
#include <wincrypt.h>
|
|
|
|
#ifdef _MSC_VER
|
|
# include <delayimp.h>
|
|
#endif
|
|
|
|
#include <emmintrin.h>
|
|
#include <share.h> // For share flags for _wfsopen
|
|
#include <shlobj.h>
|
|
#include <shobjidl.h>
|
|
|
|
#include <limits>
|
|
|
|
extern "C" {
|
|
void __cpuid(int a[4], int b);
|
|
void mumble_speex_init();
|
|
};
|
|
|
|
#define DUMP_BUFFER_SIZE 1024
|
|
|
|
static wchar_t wcCrashDumpPath[DUMP_BUFFER_SIZE];
|
|
static FILE *fConsole = nullptr;
|
|
|
|
static wchar_t wcComment[DUMP_BUFFER_SIZE] = L"";
|
|
static MINIDUMP_USER_STREAM musComment;
|
|
|
|
static QSharedPointer< LogEmitter > le;
|
|
|
|
static int cpuinfo[4];
|
|
|
|
bool bIsWin7 = false;
|
|
bool bIsVistaSP1 = false;
|
|
|
|
HWND mumble_mw_hwnd = 0;
|
|
|
|
static void mumbleMessageOutputQString(QtMsgType type, const QString &msg) {
|
|
char c;
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
c = 'D';
|
|
break;
|
|
case QtWarningMsg:
|
|
c = 'W';
|
|
break;
|
|
case QtFatalMsg:
|
|
c = 'F';
|
|
break;
|
|
default:
|
|
c = 'X';
|
|
}
|
|
QString date = QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
QString fmsg = QString::fromLatin1("<%1>%2 %3").arg(c).arg(date).arg(msg);
|
|
fprintf(fConsole, "%s\n", qPrintable(fmsg));
|
|
fflush(fConsole);
|
|
OutputDebugStringA(qPrintable(fmsg));
|
|
le->addLogEntry(fmsg);
|
|
if (type == QtFatalMsg) {
|
|
::MessageBoxA(nullptr, qPrintable(msg), "Mumble", MB_OK | MB_ICONERROR);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
static void mumbleMessageOutputWithContext(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) {
|
|
Q_UNUSED(ctx);
|
|
mumbleMessageOutputQString(type, msg);
|
|
}
|
|
|
|
static LONG WINAPI MumbleUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
|
|
MINIDUMP_EXCEPTION_INFORMATION i;
|
|
i.ThreadId = GetCurrentThreadId();
|
|
i.ExceptionPointers = ExceptionInfo;
|
|
|
|
MINIDUMP_USER_STREAM_INFORMATION musi;
|
|
|
|
musi.UserStreamCount = 1;
|
|
musi.UserStreamArray = &musComment;
|
|
|
|
HANDLE hMinidump =
|
|
CreateFile(wcCrashDumpPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
if (hMinidump != INVALID_HANDLE_VALUE) {
|
|
if (MiniDumpWriteDump(
|
|
GetCurrentProcess(), GetCurrentProcessId(), hMinidump,
|
|
static_cast< MINIDUMP_TYPE >(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithThreadInfo), &i,
|
|
&musi, nullptr)) {
|
|
FlushFileBuffers(hMinidump);
|
|
}
|
|
CloseHandle(hMinidump);
|
|
}
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
static void enableCrashOnCrashes() {
|
|
// Makes sure the application actually crashes when one of its callbacks
|
|
// called from the kernel crashes.
|
|
//
|
|
// See http://support.microsoft.com/kb/976038
|
|
// http://www.altdevblogaday.com/2012/07/06/when-even-crashing-doesnt-work/
|
|
|
|
typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags);
|
|
typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags);
|
|
|
|
const DWORD PROCESS_CALLBACK_FILTER_ENABLED = 0x01;
|
|
|
|
HMODULE kernel32 = LoadLibrary(L"kernel32.dll");
|
|
|
|
tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
|
|
|
|
tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
|
|
|
|
if (pGetPolicy && pSetPolicy) { // Only available as of Vista SP2 / Win7 SP1
|
|
DWORD dwFlags;
|
|
if (pGetPolicy(&dwFlags)) {
|
|
if (!pSetPolicy(dwFlags & ~PROCESS_CALLBACK_FILTER_ENABLED))
|
|
qWarning("enableCrashOnCrashes: Failed to set policy");
|
|
} else {
|
|
qWarning("enableCrashOnCrashes: Failed to get policy");
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL SetHeapOptions() {
|
|
HMODULE hLib = LoadLibrary(L"kernel32.dll");
|
|
if (!hLib)
|
|
return FALSE;
|
|
|
|
typedef BOOL(WINAPI * HSI)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
|
|
HSI pHsi = (HSI) GetProcAddress(hLib, "HeapSetInformation");
|
|
if (!pHsi) {
|
|
FreeLibrary(hLib);
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef HeapEnableTerminationOnCorruption
|
|
# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS) 1
|
|
#endif
|
|
|
|
BOOL fRet = (pHsi)(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0) ? TRUE : FALSE;
|
|
if (hLib)
|
|
FreeLibrary(hLib);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// We only support delay-loading on MSVC, not on MinGW.
|
|
#ifdef _MSC_VER
|
|
FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) {
|
|
if (dliNotify != dliNotePreLoadLibrary)
|
|
return 0;
|
|
|
|
|
|
size_t length = strlen(pdli->szDll);
|
|
if (length < 5)
|
|
return 0;
|
|
|
|
size_t buflen = length + 10;
|
|
|
|
std::vector< char > filename;
|
|
filename.resize(buflen);
|
|
strcpy_s(filename.data(), buflen, pdli->szDll);
|
|
|
|
size_t offset = 0;
|
|
|
|
if (_stricmp(filename.data() + length - 4, ".dll") == 0)
|
|
offset = length - 4;
|
|
else
|
|
offset = length;
|
|
|
|
HMODULE hmod = 0;
|
|
|
|
// SSE?
|
|
if (cpuinfo[3] & 0x02000000) {
|
|
// SSE2?
|
|
if (cpuinfo[3] & 0x04000000) {
|
|
// And SSE3?
|
|
if (cpuinfo[2] & 0x00000001) {
|
|
strcpy_s(filename.data() + offset, 10, ".sse3.dll");
|
|
hmod = LoadLibraryA(filename.data());
|
|
if (hmod)
|
|
return (FARPROC) hmod;
|
|
}
|
|
|
|
strcpy_s(filename.data() + offset, 10, ".sse2.dll");
|
|
hmod = LoadLibraryA(filename.data());
|
|
if (hmod)
|
|
return (FARPROC) hmod;
|
|
}
|
|
|
|
strcpy_s(filename.data() + offset, 10, ".sse.dll");
|
|
hmod = LoadLibraryA(filename.data());
|
|
if (hmod)
|
|
return (FARPROC) hmod;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
decltype(__pfnDliNotifyHook2) __pfnDliNotifyHook2 = delayHook;
|
|
#endif
|
|
|
|
void os_init() {
|
|
__cpuid(cpuinfo, 1);
|
|
|
|
#define MMXSSE 0x02800000
|
|
if ((cpuinfo[3] & MMXSSE) != MMXSSE) {
|
|
::MessageBoxA(nullptr, "Mumble requires a SSE capable processor (Pentium 3 / Ahtlon-XP)", "Mumble",
|
|
MB_OK | MB_ICONERROR);
|
|
exit(0);
|
|
}
|
|
|
|
bIsWin7 = IsWindows7OrGreater();
|
|
bIsVistaSP1 = IsWindowsVistaSP1OrGreater();
|
|
|
|
#if _MSC_VER == 1800 && defined(_M_X64)
|
|
// Disable MSVC 2013's FMA-optimized math routines on Windows
|
|
// versions earlier than Windows 8 (6.2).
|
|
// There are various issues on OSes that do not support the newer
|
|
// instructions.
|
|
// See issue mumble-voip/mumble#1615.
|
|
if (!IsWindows8OrGreater()) {
|
|
_set_FMA3_enable(0);
|
|
}
|
|
#endif
|
|
|
|
unsigned int currentControl = 0;
|
|
_controlfp_s(¤tControl, _DN_FLUSH, _MCW_DN);
|
|
|
|
SetHeapOptions();
|
|
enableCrashOnCrashes();
|
|
mumble_speex_init();
|
|
|
|
// Make a copy of the global LogEmitter, such that
|
|
// os_win.cpp doesn't have to consider the deletion
|
|
// of the Global object and its LogEmitter object.
|
|
le = Global::get().le;
|
|
|
|
#ifdef QT_NO_DEBUG
|
|
QString console = Global::get().qdBasePath.filePath(QLatin1String("Console.txt"));
|
|
fConsole = _wfsopen(console.toStdWString().c_str(), L"a+", _SH_DENYWR);
|
|
|
|
if (fConsole) {
|
|
qInstallMessageHandler(mumbleMessageOutputWithContext);
|
|
}
|
|
|
|
QString hash;
|
|
QFile f(qApp->applicationFilePath());
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
qWarning("VersionCheck: Failed to open binary");
|
|
} else {
|
|
QByteArray a = f.readAll();
|
|
if (a.size() > 0) {
|
|
QCryptographicHash qch(QCryptographicHash::Sha1);
|
|
qch.addData(a);
|
|
hash = QLatin1String(qch.result().toHex());
|
|
}
|
|
}
|
|
|
|
QString comment = QString::fromLatin1("%1\n%2").arg(Version::getRelease(), hash);
|
|
|
|
wcscpy_s(wcComment, DUMP_BUFFER_SIZE, comment.toStdWString().c_str());
|
|
musComment.Type = CommentStreamW;
|
|
musComment.Buffer = wcComment;
|
|
musComment.BufferSize = static_cast< ULONG >(wcslen(wcComment) * sizeof(wchar_t));
|
|
|
|
QString dump = Global::get().qdBasePath.filePath(QLatin1String("mumble.dmp"));
|
|
|
|
QFileInfo fi(dump);
|
|
QDir::root().mkpath(fi.absolutePath());
|
|
|
|
if (wcscpy_s(wcCrashDumpPath, DUMP_BUFFER_SIZE, dump.toStdWString().c_str()) == 0)
|
|
SetUnhandledExceptionFilter(MumbleUnhandledExceptionFilter);
|
|
|
|
#endif
|
|
|
|
Global::get().qdBasePath.mkpath(QLatin1String("Snapshots"));
|
|
if (bIsWin7)
|
|
SetCurrentProcessExplicitAppUserModelID(L"net.sourceforge.mumble.Mumble");
|
|
}
|
|
|
|
DWORD WinVerifySslCert(const QByteArray &cert) {
|
|
DWORD errorStatus = std::numeric_limits< DWORD >::max();
|
|
|
|
PCCERT_CONTEXT certContext = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING, reinterpret_cast< const BYTE * >(cert.constData()), cert.size());
|
|
if (!certContext) {
|
|
return errorStatus;
|
|
}
|
|
|
|
LPCSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE };
|
|
|
|
CERT_CHAIN_PARA chainParameter;
|
|
memset(&chainParameter, 0, sizeof(CERT_CHAIN_PARA));
|
|
chainParameter.cbSize = sizeof(CERT_CHAIN_PARA);
|
|
chainParameter.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
|
|
chainParameter.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage);
|
|
chainParameter.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast< LPSTR * >(usage);
|
|
|
|
PCCERT_CHAIN_CONTEXT chainContext = nullptr;
|
|
CertGetCertificateChain(nullptr, certContext, nullptr, nullptr, &chainParameter, 0, nullptr, &chainContext);
|
|
|
|
if (chainContext) {
|
|
errorStatus = chainContext->TrustStatus.dwErrorStatus;
|
|
CertFreeCertificateChain(chainContext);
|
|
}
|
|
|
|
CertFreeCertificateContext(certContext);
|
|
|
|
return errorStatus;
|
|
}
|
|
|
|
#undef DUMP_BUFFER_SIZE
|
|
#undef HeapEnableTerminationOnCorruption
|
|
#undef MMXSSE
|