diff options
Diffstat (limited to 'toolkit/crashreporter/client/crashreporter_win.cpp')
-rw-r--r-- | toolkit/crashreporter/client/crashreporter_win.cpp | 1568 |
1 files changed, 0 insertions, 1568 deletions
diff --git a/toolkit/crashreporter/client/crashreporter_win.cpp b/toolkit/crashreporter/client/crashreporter_win.cpp deleted file mode 100644 index 57ca495ba..000000000 --- a/toolkit/crashreporter/client/crashreporter_win.cpp +++ /dev/null @@ -1,1568 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifdef WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -#include "crashreporter.h" - -#include <windows.h> -#include <versionhelpers.h> -#include <commctrl.h> -#include <richedit.h> -#include <shellapi.h> -#include <shlobj.h> -#include <shlwapi.h> -#include <math.h> -#include <set> -#include <algorithm> -#include "resource.h" -#include "client/windows/sender/crash_report_sender.h" -#include "common/windows/string_utils-inl.h" - -#define CRASH_REPORTER_VALUE L"Enabled" -#define SUBMIT_REPORT_VALUE L"SubmitCrashReport" -#define SUBMIT_REPORT_OLD L"SubmitReport" -#define INCLUDE_URL_VALUE L"IncludeURL" -#define EMAIL_ME_VALUE L"EmailMe" -#define EMAIL_VALUE L"Email" -#define MAX_EMAIL_LENGTH 1024 - -#define SENDURL_ORIGINAL L"https://crash-reports.mozilla.com/submit" -#define SENDURL_XPSP2 L"https://crash-reports-xpsp2.mozilla.com/submit" - -#define WM_UPLOADCOMPLETE WM_APP - -// Thanks, Windows.h :( -#undef min -#undef max - -using std::string; -using std::wstring; -using std::map; -using std::vector; -using std::set; -using std::ios; -using std::ifstream; -using std::ofstream; - -using namespace CrashReporter; - -typedef struct { - HWND hDlg; - map<wstring,wstring> queryParameters; - map<wstring,wstring> files; - wstring sendURL; - - wstring serverResponse; -} SendThreadData; - -/* - * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx - * "The DLGTEMPLATEEX structure is not defined in any standard header file. - * The structure definition is provided here to explain the format of an - * extended template for a dialog box. -*/ -typedef struct { - WORD dlgVer; - WORD signature; - DWORD helpID; - DWORD exStyle; - // There's more to this struct, but it has weird variable-length - // members, and I only actually need to touch exStyle on an existing - // instance, so I've omitted the rest. -} DLGTEMPLATEEX; - -static HANDLE gThreadHandle; -static SendThreadData gSendData = { 0, }; -static vector<string> gRestartArgs; -static map<wstring,wstring> gQueryParameters; -static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter"); -static wstring gURLParameter; -static int gCheckboxPadding = 6; -static bool gRTLlayout = false; - -// When vertically resizing the dialog, these items should move down -static set<UINT> gAttachedBottom; - -// Default set of items for gAttachedBottom -static const UINT kDefaultAttachedBottom[] = { - IDC_SUBMITREPORTCHECK, - IDC_VIEWREPORTBUTTON, - IDC_COMMENTTEXT, - IDC_INCLUDEURLCHECK, - IDC_EMAILMECHECK, - IDC_EMAILTEXT, - IDC_PROGRESSTEXT, - IDC_THROBBER, - IDC_CLOSEBUTTON, - IDC_RESTARTBUTTON, -}; - -static wstring UTF8ToWide(const string& utf8, bool *success = 0); -static DWORD WINAPI SendThreadProc(LPVOID param); - -static wstring Str(const char* key) -{ - return UTF8ToWide(gStrings[key]); -} - -/* === win32 helper functions === */ - -static void DoInitCommonControls() -{ - INITCOMMONCONTROLSEX ic; - ic.dwSize = sizeof(INITCOMMONCONTROLSEX); - ic.dwICC = ICC_PROGRESS_CLASS; - InitCommonControlsEx(&ic); - // also get the rich edit control - LoadLibrary(L"Msftedit.dll"); -} - -static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value) -{ - DWORD type, dataSize; - dataSize = sizeof(DWORD); - if (RegQueryValueEx(hRegKey, valueName, nullptr, - &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS && - type == REG_DWORD) - return true; - - return false; -} - -// Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists. -static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName) -{ - HKEY hRegKey; - - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey) - == ERROR_SUCCESS) { - RegDeleteValue(hRegKey, valueName); - RegCloseKey(hRegKey); - } - - if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey) - == ERROR_SUCCESS) { - RegDeleteValue(hRegKey, valueName); - RegCloseKey(hRegKey); - } -} - -static bool CheckBoolKey(const wchar_t* key, - const wchar_t* valueName, - bool* enabled) -{ - /* - * NOTE! This code needs to stay in sync with the preference checking - * code in in nsExceptionHandler.cpp. - */ - *enabled = false; - bool found = false; - HKEY hRegKey; - DWORD val; - // see if our reg key is set globally - if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) { - if (GetBoolValue(hRegKey, valueName, &val)) { - *enabled = (val == 1); - found = true; - } - RegCloseKey(hRegKey); - } else { - // look for it in user settings - if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { - if (GetBoolValue(hRegKey, valueName, &val)) { - *enabled = (val == 1); - found = true; - } - RegCloseKey(hRegKey); - } - } - - return found; -} - -static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled) -{ - /* - * NOTE! This code needs to stay in sync with the preference setting - * code in in nsExceptionHandler.cpp. - */ - HKEY hRegKey; - - // remove the old value from the registry if it exists - RemoveUnusedValues(key, SUBMIT_REPORT_OLD); - - if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { - DWORD data = (enabled ? 1 : 0); - RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data)); - RegCloseKey(hRegKey); - } -} - -static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value) -{ - DWORD type, dataSize; - wchar_t buf[2048]; - dataSize = sizeof(buf); - if (RegQueryValueEx(hRegKey, valueName, nullptr, - &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS && - type == REG_SZ) { - value = buf; - return true; - } - - return false; -} - -static bool GetStringKey(const wchar_t* key, - const wchar_t* valueName, - wstring& value) -{ - value = L""; - bool found = false; - HKEY hRegKey; - // see if our reg key is set globally - if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) { - if (GetStringValue(hRegKey, valueName, value)) { - found = true; - } - RegCloseKey(hRegKey); - } else { - // look for it in user settings - if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { - if (GetStringValue(hRegKey, valueName, value)) { - found = true; - } - RegCloseKey(hRegKey); - } - } - - return found; -} - -static void SetStringKey(const wchar_t* key, - const wchar_t* valueName, - const wstring& value) -{ - HKEY hRegKey; - if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { - RegSetValueEx(hRegKey, valueName, 0, REG_SZ, - (LPBYTE)value.c_str(), - (value.length() + 1) * sizeof(wchar_t)); - RegCloseKey(hRegKey); - } -} - -static string FormatLastError() -{ - DWORD err = GetLastError(); - LPWSTR s; - string message = "Crash report submission failed: "; - // odds are it's a WinInet error - HANDLE hInetModule = GetModuleHandle(L"WinInet.dll"); - if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_FROM_HMODULE, - hInetModule, - err, - 0, - (LPWSTR)&s, - 0, - nullptr) != 0) { - message += WideToUTF8(s, nullptr); - LocalFree(s); - // strip off any trailing newlines - string::size_type n = message.find_last_not_of("\r\n"); - if (n < message.size() - 1) { - message.erase(n+1); - } - } - else { - char buf[64]; - sprintf(buf, "Unknown error, error code: 0x%08x", err); - message += buf; - } - return message; -} - -#define TS_DRAW 2 -#define BP_CHECKBOX 3 - -typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); -typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme); -typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, - int iStateId, RECT* prc, int ts, - SIZE* psz); -typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId, - int iStateId, const RECT* pRect, - RECT* pContentRect); - - -static void GetThemeSizes(HWND hwnd) -{ - HMODULE themeDLL = LoadLibrary(L"uxtheme.dll"); - - if (!themeDLL) - return; - - OpenThemeDataPtr openTheme = - (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData"); - CloseThemeDataPtr closeTheme = - (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData"); - GetThemePartSizePtr getThemePartSize = - (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize"); - - if (!openTheme || !closeTheme || !getThemePartSize) { - FreeLibrary(themeDLL); - return; - } - - HANDLE buttonTheme = openTheme(hwnd, L"Button"); - if (!buttonTheme) { - FreeLibrary(themeDLL); - return; - } - HDC hdc = GetDC(hwnd); - SIZE s; - getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s); - gCheckboxPadding = s.cx; - closeTheme(buttonTheme); - FreeLibrary(themeDLL); -} - -// Gets the position of a window relative to another window's client area -static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r) -{ - GetWindowRect(hwnd, r); - MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2); -} - -static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible) -{ - HWND hwnd = GetDlgItem(hwndDlg, item); - - ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE); -} - -static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled) -{ - HWND hwnd = GetDlgItem(hwndDlg, item); - LONG style = GetWindowLong(hwnd, GWL_STYLE); - if (!disabled) - style |= WS_DISABLED; - else - style &= ~WS_DISABLED; - - SetWindowLong(hwnd, GWL_STYLE, style); -} - -/* === Crash Reporting Dialog === */ - -static void StretchDialog(HWND hwndDlg, int ydiff) -{ - RECT r; - GetWindowRect(hwndDlg, &r); - r.bottom += ydiff; - MoveWindow(hwndDlg, r.left, r.top, - r.right - r.left, r.bottom - r.top, TRUE); -} - -static void ReflowDialog(HWND hwndDlg, int ydiff) -{ - // Move items attached to the bottom down/up by as much as - // the window resize - for (set<UINT>::const_iterator item = gAttachedBottom.begin(); - item != gAttachedBottom.end(); - item++) { - RECT r; - HWND hwnd = GetDlgItem(hwndDlg, *item); - GetRelativeRect(hwnd, hwndDlg, &r); - r.top += ydiff; - r.bottom += ydiff; - MoveWindow(hwnd, r.left, r.top, - r.right - r.left, r.bottom - r.top, TRUE); - } -} - -static DWORD WINAPI SendThreadProc(LPVOID param) -{ - bool finishedOk; - SendThreadData* td = (SendThreadData*)param; - - if (td->sendURL.empty()) { - finishedOk = false; - LogMessage("No server URL, not sending report"); - } else { - google_breakpad::CrashReportSender sender(L""); - finishedOk = (sender.SendCrashReport(td->sendURL, - td->queryParameters, - td->files, - &td->serverResponse) - == google_breakpad::RESULT_SUCCEEDED); - if (finishedOk) { - LogMessage("Crash report submitted successfully"); - } - else { - // get an error string and print it to the log - //XXX: would be nice to get the HTTP status code here, filed: - // http://code.google.com/p/google-breakpad/issues/detail?id=220 - LogMessage(FormatLastError()); - } - } - - PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0); - - return 0; -} - -static void EndCrashReporterDialog(HWND hwndDlg, int code) -{ - // Save the current values to the registry - wchar_t email[MAX_EMAIL_LENGTH]; - GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email, - sizeof(email) / sizeof(email[0])); - SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email); - - SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, - IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0); - SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, - IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0); - SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE, - IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0); - - EndDialog(hwndDlg, code); -} - -static void MaybeResizeProgressText(HWND hwndDlg) -{ - HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT); - HDC hdc = GetDC(hwndProgress); - HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0); - if (hfont) - SelectObject(hdc, hfont); - SIZE size; - RECT rect; - GetRelativeRect(hwndProgress, hwndDlg, &rect); - - wchar_t text[1024]; - GetWindowText(hwndProgress, text, 1024); - - if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size)) - return; - - if (size.cx < (rect.right - rect.left)) - return; - - // Figure out how much we need to resize things vertically - // This is sort of a fudge, but it should be good enough. - int wantedHeight = size.cy * - (int)ceil((float)size.cx / (float)(rect.right - rect.left)); - int diff = wantedHeight - (rect.bottom - rect.top); - if (diff <= 0) - return; - - MoveWindow(hwndProgress, rect.left, rect.top, - rect.right - rect.left, - wantedHeight, - TRUE); - - gAttachedBottom.clear(); - gAttachedBottom.insert(IDC_CLOSEBUTTON); - gAttachedBottom.insert(IDC_RESTARTBUTTON); - - StretchDialog(hwndDlg, diff); - - for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) { - gAttachedBottom.insert(kDefaultAttachedBottom[i]); - } -} - -static void MaybeSendReport(HWND hwndDlg) -{ - if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) { - EndCrashReporterDialog(hwndDlg, 0); - return; - } - - // disable all the form controls - EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false); - EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str()); - MaybeResizeProgressText(hwndDlg); - // start throbber - // play entire AVI, and loop - Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1); - SetDlgItemVisible(hwndDlg, IDC_THROBBER, true); - gThreadHandle = nullptr; - gSendData.hDlg = hwndDlg; - gSendData.queryParameters = gQueryParameters; - - gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0, - nullptr); -} - -static void RestartApplication() -{ - wstring cmdLine; - - for (unsigned int i = 0; i < gRestartArgs.size(); i++) { - cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" "; - } - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOWNORMAL; - ZeroMemory(&pi, sizeof(pi)); - - if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE, - 0, nullptr, nullptr, &si, &pi)) { - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } -} - -static void ShowReportInfo(HWND hwndDlg) -{ - wstring description; - - for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin(); - i != gQueryParameters.end(); - i++) { - description += i->first; - description += L": "; - description += i->second; - description += L"\n"; - } - - description += L"\n"; - description += Str(ST_EXTRAREPORTINFO); - - SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str()); -} - -static void UpdateURL(HWND hwndDlg) -{ - if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) { - gQueryParameters[L"URL"] = gURLParameter; - } else { - gQueryParameters.erase(L"URL"); - } -} - -static void UpdateEmail(HWND hwndDlg) -{ - if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) { - wchar_t email[MAX_EMAIL_LENGTH]; - GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email, - sizeof(email) / sizeof(email[0])); - gQueryParameters[L"Email"] = email; - if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true); - } else { - gQueryParameters.erase(L"Email"); - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false); - } -} - -static void UpdateComment(HWND hwndDlg) -{ - wchar_t comment[MAX_COMMENT_LENGTH + 1]; - GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment, - sizeof(comment) / sizeof(comment[0])); - if (wcslen(comment) > 0) - gQueryParameters[L"Comments"] = comment; - else - gQueryParameters.erase(L"Comments"); -} - -/* - * Dialog procedure for the "view report" dialog. - */ -static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message, - WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_INITDIALOG: { - SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str()); - SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str()); - SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT, - EM_SETTARGETDEVICE, (WPARAM)nullptr, 0); - ShowReportInfo(hwndDlg); - SetFocus(GetDlgItem(hwndDlg, IDOK)); - return FALSE; - } - - case WM_COMMAND: { - if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) - EndDialog(hwndDlg, 0); - return FALSE; - } - } - return FALSE; -} - -// Return the number of bytes this string will take encoded -// in UTF-8 -static inline int BytesInUTF8(wchar_t* str) -{ - // Just count size of buffer for UTF-8, minus one - // (we don't need to count the null terminator) - return WideCharToMultiByte(CP_UTF8, 0, str, -1, - nullptr, 0, nullptr, nullptr) - 1; -} - -// Calculate the length of the text in this edit control (in bytes, -// in the UTF-8 encoding) after replacing the current selection -// with |insert|. -static int NewTextLength(HWND hwndEdit, wchar_t* insert) -{ - wchar_t current[MAX_COMMENT_LENGTH + 1]; - - GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1); - DWORD selStart, selEnd; - SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd); - - int selectionLength = 0; - if (selEnd - selStart > 0) { - wchar_t selection[MAX_COMMENT_LENGTH + 1]; - google_breakpad::WindowsStringUtils::safe_wcsncpy(selection, - MAX_COMMENT_LENGTH + 1, - current + selStart, - selEnd - selStart); - selection[selEnd - selStart] = '\0'; - selectionLength = BytesInUTF8(selection); - } - - // current string length + replacement text length - // - replaced selection length - return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength; -} - -// Window procedure for subclassing edit controls -static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) -{ - static WNDPROC super = nullptr; - - if (super == nullptr) - super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (uMsg) { - case WM_PAINT: { - HDC hdc; - PAINTSTRUCT ps; - RECT r; - wchar_t windowText[1024]; - - GetWindowText(hwnd, windowText, 1024); - // if the control contains text or is focused, draw it normally - if (GetFocus() == hwnd || windowText[0] != '\0') - return CallWindowProc(super, hwnd, uMsg, wParam, lParam); - - GetClientRect(hwnd, &r); - hdc = BeginPaint(hwnd, &ps); - FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd) - ? COLOR_WINDOW : COLOR_BTNFACE)); - SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); - SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT)); - SetBkMode(hdc, TRANSPARENT); - wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT"); - // Get the actual edit control rect - CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r); - UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL; - if (gRTLlayout) - format |= DT_RIGHT; - if (txt) - DrawText(hdc, txt, wcslen(txt), &r, format); - EndPaint(hwnd, &ps); - return 0; - } - - // We handle WM_CHAR and WM_PASTE to limit the comment box to 500 - // bytes in UTF-8. - case WM_CHAR: { - // Leave accelerator keys and non-printing chars (except LF) alone - if (wParam & (1<<24) || wParam & (1<<29) || - (wParam < ' ' && wParam != '\n')) - break; - - wchar_t ch[2] = { (wchar_t)wParam, 0 }; - if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH) - return 0; - - break; - } - - case WM_PASTE: { - if (IsClipboardFormatAvailable(CF_UNICODETEXT) && - OpenClipboard(hwnd)) { - HGLOBAL hg = GetClipboardData(CF_UNICODETEXT); - wchar_t* pastedText = (wchar_t*)GlobalLock(hg); - int newSize = 0; - - if (pastedText) - newSize = NewTextLength(hwnd, pastedText); - - GlobalUnlock(hg); - CloseClipboard(); - - if (newSize > MAX_COMMENT_LENGTH) - return 0; - } - break; - } - - case WM_SETFOCUS: - case WM_KILLFOCUS: { - RECT r; - GetClientRect(hwnd, &r); - InvalidateRect(hwnd, &r, TRUE); - break; - } - - case WM_DESTROY: { - // cleanup our property - HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT"); - if (hData) - GlobalFree(hData); - } - } - - return CallWindowProc(super, hwnd, uMsg, wParam, lParam); -} - -// Resize a control to fit this text -static int ResizeControl(HWND hwndButton, RECT& rect, wstring text, - bool shiftLeft, int userDefinedPadding) -{ - HDC hdc = GetDC(hwndButton); - HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0); - if (hfont) - SelectObject(hdc, hfont); - SIZE size, oldSize; - int sizeDiff = 0; - - wchar_t oldText[1024]; - GetWindowText(hwndButton, oldText, 1024); - - if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size) - // default text on the button - && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) { - /* - Expand control widths to accomidate wider text strings. For most - controls (including buttons) the text padding is defined by the - dialog's rc file. Some controls (such as checkboxes) have padding - that extends to the end of the dialog, in which case we ignore the - rc padding and rely on a user defined value passed in through - userDefinedPadding. - */ - int textIncrease = size.cx - oldSize.cx; - if (textIncrease < 0) - return 0; - int existingTextPadding; - if (userDefinedPadding == 0) - existingTextPadding = (rect.right - rect.left) - oldSize.cx; - else - existingTextPadding = userDefinedPadding; - sizeDiff = textIncrease + existingTextPadding; - - if (shiftLeft) { - // shift left by the amount the button should grow - rect.left -= sizeDiff; - } - else { - // grow right instead - rect.right += sizeDiff; - } - MoveWindow(hwndButton, rect.left, rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - TRUE); - } - return sizeDiff; -} - -// The window was resized horizontally, so widen some of our -// controls to make use of the space -static void StretchControlsToFit(HWND hwndDlg) -{ - int controls[] = { - IDC_DESCRIPTIONTEXT, - IDC_SUBMITREPORTCHECK, - IDC_COMMENTTEXT, - IDC_INCLUDEURLCHECK, - IDC_EMAILMECHECK, - IDC_EMAILTEXT, - IDC_PROGRESSTEXT - }; - - RECT dlgRect; - GetClientRect(hwndDlg, &dlgRect); - - for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) { - RECT r; - HWND hwndControl = GetDlgItem(hwndDlg, controls[i]); - GetRelativeRect(hwndControl, hwndDlg, &r); - // 6 pixel spacing on the right - if (r.right + 6 != dlgRect.right) { - r.right = dlgRect.right - 6; - MoveWindow(hwndControl, r.left, r.top, - r.right - r.left, - r.bottom - r.top, - TRUE); - } - } -} - -static void SubmitReportChecked(HWND hwndDlg) -{ - bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0); - EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled); - EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), - enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) - != 0)); - SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled); -} - -static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent, - DLGPROC dlgProc, LPARAM param) -{ - INT_PTR rv = 0; - if (gRTLlayout) { - // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog - // template. - HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd), - RT_DIALOG); - HGLOBAL hDlgTemplate = LoadResource(nullptr, hDialogRC); - DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate); - unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC); - HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg); - DLGTEMPLATEEX* pMyDlgTemplate = - (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate); - memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg); - - pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL; - - rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate, - hwndParent, dlgProc, param); - GlobalUnlock(hMyDlgTemplate); - GlobalFree(hMyDlgTemplate); - } - else { - rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent, - dlgProc, param); - } - - return rv; -} - - -static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, - WPARAM wParam, LPARAM lParam) -{ - static int sHeight = 0; - - bool success; - bool enabled; - - switch (message) { - case WM_INITDIALOG: { - GetThemeSizes(hwndDlg); - RECT r; - GetClientRect(hwndDlg, &r); - sHeight = r.bottom - r.top; - - SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str()); - HICON hIcon = LoadIcon(GetModuleHandle(nullptr), - MAKEINTRESOURCE(IDI_MAINICON)); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); - - // resize the "View Report" button based on the string length - RECT rect; - HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON); - GetRelativeRect(hwnd, hwndDlg, &rect); - ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0); - SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str()); - - hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK); - GetRelativeRect(hwnd, hwndDlg, &rect); - long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false, - gCheckboxPadding); - SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK, - Str(ST_CHECKSUBMIT).c_str()); - - if (!CheckBoolKey(gCrashReporterKey.c_str(), - SUBMIT_REPORT_VALUE, &enabled)) - enabled = ShouldEnableSending(); - - CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED - : BST_UNCHECKED); - SubmitReportChecked(hwndDlg); - - HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT); - WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment, - GWLP_WNDPROC, - (LONG_PTR)EditSubclassProc); - - // Subclass comment edit control to get placeholder text - SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc); - wstring commentGrayText = Str(ST_COMMENTGRAYTEXT); - wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t)); - wcscpy(hMem, commentGrayText.c_str()); - SetProp(hwndComment, L"PROP_GRAYTEXT", hMem); - - hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK); - GetRelativeRect(hwnd, hwndDlg, &rect); - long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false, - gCheckboxPadding); - maxdiff = std::max(diff, maxdiff); - SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str()); - - // want this on by default - if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) && - !enabled) { - CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED); - } else { - CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED); - } - - hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK); - GetRelativeRect(hwnd, hwndDlg, &rect); - diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false, - gCheckboxPadding); - maxdiff = std::max(diff, maxdiff); - SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str()); - - if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) && - enabled) { - CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED); - } else { - CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED); - } - - wstring email; - if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) { - SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str()); - } - - // Subclass email edit control to get placeholder text - HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT); - OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail, - GWLP_WNDPROC, - (LONG_PTR)EditSubclassProc); - SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc); - wstring emailGrayText = Str(ST_EMAILGRAYTEXT); - hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t)); - wcscpy(hMem, emailGrayText.c_str()); - SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str()); - - RECT closeRect; - HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON); - GetRelativeRect(hwndClose, hwndDlg, &closeRect); - - RECT restartRect; - HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON); - GetRelativeRect(hwndRestart, hwndDlg, &restartRect); - - // set the close button text and shift the buttons around - // since the size may need to change - int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT), - true, 0); - restartRect.left -= sizeDiff; - restartRect.right -= sizeDiff; - SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str()); - - if (gRestartArgs.size() > 0) { - // Resize restart button to fit text - ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0); - SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str()); - } else { - // No restart arguments, so just hide the restart button - SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false); - } - // See if we need to widen the window - // Leave 6 pixels on either side + 6 pixels between the buttons - int neededSize = closeRect.right - closeRect.left + - restartRect.right - restartRect.left + 6 * 3; - GetClientRect(hwndDlg, &r); - // We may already have resized one of the checkboxes above - maxdiff = std::max(maxdiff, neededSize - (r.right - r.left)); - - if (maxdiff > 0) { - // widen window - GetWindowRect(hwndDlg, &r); - r.right += maxdiff; - MoveWindow(hwndDlg, r.left, r.top, - r.right - r.left, r.bottom - r.top, TRUE); - // shift both buttons right - if (restartRect.left + maxdiff < 6) - maxdiff += 6; - closeRect.left += maxdiff; - closeRect.right += maxdiff; - restartRect.left += maxdiff; - restartRect.right += maxdiff; - MoveWindow(hwndClose, closeRect.left, closeRect.top, - closeRect.right - closeRect.left, - closeRect.bottom - closeRect.top, - TRUE); - StretchControlsToFit(hwndDlg); - } - // need to move the restart button regardless - MoveWindow(hwndRestart, restartRect.left, restartRect.top, - restartRect.right - restartRect.left, - restartRect.bottom - restartRect.top, - TRUE); - - // Resize the description text last, in case the window was resized - // before this. - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, - EM_SETEVENTMASK, (WPARAM)nullptr, - ENM_REQUESTRESIZE); - - wstring description = Str(ST_CRASHREPORTERHEADER); - description += L"\n\n"; - description += Str(ST_CRASHREPORTERDESCRIPTION); - SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str()); - - - // Make the title bold. - CHARFORMAT fmt = { 0, }; - fmt.cbSize = sizeof(fmt); - fmt.dwMask = CFM_BOLD; - fmt.dwEffects = CFE_BOLD; - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, - 0, Str(ST_CRASHREPORTERHEADER).length()); - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT, - SCF_SELECTION, (LPARAM)&fmt); - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0); - // Force redraw. - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, - EM_SETTARGETDEVICE, (WPARAM)nullptr, 0); - // Force resize. - SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, - EM_REQUESTRESIZE, 0, 0); - - // if no URL was given, hide the URL checkbox - if (gQueryParameters.find(L"URL") == gQueryParameters.end()) { - RECT urlCheckRect, emailCheckRect; - GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect); - GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect); - - SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false); - - gAttachedBottom.erase(IDC_VIEWREPORTBUTTON); - gAttachedBottom.erase(IDC_SUBMITREPORTCHECK); - gAttachedBottom.erase(IDC_COMMENTTEXT); - - StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top); - - gAttachedBottom.insert(IDC_VIEWREPORTBUTTON); - gAttachedBottom.insert(IDC_SUBMITREPORTCHECK); - gAttachedBottom.insert(IDC_COMMENTTEXT); - } - - MaybeResizeProgressText(hwndDlg); - - // Open the AVI resource for the throbber - Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER), - MAKEINTRESOURCE(IDR_THROBBER)); - - UpdateURL(hwndDlg); - UpdateEmail(hwndDlg); - - SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK)); - return FALSE; - } - case WM_SIZE: { - ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight); - sHeight = HIWORD(lParam); - InvalidateRect(hwndDlg, nullptr, TRUE); - return FALSE; - } - case WM_NOTIFY: { - NMHDR* notification = reinterpret_cast<NMHDR*>(lParam); - if (notification->code == EN_REQUESTRESIZE) { - // Resizing the rich edit control to fit the description text. - REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam); - RECT newSize = reqresize->rc; - RECT oldSize; - GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize); - - // resize the text box as requested - MoveWindow(notification->hwndFrom, newSize.left, newSize.top, - newSize.right - newSize.left, newSize.bottom - newSize.top, - TRUE); - - // Resize the dialog to fit (the WM_SIZE handler will move the controls) - StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom); - } - return FALSE; - } - case WM_COMMAND: { - if (HIWORD(wParam) == BN_CLICKED) { - switch(LOWORD(wParam)) { - case IDC_VIEWREPORTBUTTON: - DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg, - (DLGPROC)ViewReportDialogProc, 0); - break; - case IDC_SUBMITREPORTCHECK: - SubmitReportChecked(hwndDlg); - break; - case IDC_INCLUDEURLCHECK: - UpdateURL(hwndDlg); - break; - case IDC_EMAILMECHECK: - UpdateEmail(hwndDlg); - break; - case IDC_CLOSEBUTTON: - MaybeSendReport(hwndDlg); - break; - case IDC_RESTARTBUTTON: - RestartApplication(); - MaybeSendReport(hwndDlg); - break; - } - } else if (HIWORD(wParam) == EN_CHANGE) { - switch(LOWORD(wParam)) { - case IDC_EMAILTEXT: - UpdateEmail(hwndDlg); - break; - case IDC_COMMENTTEXT: - UpdateComment(hwndDlg); - } - } - - return FALSE; - } - case WM_UPLOADCOMPLETE: { - WaitForSingleObject(gThreadHandle, INFINITE); - success = (wParam == 1); - SendCompleted(success, WideToUTF8(gSendData.serverResponse)); - // hide throbber - Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER)); - SetDlgItemVisible(hwndDlg, IDC_THROBBER, false); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, - success ? - Str(ST_REPORTSUBMITSUCCESS).c_str() : - Str(ST_SUBMITFAILED).c_str()); - MaybeResizeProgressText(hwndDlg); - // close dialog after 5 seconds - SetTimer(hwndDlg, 0, 5000, nullptr); - // - return TRUE; - } - - case WM_LBUTTONDOWN: { - HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT); - POINT p = { LOWORD(lParam), HIWORD(lParam) }; - // if the email edit control is clicked, enable it, - // check the email checkbox, and focus the email edit control - if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail && - IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) && - !IsWindowEnabled(hwndEmail) && - IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) { - CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED); - UpdateEmail(hwndDlg); - SetFocus(hwndEmail); - } - break; - } - - case WM_TIMER: { - // The "1" gets used down in UIShowCrashUI to indicate that we at least - // tried to send the report. - EndCrashReporterDialog(hwndDlg, 1); - return FALSE; - } - - case WM_CLOSE: { - EndCrashReporterDialog(hwndDlg, 0); - return FALSE; - } - } - return FALSE; -} - -static wstring UTF8ToWide(const string& utf8, bool *success) -{ - wchar_t* buffer = nullptr; - int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), - -1, nullptr, 0); - if(buffer_size == 0) { - if (success) - *success = false; - return L""; - } - - buffer = new wchar_t[buffer_size]; - if(buffer == nullptr) { - if (success) - *success = false; - return L""; - } - - MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), - -1, buffer, buffer_size); - wstring str = buffer; - delete [] buffer; - - if (success) - *success = true; - - return str; -} - -static string WideToMBCP(const wstring& wide, - unsigned int cp, - bool* success = nullptr) -{ - char* buffer = nullptr; - int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(), - -1, nullptr, 0, nullptr, nullptr); - if(buffer_size == 0) { - if (success) - *success = false; - return ""; - } - - buffer = new char[buffer_size]; - if(buffer == nullptr) { - if (success) - *success = false; - return ""; - } - - WideCharToMultiByte(cp, 0, wide.c_str(), - -1, buffer, buffer_size, nullptr, nullptr); - string mb = buffer; - delete [] buffer; - - if (success) - *success = true; - - return mb; -} - -string WideToUTF8(const wstring& wide, bool* success) -{ - return WideToMBCP(wide, CP_UTF8, success); -} - -/* === Crashreporter UI Functions === */ - -bool UIInit() -{ - for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) { - gAttachedBottom.insert(kDefaultAttachedBottom[i]); - } - - DoInitCommonControls(); - - return true; -} - -void UIShutdown() -{ -} - -void UIShowDefaultUI() -{ - MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(), - L"Crash Reporter", - MB_OK | MB_ICONSTOP); -} - -static bool CanUseMainCrashReportServer() -{ - // Any NT from 6.0 and above is fine. - if (IsWindowsVersionOrGreater(6, 0, 0)) { - return true; - } - - // On NT 5 servers, we need Server 2003 SP2. - if (IsWindowsServer()) { - return IsWindowsVersionOrGreater(5, 2, 2); - } - - // Otherwise we have an NT 5 client. - // We need exactly XP SP3 (version 5.1 SP3 but not version 5.2). - return (IsWindowsVersionOrGreater(5, 1, 3) && - !IsWindowsVersionOrGreater(5, 2, 0)); -} - -bool UIShowCrashUI(const StringTable& files, - const StringTable& queryParameters, - const string& sendURL, - const vector<string>& restartArgs) -{ - gSendData.hDlg = nullptr; - gSendData.sendURL = UTF8ToWide(sendURL); - - // Older Windows don't support the crash report server's crypto. - // This is a hack to use an alternate server. - if (!CanUseMainCrashReportServer() && - gSendData.sendURL.find(SENDURL_ORIGINAL) == 0) { - gSendData.sendURL.replace(0, ARRAYSIZE(SENDURL_ORIGINAL) - 1, - SENDURL_XPSP2); - } - - for (StringTable::const_iterator i = files.begin(); - i != files.end(); - i++) { - gSendData.files[UTF8ToWide(i->first)] = UTF8ToWide(i->second); - } - - for (StringTable::const_iterator i = queryParameters.begin(); - i != queryParameters.end(); - i++) { - gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second); - } - - if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) { - gCrashReporterKey = L"Software\\"; - if (!gQueryParameters[L"Vendor"].empty()) { - gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\"; - } - gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter"; - } - - if (gQueryParameters.find(L"URL") != gQueryParameters.end()) - gURLParameter = gQueryParameters[L"URL"]; - - gRestartArgs = restartArgs; - - if (gStrings.find("isRTL") != gStrings.end() && - gStrings["isRTL"] == "yes") - gRTLlayout = true; - - return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr, - (DLGPROC)CrashReporterDialogProc, 0); -} - -void UIError_impl(const string& message) -{ - wstring title = Str(ST_CRASHREPORTERTITLE); - if (title.empty()) - title = L"Crash Reporter Error"; - - MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(), - MB_OK | MB_ICONSTOP); -} - -bool UIGetIniPath(string& path) -{ - wchar_t fileName[MAX_PATH]; - if (GetModuleFileName(nullptr, fileName, MAX_PATH)) { - // get crashreporter ini - wchar_t* s = wcsrchr(fileName, '.'); - if (s) { - wcscpy(s, L".ini"); - path = WideToUTF8(fileName); - return true; - } - } - - return false; -} - -bool UIGetSettingsPath(const string& vendor, - const string& product, - string& settings_path) -{ - wchar_t path[MAX_PATH]; - HRESULT hRes = SHGetFolderPath(nullptr, - CSIDL_APPDATA, - nullptr, - 0, - path); - if (FAILED(hRes)) { - // This provides a fallback for getting the path to APPDATA by querying the - // registry when the call to SHGetFolderPath is unable to provide this path - // (Bug 513958). - HKEY key; - DWORD type, size, dwRes; - dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", - 0, - KEY_READ, - &key); - if (dwRes != ERROR_SUCCESS) - return false; - - dwRes = RegQueryValueExW(key, - L"AppData", - nullptr, - &type, - (LPBYTE)&path, - &size); - ::RegCloseKey(key); - // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the - // buffer size must not equal 0, and the buffer size be a multiple of 2. - if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) - return false; - } - - if (!vendor.empty()) { - PathAppend(path, UTF8ToWide(vendor).c_str()); - } - PathAppend(path, UTF8ToWide(product).c_str()); - PathAppend(path, L"Crash Reports"); - settings_path = WideToUTF8(path); - return true; -} - -bool UIEnsurePathExists(const string& path) -{ - if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) { - if (GetLastError() != ERROR_ALREADY_EXISTS) - return false; - } - - return true; -} - -bool UIFileExists(const string& path) -{ - DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str()); - return (attrs != INVALID_FILE_ATTRIBUTES); -} - -bool UIMoveFile(const string& oldfile, const string& newfile) -{ - if (oldfile == newfile) - return true; - - return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str()) - == TRUE; -} - -bool UIDeleteFile(const string& oldfile) -{ - return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE; -} - -ifstream* UIOpenRead(const string& filename) -{ - // adapted from breakpad's src/common/windows/http_upload.cc - -#if defined(_MSC_VER) - ifstream* file = new ifstream(); - file->open(UTF8ToWide(filename).c_str(), ios::in); -#else // GCC - ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(), - ios::in); -#endif // _MSC_VER - - return file; -} - -ofstream* UIOpenWrite(const string& filename, - bool append, // append=false - bool binary) // binary=false -{ - // adapted from breakpad's src/common/windows/http_upload.cc - std::ios_base::openmode mode = ios::out; - if (append) { - mode = mode | ios::app; - } - if (binary) { - mode = mode | ios::binary; - } - -#if defined(_MSC_VER) - ofstream* file = new ofstream(); - file->open(UTF8ToWide(filename).c_str(), mode); -#else // GCC - ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(), - mode); -#endif // _MSC_VER - - return file; -} - -struct FileData -{ - FILETIME timestamp; - wstring path; -}; - -static bool CompareFDTime(const FileData& fd1, const FileData& fd2) -{ - return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0; -} - -void UIPruneSavedDumps(const std::string& directory) -{ - wstring wdirectory = UTF8ToWide(directory); - - WIN32_FIND_DATA fdata; - wstring findpath = wdirectory + L"\\*.dmp"; - HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata); - if (dirlist == INVALID_HANDLE_VALUE) - return; - - vector<FileData> dumpfiles; - - for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) { - FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName}; - dumpfiles.push_back(fd); - } - - sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime); - - while (dumpfiles.size() > kSaveCount) { - // get the path of the oldest file - wstring path = (--dumpfiles.end())->path; - DeleteFile(path.c_str()); - - // s/.dmp/.extra/ - path.replace(path.size() - 4, 4, L".extra"); - DeleteFile(path.c_str()); - - dumpfiles.pop_back(); - } -} - -void UIRunMinidumpAnalyzer(const string& exename, const string& filename) -{ - wstring cmdLine; - - cmdLine += L"\"" + UTF8ToWide(exename) + L"\" "; - cmdLine += L"\"" + UTF8ToWide(filename) + L"\" "; - - STARTUPINFO si = {}; - PROCESS_INFORMATION pi = {}; - - si.cb = sizeof(si); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOWNORMAL; - - if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE, - 0, nullptr, nullptr, &si, &pi)) { - WaitForSingleObject(pi.hProcess, INFINITE); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } -} |