1515 lines
49 KiB
C++
1515 lines
49 KiB
C++
/*
|
|
* This is a part of the BugTrap package.
|
|
* Copyright (c) 2005-2009 IntelleSoft.
|
|
* All rights reserved.
|
|
*
|
|
* Description: Common BugTrap UI routines.
|
|
* Author: Maksim Pyatkovskiy.
|
|
*
|
|
* This source code is only intended as a supplement to the
|
|
* BugTrap package reference and related electronic documentation
|
|
* provided with the product. See these sources for detailed
|
|
* information regarding the BugTrap package.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "resource.h"
|
|
#include "BugTrap.h"
|
|
#include "BugTrapUtils.h"
|
|
#include "BugTrapUI.h"
|
|
#include "WaitDlg.h"
|
|
#include "WaitCursor.h"
|
|
#include "MainDlg.h"
|
|
#include "SimpleDlg.h"
|
|
#include "SendMailDlg.h"
|
|
#include "DescribeErrorDlg.h"
|
|
#include "Globals.h"
|
|
#include "Encoding.h"
|
|
#include "MemStream.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
/// Flag indicating whatever UI should be visible.
|
|
static BOOL g_bShowUI = FALSE;
|
|
|
|
/**
|
|
* @addtogroup BugTrapUI BugTrap Graphical User Interface
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Get default subject for bug report.
|
|
* @param pszSubject - pointer to subject buffer.
|
|
* @param dwSubjectSize - size of subject buffer.
|
|
*/
|
|
void GetDefaultMailSubject(PTSTR pszSubject, DWORD dwSubjectSize)
|
|
{
|
|
if (*g_szAppName)
|
|
_stprintf_s(pszSubject, dwSubjectSize, _T("\"%s\" Error Report"), g_szAppName);
|
|
else
|
|
_tcscpy_s(pszSubject, dwSubjectSize, _T("Error Report"));
|
|
}
|
|
|
|
/**
|
|
* @brief Get default mail-to URL for bug report.
|
|
* @param pszURLString - pointer to URL buffer.
|
|
* @param dwURLSize - size of URL buffer.
|
|
*/
|
|
void GetDefaultMailURL(PTSTR pszURLString, DWORD dwURLSize)
|
|
{
|
|
if (*g_szAppName)
|
|
_stprintf_s(pszURLString, dwURLSize, _T("mailto:%s?subject=%%22%s%%22%%20Error%%20Report"), g_szSupportEMail, g_szAppName);
|
|
else
|
|
_stprintf_s(pszURLString, dwURLSize, _T("mailto:%s?subject=Error%%20Report"), g_szSupportEMail);
|
|
}
|
|
|
|
/**
|
|
* @brief Return true if standard MAPI message editor should be shown.
|
|
* @return true if standard MAPI message editor should be shown.
|
|
*/
|
|
static inline BOOL ShowMapiMessageEditor(void)
|
|
{
|
|
return (g_bShowUI && (g_dwFlags & BTF_EDITMAIL) == 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Return true if custom BugTrap message editor should be shown.
|
|
* @return true if custom BugTrap message editor should be shown.
|
|
*/
|
|
static inline BOOL ShowBtMessageEditor(void)
|
|
{
|
|
return (g_bShowUI && (g_dwFlags & BTF_EDITMAIL) != 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Send e-mail message through Simple MPI facilities.
|
|
* @param hwndParent - parent window handle (valid if user interface is enabled).
|
|
* @param pszSubject - subject text.
|
|
* @param pszMessage - message text.
|
|
* @return true for successfully completed operation.
|
|
*/
|
|
BOOL SendEMail(HWND hwndParent, PCTSTR pszSubject, PCTSTR pszMessage)
|
|
{
|
|
if (*g_szSupportEMail == _T('\0'))
|
|
return FALSE;
|
|
CWaitCursor wait;
|
|
if (g_bShowUI)
|
|
wait.BeginWait();
|
|
if (g_pMapiSession == NULL)
|
|
{
|
|
g_pMapiSession = new CMapiSession;
|
|
if (g_pMapiSession == NULL)
|
|
return FALSE;
|
|
}
|
|
if (! g_pMapiSession->LoggedOn())
|
|
{
|
|
if (*g_szMailProfile)
|
|
{
|
|
if (! g_pMapiSession->Logon(g_szMailProfile, g_szMailPassword))
|
|
return FALSE;
|
|
}
|
|
else if (g_bShowUI)
|
|
{
|
|
wait.EndWait();
|
|
if (! g_pMapiSession->Logon(hwndParent))
|
|
return FALSE;
|
|
wait.BeginWait();
|
|
}
|
|
else
|
|
{
|
|
if (! g_pMapiSession->Logon())
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
CMapiMessage message;
|
|
message.GetTo().AddItem(g_szSupportEMail);
|
|
message.SetSubject(pszSubject);
|
|
message.SetBody(pszMessage);
|
|
if (g_dwFlags & BTF_ATTACHREPORT)
|
|
{
|
|
message.GetAttachments().AddItem(g_szInternalReportFilePath);
|
|
message.GetAttachmentTitles().AddItem(PathFindFileName(g_szInternalReportFilePath));
|
|
}
|
|
return g_pMapiSession->Send(message, ShowMapiMessageEditor(), hwndParent);
|
|
}
|
|
|
|
/**
|
|
* @brief Send bug report through e-mail either using custom or system dialogs.
|
|
* @param hwndParent - parent window handle.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL MailTempReport(HWND hwndParent)
|
|
{
|
|
if (*g_szSupportEMail == _T('\0'))
|
|
return FALSE;
|
|
TCHAR szSubject[MAX_PATH];
|
|
GetDefaultMailSubject(szSubject, countof(szSubject));
|
|
return SendEMail(hwndParent, szSubject, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Send bug report through e-mail either using custom or system dialogs.
|
|
* @param hwndParent - parent window handle.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL MailTempReportEx(HWND hwndParent)
|
|
{
|
|
if (*g_szSupportEMail == _T('\0'))
|
|
return FALSE;
|
|
BOOL bResult;
|
|
if (ShowBtMessageEditor())
|
|
{
|
|
bResult = DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_SEND_MAIL_DLG), hwndParent, SendMailDlgProc) == IDOK;
|
|
}
|
|
else if (g_dwFlags & BTF_ATTACHREPORT)
|
|
{
|
|
bResult = MailTempReport(hwndParent);
|
|
}
|
|
else
|
|
{
|
|
CWaitCursor wait;
|
|
if (g_bShowUI)
|
|
wait.BeginWait();
|
|
TCHAR szURLString[MAX_PATH];
|
|
GetDefaultMailURL(szURLString, countof(szURLString));
|
|
bResult = ShellExecute(NULL, _T("open"), szURLString, NULL, NULL, SW_SHOWDEFAULT) == ERROR_SUCCESS;
|
|
}
|
|
if (g_bShowUI)
|
|
{
|
|
TCHAR szProjectName[32], szMessageText[128];
|
|
LoadString(g_hInstance, IDS_BUGTRAP_NAME, szProjectName, countof(szProjectName));
|
|
if (bResult)
|
|
{
|
|
SetForegroundWindow(hwndParent);
|
|
LoadString(g_hInstance, IDS_STATUS_REPORTSENT, szMessageText, countof(szMessageText));
|
|
::MessageBox(hwndParent, szMessageText, szProjectName, MB_ICONINFORMATION | MB_OK);
|
|
EndDialog(hwndParent, FALSE);
|
|
}
|
|
else if ((g_dwFlags & BTF_EDITMAIL) == 0)
|
|
{
|
|
SetForegroundWindow(hwndParent);
|
|
LoadString(g_hInstance, IDS_ERROR_TRANSFERFAILED, szMessageText, countof(szMessageText));
|
|
::MessageBox(hwndParent, szMessageText, szProjectName, MB_ICONERROR | MB_OK);
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/// Protocol message type.
|
|
enum MESSAGE_TYPE
|
|
{
|
|
/// Compound message that includes project info and report data.
|
|
MT_COMPOUND_MESSAGE = 1
|
|
};
|
|
|
|
/// Compound message flags.
|
|
enum COMPOUND_MESSAGE_FLAGS
|
|
{
|
|
/// No flags specified.
|
|
CMF_NONE = 0x00
|
|
};
|
|
|
|
/**
|
|
* @param hwndSink - sink window handle.
|
|
*/
|
|
CTransferThreadParams::CTransferThreadParams(HWND hwndSink)
|
|
{
|
|
m_hCancellationEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // non-signalled manual reset event
|
|
m_hCompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // non-signalled manual reset event
|
|
m_hwndSink = hwndSink;
|
|
m_dwErrorCode = ERROR_SUCCESS;
|
|
m_pszMessageBuffer = NULL;
|
|
m_eContext = IC_UNDEFINED;
|
|
m_dwCallbackResult = ERROR_SUCCESS;
|
|
}
|
|
|
|
CTransferThreadParams::~CTransferThreadParams(void)
|
|
{
|
|
if (m_pszMessageBuffer)
|
|
LocalFree(m_pszMessageBuffer);
|
|
CloseHandle(m_hCancellationEvent);
|
|
CloseHandle(m_hCompletionEvent);
|
|
}
|
|
|
|
/**
|
|
* @param dwErrorCode - error code.
|
|
* @param pszMessageBuffer - error message.
|
|
*/
|
|
void CTransferThreadParams::SetErrorCode(DWORD dwErrorCode, PTSTR pszMessageBuffer)
|
|
{
|
|
if (m_pszMessageBuffer)
|
|
LocalFree(m_pszMessageBuffer);
|
|
_ASSERTE(dwErrorCode != ERROR_SUCCESS);
|
|
m_dwErrorCode = dwErrorCode;
|
|
m_pszMessageBuffer = pszMessageBuffer;
|
|
}
|
|
|
|
/**
|
|
* @param dwErrorCode - error code.
|
|
*/
|
|
void CTransferThreadParams::SetErrorCode(DWORD dwErrorCode)
|
|
{
|
|
PTSTR pszMessageBuffer = NULL;
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
|
(PTSTR)&pszMessageBuffer, 0, NULL);
|
|
if (pszMessageBuffer)
|
|
TrimSpaces(pszMessageBuffer);
|
|
}
|
|
SetErrorCode(dwErrorCode, pszMessageBuffer);
|
|
}
|
|
|
|
/**
|
|
* @brief Wait until pending operation is either complete or canceled.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
* @return error code.
|
|
*/
|
|
static DWORD WaitForPendingSocketRequest(CTransferThreadParams* pTransferThreadParams)
|
|
{
|
|
HANDLE hCancelationEvent = pTransferThreadParams->GetCancellationEvent();
|
|
HANDLE hCompletionEvent = pTransferThreadParams->GetCompletionEvent();
|
|
HANDLE arrEvents[] = { hCompletionEvent, hCancelationEvent };
|
|
|
|
DWORD dwErrorCode = ERROR_SUCCESS;
|
|
DWORD dwTemp = WSAGetLastError();
|
|
if (dwTemp != WSA_IO_PENDING && dwTemp != WSAEWOULDBLOCK)
|
|
dwErrorCode = dwTemp;
|
|
else if (WaitForMultipleObjects(countof(arrEvents), arrEvents, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
|
dwErrorCode = WSA_OPERATION_ABORTED;
|
|
return dwErrorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief Submit bug report through low-level network protocol to BugTrap server.
|
|
* @param pszHostName - server host name.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
* @return error code.
|
|
*/
|
|
static DWORD WSASendReport(PCTSTR pszHostName, CTransferThreadParams* pTransferThreadParams)
|
|
{
|
|
typedef BOOL (WINAPI *PFCancelIo)(HANDLE hFile); /// Type definition of pointer to CancelIO() function.
|
|
HMODULE hKernelDll = GetModuleHandle(_T("KERNEL32.DLL"));
|
|
PFCancelIo FCancelIo = hKernelDll ? (PFCancelIo)GetProcAddress(hKernelDll, "CancelIo") : NULL;
|
|
HWND hwndSink = pTransferThreadParams->GetSinkWnd();
|
|
|
|
DWORD dwErrorCode = ERROR_SUCCESS;
|
|
HANDLE hFile = CreateFile(g_szInternalReportFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
const DWORD dwAppNameSize = sizeof(DWORD) + countof(g_szAppName) * sizeof(DWORD);
|
|
const DWORD dwAppVersionSize = sizeof(DWORD) + countof(g_szAppVersion) * sizeof(DWORD);
|
|
const DWORD dwReportFileExtensionSize = sizeof(DWORD) + 8 * sizeof(DWORD);
|
|
const DWORD dwNotificationEMailSize = sizeof(DWORD) + countof(g_szNotificationEMail) * sizeof(DWORD);
|
|
const DWORD dwMaxHeaderSize =
|
|
sizeof(DWORD) + // Protocol signature
|
|
sizeof(DWORD) + // Data size
|
|
sizeof(BYTE) + // Message type
|
|
sizeof(DWORD) + // Message flags
|
|
dwAppNameSize + // Application name
|
|
dwAppVersionSize + // Application version
|
|
dwReportFileExtensionSize + // Report file extension
|
|
dwNotificationEMailSize; // Notification e-mail
|
|
|
|
DWORD dwFileSize = GetFileSize(hFile, NULL);
|
|
DWORD dwBufferSize = max(dwMaxHeaderSize, min(dwFileSize, g_dwMaxBufferSize));
|
|
PBYTE pBuffer = new BYTE[dwBufferSize];
|
|
if (pBuffer)
|
|
{
|
|
WSADATA wd;
|
|
dwErrorCode = WSAStartup(MAKEWORD(2, 0), &wd);
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
PCSTR pszHostNameA;
|
|
#ifdef _UNICODE
|
|
CHAR szHostNameA[MAX_PATH];
|
|
WideCharToMultiByte(CP_ACP, 0, pszHostName, -1, szHostNameA, countof(szHostNameA), NULL, NULL);
|
|
pszHostNameA = szHostNameA;
|
|
#else
|
|
pszHostNameA = pszHostName;
|
|
#endif
|
|
|
|
SOCKADDR_IN sockAddr;
|
|
ZeroMemory(&sockAddr, sizeof(sockAddr));
|
|
if (*pszHostNameA >= '0' && *pszHostNameA <= '9')
|
|
sockAddr.sin_addr.s_addr = inet_addr(pszHostNameA);
|
|
else
|
|
{
|
|
hostent* pHostEnt = gethostbyname(pszHostNameA);
|
|
if (pHostEnt != NULL)
|
|
CopyMemory(&sockAddr.sin_addr, pHostEnt->h_addr, pHostEnt->h_length);
|
|
else
|
|
sockAddr.sin_addr.s_addr = INADDR_NONE;
|
|
}
|
|
if (sockAddr.sin_addr.s_addr != INADDR_NONE)
|
|
{
|
|
sockAddr.sin_family = AF_INET;
|
|
sockAddr.sin_port = htons(g_nSupportPort);
|
|
SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
|
|
if (sock != INVALID_SOCKET)
|
|
{
|
|
CMemStream MemStream(256);
|
|
CUTF8EncStream EncStream(&MemStream);
|
|
BOOL bConnected = FALSE;
|
|
HANDLE hCompletionEvent = pTransferThreadParams->GetCompletionEvent();
|
|
// Implicitly put socket in non-blocking mode.
|
|
if (WSAEventSelect(sock, hCompletionEvent, FD_CONNECT) == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = WSAGetLastError();
|
|
goto end; // Internal error.
|
|
}
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_CONNECTINGTOSERVER, 0, 0);
|
|
if (connect(sock, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = WaitForPendingSocketRequest(pTransferThreadParams);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto end;
|
|
WSANETWORKEVENTS nev;
|
|
if (WSAEnumNetworkEvents(sock, hCompletionEvent, &nev) == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = WSAGetLastError();
|
|
goto end; // Internal error.
|
|
}
|
|
_ASSERTE(nev.lNetworkEvents & FD_CONNECT);
|
|
dwErrorCode = nev.iErrorCode[FD_CONNECT_BIT];
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto end; // Connection can't be established.
|
|
}
|
|
|
|
bConnected = TRUE;
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_SENDINGREPORT, 0, 0);
|
|
|
|
// Cancel association and selection of network events.
|
|
if (WSAEventSelect(sock, hCompletionEvent, 0) == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = WSAGetLastError();
|
|
goto end; // Internal error.
|
|
}
|
|
|
|
size_t nHeaderPosition = 0;
|
|
|
|
// Protocol signature.
|
|
*(PDWORD)(pBuffer + nHeaderPosition) = g_dwProtocolSignature;
|
|
nHeaderPosition += sizeof(DWORD);
|
|
|
|
// Data size placeholder.
|
|
*(PDWORD)(pBuffer + nHeaderPosition) = 0;
|
|
nHeaderPosition += sizeof(DWORD);
|
|
|
|
// Message type.
|
|
*(PBYTE)(pBuffer + nHeaderPosition) = MT_COMPOUND_MESSAGE;
|
|
nHeaderPosition += sizeof(BYTE);
|
|
|
|
// Message flags.
|
|
*(PDWORD)(pBuffer + nHeaderPosition) = CMF_NONE;
|
|
nHeaderPosition += sizeof(DWORD);
|
|
|
|
// Application name.
|
|
if (! WriteBinaryString(EncStream, g_szAppName, pBuffer, nHeaderPosition, dwBufferSize))
|
|
{
|
|
dwErrorCode = ERROR_INTERNAL_ERROR;
|
|
goto end; // Internal error.
|
|
}
|
|
|
|
// Application version.
|
|
if (! WriteBinaryString(EncStream, g_szAppVersion, pBuffer, nHeaderPosition, dwBufferSize))
|
|
{
|
|
dwErrorCode = ERROR_INTERNAL_ERROR;
|
|
goto end; // Internal error.
|
|
}
|
|
|
|
// Report file extension.
|
|
PCTSTR pszReportFileExtension = CSymEngine::GetReportFileExtension();
|
|
if (! WriteBinaryString(EncStream, pszReportFileExtension, pBuffer, nHeaderPosition, dwBufferSize))
|
|
{
|
|
dwErrorCode = ERROR_INTERNAL_ERROR;
|
|
goto end; // Internal error.
|
|
}
|
|
|
|
// Notification e-mail.
|
|
if (! WriteBinaryString(EncStream, g_szNotificationEMail, pBuffer, nHeaderPosition, dwBufferSize))
|
|
{
|
|
dwErrorCode = ERROR_INTERNAL_ERROR;
|
|
goto end; // Internal error.
|
|
}
|
|
|
|
_ASSERTE((DWORD)nHeaderPosition <= dwBufferSize);
|
|
// Store real data size.
|
|
((PDWORD)pBuffer)[1] = (DWORD)nHeaderPosition + dwFileSize;
|
|
|
|
WSAOVERLAPPED ov;
|
|
ZeroMemory(&ov, sizeof(ov));
|
|
ov.hEvent = hCompletionEvent;
|
|
|
|
DWORD dwPacketSize = (DWORD)nHeaderPosition;
|
|
for (;;)
|
|
{
|
|
WSABUF buf;
|
|
buf.buf = (char*)pBuffer;
|
|
buf.len = dwPacketSize;
|
|
while (buf.len > 0)
|
|
{
|
|
DWORD dwProcessedNumber = 0;
|
|
if (WSASend(sock, &buf, 1, &dwProcessedNumber, 0, &ov, NULL) == SOCKET_ERROR)
|
|
{
|
|
dwErrorCode = WaitForPendingSocketRequest(pTransferThreadParams);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto end;
|
|
DWORD dwFlags;
|
|
if (! WSAGetOverlappedResult(sock, &ov, &dwProcessedNumber, FALSE, &dwFlags))
|
|
{
|
|
dwErrorCode = WSAGetLastError();
|
|
goto end; // Internal error.
|
|
}
|
|
}
|
|
buf.buf += dwProcessedNumber;
|
|
buf.len -= dwProcessedNumber;
|
|
}
|
|
if (! ReadFile(hFile, pBuffer, dwBufferSize, &dwPacketSize, NULL) || dwPacketSize == 0)
|
|
{
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_CHECKINGERRORSTATUS, 0, 0);
|
|
goto end; // End of file.
|
|
}
|
|
}
|
|
end:
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Initiate graceful shutdown.
|
|
shutdown(sock, SD_BOTH);
|
|
}
|
|
else
|
|
{
|
|
if (bConnected && FCancelIo)
|
|
{
|
|
// Cancel pending send operation.
|
|
FCancelIo((HANDLE)sock);
|
|
}
|
|
// Initiate abortive shutdown.
|
|
LINGER linger;
|
|
linger.l_onoff = TRUE;
|
|
linger.l_linger = 0;
|
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof(linger));
|
|
}
|
|
closesocket(sock);
|
|
}
|
|
}
|
|
WSACleanup();
|
|
}
|
|
delete[] pBuffer;
|
|
}
|
|
else
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
CloseHandle(hFile);
|
|
}
|
|
else
|
|
dwErrorCode = GetLastError();
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
/**
|
|
* Set error state and find system error description.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
* @param dwErrorCode - error code.
|
|
*/
|
|
static void SetInternetErrorMessage(CTransferThreadParams* pTransferThreadParams, DWORD dwErrorCode)
|
|
{
|
|
HANDLE hWinInetDll = GetModuleHandle(_T("wininet.dll"));
|
|
PTSTR pszMessageBuffer = NULL;
|
|
DWORD dwBaseMessageLength = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
|
|
hWinInetDll, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
|
(PTSTR)&pszMessageBuffer, 0, NULL);
|
|
if (pszMessageBuffer == NULL)
|
|
{
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
return;
|
|
}
|
|
TrimSpaces(pszMessageBuffer);
|
|
if (dwErrorCode == ERROR_INTERNET_EXTENDED_ERROR)
|
|
{
|
|
DWORD dwInetError, dwAdditionalMessageLength;
|
|
if (InternetGetLastResponseInfo(&dwInetError, NULL, &dwAdditionalMessageLength))
|
|
{
|
|
++dwAdditionalMessageLength;
|
|
const DWORD dwMessageDividerSize = 128;
|
|
PTSTR pszNewMessageBuffer = (PTSTR)LocalReAlloc(pszMessageBuffer, (dwBaseMessageLength + dwMessageDividerSize + dwAdditionalMessageLength) * sizeof(TCHAR), LMEM_MOVEABLE);
|
|
if (pszNewMessageBuffer != NULL)
|
|
{
|
|
pszMessageBuffer = pszNewMessageBuffer;
|
|
pszNewMessageBuffer += dwBaseMessageLength;
|
|
pszNewMessageBuffer += LoadString(g_hInstance, IDS_ADDITIONAL_INFORMATION, pszNewMessageBuffer, dwMessageDividerSize);
|
|
InternetGetLastResponseInfo(&dwInetError, pszNewMessageBuffer, &dwAdditionalMessageLength);
|
|
TrimSpaces(pszNewMessageBuffer);
|
|
}
|
|
}
|
|
}
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode, pszMessageBuffer);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
/**
|
|
* Print Internet status debugging information.
|
|
* @param dwInternetStatus - status code that indicates why the callback function is being called.
|
|
* @param pvStatusInformation - pointer to additional status information.
|
|
* @param dwStatusInformationLength - size of the data pointed to by @a pvStatusInformation.
|
|
*/
|
|
static void PrintInternetStatus(DWORD dwInternetStatus, PVOID pvStatusInformation, DWORD dwStatusInformationLength)
|
|
{
|
|
#define CONVERT_INTERNET_STATUS_TO_STRING(status) \
|
|
case INTERNET_STATUS_##status: pszInternetStatus = _T(#status); break
|
|
#define CONVERT_INTERNET_STATE_TO_STRING(state) \
|
|
case INTERNET_STATE_##state: pszInternetState = _T(#state); break
|
|
|
|
PCTSTR pszInternetStatus, pszInternetState;
|
|
TCHAR szInternetStatusBuffer[128], szInternetStateBuffer[128];
|
|
|
|
switch (dwInternetStatus)
|
|
{
|
|
CONVERT_INTERNET_STATUS_TO_STRING(RESOLVING_NAME);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(NAME_RESOLVED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(CONNECTING_TO_SERVER);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(CONNECTED_TO_SERVER);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(SENDING_REQUEST);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(REQUEST_SENT);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(RECEIVING_RESPONSE);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(RESPONSE_RECEIVED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(CTL_RESPONSE_RECEIVED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(PREFETCH);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(CLOSING_CONNECTION);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(CONNECTION_CLOSED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(HANDLE_CREATED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(HANDLE_CLOSING);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(DETECTING_PROXY);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(REQUEST_COMPLETE);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(REDIRECT);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(INTERMEDIATE_RESPONSE);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(USER_INPUT_REQUIRED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(STATE_CHANGE);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(COOKIE_SENT);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(COOKIE_RECEIVED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(PRIVACY_IMPACTED);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(P3P_HEADER);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(P3P_POLICYREF);
|
|
CONVERT_INTERNET_STATUS_TO_STRING(COOKIE_HISTORY);
|
|
default:
|
|
_stprintf_s(szInternetStatusBuffer, countof(szInternetStatusBuffer), _T("0x%08lX"), dwInternetStatus);
|
|
pszInternetStatus = szInternetStatusBuffer;
|
|
}
|
|
|
|
OutputDebugString(_T("INTERNET STATUS: "));
|
|
OutputDebugString(pszInternetStatus);
|
|
if (dwInternetStatus == INTERNET_STATUS_STATE_CHANGE)
|
|
{
|
|
_ASSERTE(dwStatusInformationLength == sizeof(DWORD));
|
|
switch (*(PDWORD)pvStatusInformation)
|
|
{
|
|
CONVERT_INTERNET_STATE_TO_STRING(CONNECTED);
|
|
CONVERT_INTERNET_STATE_TO_STRING(DISCONNECTED);
|
|
CONVERT_INTERNET_STATE_TO_STRING(DISCONNECTED_BY_USER);
|
|
CONVERT_INTERNET_STATE_TO_STRING(IDLE);
|
|
CONVERT_INTERNET_STATE_TO_STRING(BUSY);
|
|
default:
|
|
_stprintf_s(szInternetStateBuffer, countof(szInternetStateBuffer), _T("0x%08lX"), *(PDWORD)pvStatusInformation);
|
|
pszInternetState = szInternetStateBuffer;
|
|
}
|
|
OutputDebugString(_T(", INTERNET STATE: "));
|
|
OutputDebugString(pszInternetState);
|
|
}
|
|
OutputDebugString(_T("\n"));
|
|
|
|
#undef CONVERT_INTERNET_STATUS_TO_STRING
|
|
#undef CONVERT_INTERNET_STATE_TO_STRING
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* Application-defined status callback function.
|
|
* @param hSession - handle for which the callback function is being called.
|
|
* @param dwContext - pointer to a variable that specifies the application-defined context value associated with @a hSession.
|
|
* @param dwInternetStatus - status code that indicates why the callback function is being called.
|
|
* @param pvStatusInformation - pointer to additional status information.
|
|
* @param dwStatusInformationLength - size of the data pointed to by @a pvStatusInformation.
|
|
*/
|
|
static void CALLBACK InternetStatusCallback(HINTERNET hSession, DWORD_PTR dwContext, DWORD dwInternetStatus, PVOID pvStatusInformation, DWORD dwStatusInformationLength)
|
|
{
|
|
hSession; dwInternetStatus; pvStatusInformation; dwStatusInformationLength;
|
|
CTransferThreadParams* pTransferThreadParams = (CTransferThreadParams*)dwContext;
|
|
|
|
#ifdef _DEBUG
|
|
PrintInternetStatus(dwInternetStatus, pvStatusInformation, dwStatusInformationLength);
|
|
#endif
|
|
|
|
HANDLE hCompletionEvent = pTransferThreadParams->GetCompletionEvent();
|
|
switch (pTransferThreadParams->GetContext())
|
|
{
|
|
case CTransferThreadParams::IC_SENDREQUEST:
|
|
case CTransferThreadParams::IC_WRITEFILE:
|
|
case CTransferThreadParams::IC_ENDREQUEST:
|
|
case CTransferThreadParams::IC_RECEIVINGRESPONSE:
|
|
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
|
|
{
|
|
_ASSERTE(dwStatusInformationLength == sizeof(INTERNET_ASYNC_RESULT));
|
|
pTransferThreadParams->SetCallbackResult(((LPINTERNET_ASYNC_RESULT)pvStatusInformation)->dwError);
|
|
SetEvent(hCompletionEvent);
|
|
}
|
|
else if (dwInternetStatus == INTERNET_STATUS_STATE_CHANGE)
|
|
{
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_ERROR);
|
|
SetEvent(hCompletionEvent);
|
|
}
|
|
break;
|
|
case CTransferThreadParams::IC_INTERNETCONNECT:
|
|
case CTransferThreadParams::IC_OPENREQUEST:
|
|
case CTransferThreadParams::IC_CLOSEHANDLE:
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Wait until pending operation is either complete or canceled.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
* @return error code.
|
|
*/
|
|
static DWORD WaitForPendingInternetRequest(CTransferThreadParams* pTransferThreadParams)
|
|
{
|
|
HANDLE hCancelationEvent = pTransferThreadParams->GetCancellationEvent();
|
|
HANDLE hCompletionEvent = pTransferThreadParams->GetCompletionEvent();
|
|
HANDLE arrEvents[] = { hCompletionEvent, hCancelationEvent };
|
|
|
|
DWORD dwErrorCode = ERROR_SUCCESS;
|
|
DWORD dwTemp = GetLastError();
|
|
if (dwTemp != ERROR_IO_PENDING)
|
|
{
|
|
dwErrorCode = dwTemp;
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
else if (WaitForMultipleObjects(countof(arrEvents), arrEvents, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
|
|
{
|
|
dwErrorCode = ERROR_OPERATION_ABORTED;
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
}
|
|
else if (pTransferThreadParams->GetContext() == CTransferThreadParams::IC_ERROR)
|
|
{
|
|
dwErrorCode = ERROR_REQUEST_ABORTED;
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = pTransferThreadParams->GetCallbackResult();
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief Submit bug report through HTTP protocol to BugTrap server.
|
|
* @param pszSupportUrl - support URL address.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
* @return error code.
|
|
*/
|
|
static DWORD HTTPSendReport(PCTSTR pszSupportUrl, CTransferThreadParams* pTransferThreadParams)
|
|
{
|
|
#define MESSAGE_HEADER _T("Content-Type: multipart/form-data; boundary=") _T(SECTION_BOUNDARY)
|
|
|
|
#define SECTION_BOUNDARY "BUGTRAP-7A1D6378-1294-491B-996C-37D4FF91D184"
|
|
|
|
#define TEXT_SECTION_HEADER(name) "--" SECTION_BOUNDARY "\r\n" \
|
|
"Content-Disposition: form-data; name=\"" name "\"\r\n" \
|
|
"Content-Type: text/plain; charset=\"utf-8\"\r\n" \
|
|
"Content-Transfer-Encoding: 8bit\r\n" \
|
|
"\r\n"
|
|
|
|
#define FILE_SECTION_HEADER(name, file) "--" SECTION_BOUNDARY "\r\n" \
|
|
"Content-Disposition: form-data; name=\"" name "\"; filename=\"" file "\"\r\n" \
|
|
"Content-Type: application/octet-stream\r\n" \
|
|
"Content-Transfer-Encoding: binary\r\n" \
|
|
"\r\n"
|
|
|
|
#define SECTION_TRAILER "\r\n"
|
|
|
|
#define MESSAGE_TRAILER SECTION_TRAILER "--" SECTION_BOUNDARY "--\r\n\r\n"
|
|
|
|
HWND hwndSink = pTransferThreadParams->GetSinkWnd();
|
|
// Call dial-up dialog box to initiate a connection to the Internet
|
|
// or check if a connection already exists.
|
|
DWORD dwErrorCode = InternetAttemptConnect(0);
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
HANDLE hFile = CreateFile(g_szInternalReportFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD dwFileSize = GetFileSize(hFile, NULL);
|
|
DWORD dwBufferSize = min(dwFileSize, g_dwMaxBufferSize);
|
|
PBYTE pBuffer = new BYTE[dwBufferSize];
|
|
if (pBuffer != NULL)
|
|
{
|
|
TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH],
|
|
szPassword[INTERNET_MAX_PASSWORD_LENGTH],
|
|
szHostName[INTERNET_MAX_HOST_NAME_LENGTH],
|
|
szUrlPath[INTERNET_MAX_PATH_LENGTH];
|
|
URL_COMPONENTS urlComponents;
|
|
ZeroMemory(&urlComponents, sizeof(urlComponents));
|
|
urlComponents.dwStructSize = sizeof(urlComponents);
|
|
urlComponents.lpszHostName = szHostName;
|
|
urlComponents.dwHostNameLength = countof(szHostName);
|
|
urlComponents.lpszUserName = szUserName;
|
|
urlComponents.dwUserNameLength = countof(szUserName);
|
|
urlComponents.lpszPassword = szPassword;
|
|
urlComponents.dwPasswordLength = countof(szPassword);
|
|
urlComponents.lpszUrlPath = szUrlPath;
|
|
urlComponents.dwUrlPathLength = countof(szUrlPath);
|
|
if (InternetCrackUrl(pszSupportUrl, 0, ICU_DECODE | ICU_ESCAPE, &urlComponents))
|
|
{
|
|
if (urlComponents.nScheme == INTERNET_SCHEME_HTTP)
|
|
{
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("InternetOpen()\n"));
|
|
#endif
|
|
HINTERNET hSession = InternetOpen(BUGTRAP_TITLE, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_ASYNC);
|
|
if (hSession != NULL)
|
|
{
|
|
InternetSetStatusCallback(hSession, InternetStatusCallback);
|
|
INTERNET_PORT nPort = urlComponents.nPort ? urlComponents.nPort : INTERNET_DEFAULT_HTTP_PORT;
|
|
PCTSTR pszUserName = *szUserName ? szUserName : NULL;
|
|
PCTSTR pszPassword = *szPassword ? szPassword : NULL;
|
|
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_INTERNETCONNECT);
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("InternetConnect()\n"));
|
|
#endif
|
|
HINTERNET hConnection = InternetConnect(hSession, szHostName, nPort, pszUserName, pszPassword, INTERNET_SERVICE_HTTP, INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_NO_UI, (DWORD_PTR)pTransferThreadParams);
|
|
if (hConnection)
|
|
{
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_OPENREQUEST);
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("HttpOpenRequest()\n"));
|
|
#endif
|
|
HINTERNET hRequest = HttpOpenRequest(hConnection, _T("POST"), szUrlPath, NULL, NULL, NULL, INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_NO_UI, (DWORD_PTR)pTransferThreadParams);
|
|
if (hRequest)
|
|
{
|
|
CMemStream MemStream(4 * 1024);
|
|
CUTF8EncStream EncStream(&MemStream);
|
|
CHAR szTemp[16];
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("protocolSignature"));
|
|
EncStream.WriteBytes((const BYTE*)&g_dwProtocolSignature, sizeof(g_dwProtocolSignature));
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("messageType"));
|
|
_itoa_s(MT_COMPOUND_MESSAGE, szTemp, countof(szTemp), 10);
|
|
EncStream.WriteAscii(szTemp);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("messageFlags"));
|
|
_itoa_s(CMF_NONE, szTemp, countof(szTemp), 10);
|
|
EncStream.WriteAscii(szTemp);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("appName"));
|
|
EncStream.WriteUTF8Bin(g_szAppName);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("appVersion"));
|
|
EncStream.WriteUTF8Bin(g_szAppVersion);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("reportFileExtension"));
|
|
PCTSTR pszReportFileExtension = CSymEngine::GetReportFileExtension();
|
|
EncStream.WriteUTF8Bin(pszReportFileExtension);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(TEXT_SECTION_HEADER("notificationEMail"));
|
|
EncStream.WriteUTF8Bin(g_szNotificationEMail);
|
|
EncStream.WriteAscii(SECTION_TRAILER);
|
|
|
|
EncStream.WriteAscii(FILE_SECTION_HEADER("reportData", "report.dat"));
|
|
|
|
static const TCHAR szHeader[] = MESSAGE_HEADER;
|
|
const DWORD dwHeaderLength = countof(szHeader) - 1;
|
|
static const CHAR szTrailer[] = MESSAGE_TRAILER;
|
|
const DWORD dwTrailerLength = countof(szTrailer) - 1;
|
|
|
|
const BYTE* pFormData = MemStream.GetBuffer();
|
|
if (pFormData != NULL)
|
|
{
|
|
DWORD dwFormDataLength = (DWORD)MemStream.GetLength();
|
|
_ASSERTE(dwFormDataLength > 0);
|
|
|
|
INTERNET_BUFFERS InetBuf;
|
|
ZeroMemory(&InetBuf, sizeof(InetBuf));
|
|
InetBuf.dwStructSize = sizeof(InetBuf);
|
|
InetBuf.dwHeadersTotal = dwHeaderLength;
|
|
InetBuf.dwHeadersLength = dwHeaderLength;
|
|
InetBuf.lpcszHeader = szHeader;
|
|
InetBuf.dwBufferTotal = dwFormDataLength + dwFileSize + dwTrailerLength;
|
|
|
|
HANDLE hCompletionEvent = pTransferThreadParams->GetCompletionEvent();
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_CONNECTINGTOSERVER, 0, 0);
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_SENDREQUEST);
|
|
ResetEvent(hCompletionEvent);
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("HttpSendRequestEx()\n"));
|
|
#endif
|
|
if (! HttpSendRequestEx(hRequest, &InetBuf, NULL, 0, 456))
|
|
dwErrorCode = WaitForPendingInternetRequest(pTransferThreadParams);
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_SENDINGREPORT, 0, 0);
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_WRITEFILE);
|
|
const BYTE* pBytes = pFormData;
|
|
DWORD dwNumBytes = dwFormDataLength;
|
|
BOOL bDataEnd = FALSE;
|
|
for (;;)
|
|
{
|
|
ResetEvent(hCompletionEvent);
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("InternetWriteFile()\n"));
|
|
#endif
|
|
while (dwNumBytes > 0)
|
|
{
|
|
DWORD dwBytesWritten = 0;
|
|
if (! InternetWriteFile(hRequest, pBytes, dwNumBytes, &dwBytesWritten))
|
|
{
|
|
dwErrorCode = WaitForPendingInternetRequest(pTransferThreadParams);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
bDataEnd = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
pBytes += dwBytesWritten;
|
|
dwNumBytes -= dwBytesWritten;
|
|
}
|
|
if (bDataEnd)
|
|
break;
|
|
DWORD dwBytesRead = 0;
|
|
if (! ReadFile(hFile, pBuffer, dwBufferSize, &dwBytesRead, NULL) || dwBytesRead == 0)
|
|
{
|
|
bDataEnd = TRUE;
|
|
pBytes = (const BYTE*)szTrailer;
|
|
dwNumBytes = dwTrailerLength;
|
|
}
|
|
else
|
|
{
|
|
pBytes = pBuffer;
|
|
dwNumBytes = dwBytesRead;
|
|
}
|
|
}
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
if (hwndSink)
|
|
PostMessage(hwndSink, UM_CHECKINGERRORSTATUS, 0, 0);
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_ENDREQUEST);
|
|
ResetEvent(hCompletionEvent);
|
|
#ifdef _DEBUG
|
|
OutputDebugString(_T("HttpEndRequest()\n"));
|
|
#endif
|
|
if (! HttpEndRequest(hRequest, NULL, 0, NULL))
|
|
dwErrorCode = WaitForPendingInternetRequest(pTransferThreadParams);
|
|
#ifdef _DEBUG
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
OutputDebugString(_T("WEB RESPONSE:\n========================================\n"));
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_RECEIVINGRESPONSE);
|
|
for (;;)
|
|
{
|
|
CHAR arrBuffer[1024];
|
|
DWORD dwBytesRead = 0;
|
|
if (! InternetReadFile(hRequest, arrBuffer, sizeof(arrBuffer) - 1, &dwBytesRead))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
if (dwErrorCode != ERROR_IO_PENDING)
|
|
break;
|
|
else
|
|
{
|
|
ResetEvent(hCompletionEvent);
|
|
dwErrorCode = WaitForPendingInternetRequest(pTransferThreadParams);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
if (dwBytesRead == 0)
|
|
break;
|
|
arrBuffer[dwBytesRead] = '\0';
|
|
OutputDebugStringA(arrBuffer);
|
|
}
|
|
OutputDebugString(_T("\n========================================\n"));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
}
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_CLOSEHANDLE);
|
|
InternetCloseHandle(hRequest);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError(); // HttpOpenRequest() failed
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_CLOSEHANDLE);
|
|
InternetCloseHandle(hConnection);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError(); // InternetConnect() failed
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
pTransferThreadParams->SetContext(CTransferThreadParams::IC_CLOSEHANDLE);
|
|
InternetCloseHandle(hSession);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError(); // InternetOpen() failed
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_BAD_NET_NAME;
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError(); // InternetCrackUrl() failed
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode);
|
|
}
|
|
delete[] pBuffer;
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError(); // CreateFile() failed
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
}
|
|
}
|
|
else
|
|
SetInternetErrorMessage(pTransferThreadParams, dwErrorCode); // InternetAttemptConnect() failed
|
|
|
|
return dwErrorCode;
|
|
|
|
#undef MESSAGE_HEADER
|
|
#undef SECTION_BOUNDARY
|
|
#undef TEXT_SECTION_HEADER
|
|
#undef SECTION_TRAILER
|
|
#undef MESSAGE_TRAILER
|
|
}
|
|
|
|
/**
|
|
* @brief Thread function responsible for sending report to the server.
|
|
* @param pParam - thread parameter (pointer to CTransferThreadParams).
|
|
* @return error code.
|
|
*/
|
|
static UINT CALLBACK TransferThreadProc(PVOID pParam)
|
|
{
|
|
__try
|
|
{
|
|
CTransferThreadParams* pTransferThreadParams = (CTransferThreadParams*)pParam;
|
|
_ASSERTE(pTransferThreadParams != NULL);
|
|
DWORD dwErrorCode = ERROR_SUCCESS;
|
|
if (*g_szSupportHost == _T('\0'))
|
|
dwErrorCode = ERROR_BAD_NETPATH;
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
pTransferThreadParams->SetErrorCode(dwErrorCode);
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
static const TCHAR szHttpPrefix[] = _T("http://");
|
|
static const size_t nHttpPrefixLength = countof(szHttpPrefix) - 1;
|
|
if (_tcsnicmp(g_szSupportHost, szHttpPrefix, nHttpPrefixLength) == 0)
|
|
dwErrorCode = HTTPSendReport(g_szSupportHost, pTransferThreadParams);
|
|
else
|
|
dwErrorCode = WSASendReport(g_szSupportHost, pTransferThreadParams);
|
|
}
|
|
pTransferThreadParams->PostCompletionMessage();
|
|
return dwErrorCode;
|
|
}
|
|
__except (InternalFilter(GetExceptionInformation()))
|
|
{
|
|
return ERROR_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Start transfer thread.
|
|
* @param pTransferThreadParams - thread parameters.
|
|
*/
|
|
HANDLE StartTransferThread(CTransferThreadParams* pTransferThreadParams)
|
|
{
|
|
return (HANDLE)_beginthreadex(NULL, 0, TransferThreadProc, pTransferThreadParams, 0, NULL);
|
|
}
|
|
|
|
/**
|
|
* Close transfer thread handle and free used memory.
|
|
* @param hTransferThread - thread handle.
|
|
* @return completion code.
|
|
*/
|
|
DWORD CloseTransferThread(HANDLE hTransferThread)
|
|
{
|
|
DWORD dwErrorCode;
|
|
if (hTransferThread)
|
|
{
|
|
WaitForSingleObject(hTransferThread, INFINITE);
|
|
GetExitCodeThread(hTransferThread, &dwErrorCode);
|
|
CloseHandle(hTransferThread);
|
|
}
|
|
else
|
|
dwErrorCode = ERROR_INTERNAL_ERROR;
|
|
return dwErrorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief Submit bug report over network protocol.
|
|
* @param hwndParent - parent window handle (valid if user interface is enabled).
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL SendTempReport(HWND hwndParent)
|
|
{
|
|
BOOL bResult;
|
|
if (*g_szSupportHost && g_nSupportPort)
|
|
{
|
|
if (g_bShowUI)
|
|
{
|
|
if ((g_dwFlags & (BTF_DETAILEDMODE | BTF_DESCRIBEERROR)) == (BTF_DETAILEDMODE | BTF_DESCRIBEERROR))
|
|
bResult = (BOOL)DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DESCRIBE_ERROR_DLG), hwndParent, DescribeErrorDlgProc);
|
|
else
|
|
bResult = TRUE;
|
|
if (bResult)
|
|
bResult = (BOOL)DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_TRANSFERPROGRESS_DLG), hwndParent, TransferProgressDlgProc);
|
|
EndDialog(hwndParent, FALSE);
|
|
}
|
|
else
|
|
{
|
|
CTransferThreadParams TransferThreadParams;
|
|
bResult = TransferThreadProc(&TransferThreadParams) == ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
bResult = FALSE;
|
|
return bResult;
|
|
}
|
|
|
|
/**
|
|
* @brief E-mail error report or send it over network protocol.
|
|
* @param hwndParent - parent window handle (valid if user interface is enabled).
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL SubmitTempReport(HWND hwndParent)
|
|
{
|
|
if (*g_szSupportHost && g_nSupportPort)
|
|
return SendTempReport(hwndParent);
|
|
else if (*g_szSupportEMail)
|
|
return MailTempReportEx(hwndParent);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize introduction message.
|
|
* @param hwnd - parent window handle.
|
|
* @param hlURL - hyper link control.
|
|
*/
|
|
void InitIntro(HWND hwnd, CHyperLink& hlURL)
|
|
{
|
|
HWND hwndCtl;
|
|
|
|
if (g_pResManager->m_hDialogIcon)
|
|
{
|
|
HWND hwndDialogIcon = GetDlgItem(hwnd, IDC_DIALOG_ICON);
|
|
SendMessage(hwndDialogIcon, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)g_pResManager->m_hDialogIcon);
|
|
}
|
|
|
|
if (*g_szAppName)
|
|
SetWindowText(hwnd, g_szAppName);
|
|
|
|
if (! g_strFirstIntroMesage.IsEmpty())
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, IDC_INTRO1);
|
|
SetWindowText(hwndCtl, g_strFirstIntroMesage);
|
|
}
|
|
|
|
if (! g_strSecondIntroMesage.IsEmpty())
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, IDC_INTRO2);
|
|
SetWindowText(hwndCtl, g_strSecondIntroMesage);
|
|
}
|
|
|
|
if (*g_szSupportURL)
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, IDC_URL);
|
|
SetWindowText(hwndCtl, g_szSupportURL);
|
|
hlURL.SetLinkURL(g_szSupportURL);
|
|
hlURL.Attach(hwndCtl);
|
|
}
|
|
else
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, IDC_URL);
|
|
ShowWindow(hwndCtl, SW_HIDE);
|
|
hwndCtl = GetDlgItem(hwnd, IDC_URL_PREFIX);
|
|
ShowWindow(hwndCtl, SW_HIDE);
|
|
}
|
|
|
|
_ASSERTE(g_pResManager != NULL);
|
|
if (g_pResManager->m_hBigAppIcon)
|
|
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)g_pResManager->m_hBigAppIcon);
|
|
if (g_pResManager->m_hSmallAppIcon)
|
|
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_pResManager->m_hSmallAppIcon);
|
|
|
|
CenterWindow(hwnd, GetParent(hwnd));
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize About menu item.
|
|
* @param hwnd - parent window handle.
|
|
*/
|
|
void InitAbout(HWND hwnd)
|
|
{
|
|
// IDM_ABOUTBOX must be in the system command range.
|
|
_ASSERTE((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
|
_ASSERTE(IDM_ABOUTBOX < 0xF000);
|
|
|
|
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
|
|
if (hSysMenu != NULL)
|
|
{
|
|
TCHAR szAboutMenu[64];
|
|
LoadString(g_hInstance, IDS_ABOUT_ITEM, szAboutMenu, countof(szAboutMenu));
|
|
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL);
|
|
AppendMenu(hSysMenu, MF_STRING, IDM_ABOUTBOX, szAboutMenu);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Generate report file name based on user criteria.
|
|
* @param pszFileName - user-supplied file name.
|
|
* @param szFileNameBuffer - file name buffer that is used to store default report name.
|
|
* @return pointer to generated report name.
|
|
*/
|
|
static PCTSTR GetReportFileName(PCTSTR pszFileName, PTSTR szFileNameBuffer)
|
|
{
|
|
_ASSERTE(g_pSymEngine != NULL);
|
|
bool bGenerateFileName = pszFileName == NULL || *pszFileName == _T('\0');
|
|
if (bGenerateFileName || ! PathIsRoot(pszFileName))
|
|
{
|
|
PCTSTR pszReportFolder = BT_GetReportFilePath();
|
|
if (bGenerateFileName)
|
|
{
|
|
TCHAR szDefaultReportName[MAX_PATH];
|
|
g_pSymEngine->GetReportFileName(szDefaultReportName, countof(szDefaultReportName));
|
|
PathCombine(szFileNameBuffer, pszReportFolder, szDefaultReportName);
|
|
}
|
|
else
|
|
PathCombine(szFileNameBuffer, pszReportFolder, pszFileName);
|
|
pszFileName = szFileNameBuffer;
|
|
}
|
|
return pszFileName;
|
|
}
|
|
|
|
/**
|
|
* @brief Save report to a file.
|
|
* @param pszFileName - snapshot file name or NULL, if you want to generate file name automatically.
|
|
* @return true if operation has been completed successfully.
|
|
*/
|
|
BOOL SaveReport(PCTSTR pszFileName)
|
|
{
|
|
// Generate report file name when necessary.
|
|
TCHAR szReportFilePath[MAX_PATH];
|
|
pszFileName = GetReportFileName(pszFileName, szReportFilePath);
|
|
// Create parent report folder.
|
|
CreateParentFolder(pszFileName);
|
|
// Store report files to user supplied location.
|
|
return g_pSymEngine->WriteReport(pszFileName, g_pEnumProc);
|
|
}
|
|
|
|
/**
|
|
* @brief Create temporary report file.
|
|
* @return true if operation has been completed successfully.
|
|
*/
|
|
static BOOL CreateTempReport(void)
|
|
{
|
|
BOOL bResult;
|
|
// Scope of progress dialog.
|
|
CWaitDialog wait;
|
|
if (g_bShowUI)
|
|
wait.BeginWait(NULL);
|
|
|
|
TCHAR szReportFileName[MAX_PATH];
|
|
g_pSymEngine->GetReportFileName(szReportFileName, countof(szReportFileName));
|
|
// Generate full report file name.
|
|
GetTempPath(countof(g_szInternalReportFolder), g_szInternalReportFolder);
|
|
PathCombine(g_szInternalReportFilePath, g_szInternalReportFolder, szReportFileName);
|
|
if (g_dwFlags & BTF_DETAILEDMODE)
|
|
{
|
|
// Generate report files in temporary location.
|
|
CreateTempFolder(g_szInternalReportFolder, countof(g_szInternalReportFolder));
|
|
bResult = g_pSymEngine->WriteReportFiles(g_szInternalReportFolder, g_pEnumProc) &&
|
|
g_pSymEngine->ArchiveReportFiles(g_szInternalReportFolder, g_szInternalReportFilePath);
|
|
}
|
|
else
|
|
{
|
|
// Create single log file in system temporary folder.
|
|
bResult = g_pSymEngine->WriteLog(g_szInternalReportFilePath, g_pEnumProc);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove temporarily generated report files.
|
|
*/
|
|
static void DeleteTempReport(void)
|
|
{
|
|
if (g_dwFlags & BTF_DETAILEDMODE)
|
|
{
|
|
DeleteFolder(g_szInternalReportFolder);
|
|
*g_szInternalReportFolder = _T('\0');
|
|
}
|
|
DeleteFile(g_szInternalReportFilePath);
|
|
*g_szInternalReportFilePath = _T('\0');
|
|
}
|
|
|
|
/**
|
|
* @brief Submit bug report over network protocol.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL SendReport(void)
|
|
{
|
|
if (*g_szSupportHost == _T('\0') || g_nSupportPort == 0)
|
|
return FALSE;
|
|
if (! CreateTempReport())
|
|
return FALSE;
|
|
BOOL bResult = SendTempReport(NULL);
|
|
DeleteTempReport();
|
|
return bResult;
|
|
}
|
|
|
|
/**
|
|
* @brief Send bug report through e-mail either using custom or system dialogs.
|
|
* @param hwndParent - parent window handle.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL MailReport(void)
|
|
{
|
|
if (*g_szSupportEMail == _T('\0'))
|
|
return FALSE;
|
|
if (! CreateTempReport())
|
|
return FALSE;
|
|
BOOL bResult = MailTempReport(NULL);
|
|
DeleteTempReport();
|
|
return bResult;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform BugTrap action (show dialog, submit report, etc.).
|
|
*/
|
|
static void ExecuteHandlerAction(void)
|
|
{
|
|
if (g_eActivityType == BTA_SAVEREPORT)
|
|
{
|
|
SaveReport(NULL);
|
|
return;
|
|
}
|
|
// Create temporary report file.
|
|
BOOL bResult = CreateTempReport();
|
|
if (bResult)
|
|
{
|
|
// Process remaining actions.
|
|
switch (g_eActivityType)
|
|
{
|
|
case BTA_MAILREPORT:
|
|
MailTempReport(NULL);
|
|
break;
|
|
case BTA_SENDREPORT:
|
|
SendTempReport(NULL);
|
|
break;
|
|
case BTA_SHOWUI:
|
|
if (g_pResManager)
|
|
{
|
|
if (g_dwFlags & BTF_SHOWADVANCEDUI)
|
|
DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_MAIN_DLG), NULL, MainDlgProc);
|
|
else if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_SIMPLE_DLG), NULL, SimpleDlgProc) == TRUE)
|
|
DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_MAIN_DLG), NULL, MainDlgProc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// Remove temporarily generated report files.
|
|
DeleteTempReport();
|
|
}
|
|
|
|
/**
|
|
* @brief Thread function responsible for processing BugTrap actions in background.
|
|
* @param pParam - thread parameter.
|
|
* @return error code.
|
|
*/
|
|
static UINT CALLBACK HandlerThreadProc(PVOID pParam)
|
|
{
|
|
pParam;
|
|
__try
|
|
{
|
|
ExecuteHandlerAction();
|
|
}
|
|
__except (InternalFilter(GetExceptionInformation()))
|
|
{
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Get application window.
|
|
* @return application window handle.
|
|
*/
|
|
static HWND GetAppWindow(void)
|
|
{
|
|
HWND hwndParent = GetActiveWindow();
|
|
if (hwndParent == NULL)
|
|
hwndParent = GetForegroundWindow();
|
|
if (hwndParent != NULL)
|
|
{
|
|
DWORD dwProcessID = 0;
|
|
GetWindowThreadProcessId(hwndParent, &dwProcessID);
|
|
if (dwProcessID == GetCurrentProcessId())
|
|
{
|
|
HWND hwndNext;
|
|
for (;;)
|
|
{
|
|
hwndNext = GetParent(hwndParent);
|
|
if (hwndNext != NULL)
|
|
hwndParent = hwndNext;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
hwndParent = NULL;
|
|
}
|
|
return hwndParent;
|
|
}
|
|
|
|
/**
|
|
* @brief Hide application window.
|
|
* @param hwndParent - application window handle.
|
|
*/
|
|
static void HideAppWindow(HWND hwndParent)
|
|
{
|
|
if (hwndParent != NULL)
|
|
{
|
|
__try {
|
|
DWORD_PTR dwResult;
|
|
// Ping parent window
|
|
if (SendMessageTimeout(hwndParent, WM_NULL, 0l, 0l, SMTO_ABORTIFHUNG, SEND_MSG_TIMEOUT, &dwResult))
|
|
ShowWindow(hwndParent, SW_HIDE);
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// ignore any exception in broken app...
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Perform BugTrap action (show dialog, submit report, etc.).
|
|
*/
|
|
void StartHandlerThread(void)
|
|
{
|
|
_ASSERTE(g_pSymEngine != NULL && g_pEnumProc != NULL);
|
|
if (g_pSymEngine == NULL || g_pEnumProc == NULL)
|
|
return;
|
|
// Execute BugTrap action.
|
|
HWND hwndParent;
|
|
if (g_eActivityType == BTA_SHOWUI)
|
|
{
|
|
g_bShowUI = TRUE;
|
|
hwndParent = GetAppWindow();
|
|
g_pResManager = new CResManager(hwndParent);
|
|
HideAppWindow(hwndParent);
|
|
}
|
|
else
|
|
{
|
|
g_bShowUI = FALSE;
|
|
hwndParent = NULL;
|
|
}
|
|
HANDLE hHandlerThread = (HANDLE)_beginthreadex(NULL, 0, HandlerThreadProc, NULL, 0, NULL);
|
|
if (hHandlerThread != NULL)
|
|
{
|
|
WaitForSingleObject(hHandlerThread, INFINITE);
|
|
CloseHandle(hHandlerThread);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal unhandled exception handler.
|
|
* @param pExceptionPointers - pointer to the exception information.
|
|
* @return determines how the exception is handled.
|
|
*/
|
|
LONG InternalFilter(PEXCEPTION_POINTERS pExceptionPointers)
|
|
{
|
|
__try
|
|
{
|
|
SetUnhandledExceptionFilter(NULL);
|
|
static const TCHAR szDbgHelDll[] = _T("DBGHELP.DLL");
|
|
TCHAR szDbgHelpPath[MAX_PATH];
|
|
GetModuleFileName(g_hInstance, szDbgHelpPath, countof(szDbgHelpPath));
|
|
PathRemoveFileSpec(szDbgHelpPath);
|
|
PathAppend(szDbgHelpPath, szDbgHelDll);
|
|
HINSTANCE hDbgHelpDll = LoadLibrary(szDbgHelpPath);
|
|
if (hDbgHelpDll == NULL)
|
|
hDbgHelpDll = LoadLibrary(szDbgHelDll);
|
|
if (hDbgHelpDll != NULL)
|
|
{
|
|
PFMiniDumpWriteDump FMiniDumpWriteDump = (PFMiniDumpWriteDump)GetProcAddress(hDbgHelpDll, "MiniDumpWriteDump");
|
|
if (FMiniDumpWriteDump != NULL)
|
|
{
|
|
TCHAR szDumpFileName[MAX_PATH];
|
|
GetTempPath(countof(szDumpFileName), szDumpFileName);
|
|
PathAppend(szDumpFileName, _T("BugTrap-") _T(FILE_LONG_VERSION) _T(".dmp"));
|
|
HANDLE hFile = CreateFile(szDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
DWORD dwProcessID = GetCurrentProcessId();
|
|
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
|
|
ExInfo.ThreadId = GetCurrentThreadId();
|
|
ExInfo.ExceptionPointers = pExceptionPointers;
|
|
ExInfo.ClientPointers = TRUE;
|
|
BOOL bResult = FMiniDumpWriteDump(hProcess, dwProcessID, hFile, MiniDumpWithDataSegs, &ExInfo, NULL, NULL);
|
|
CloseHandle(hFile);
|
|
if (! bResult)
|
|
DeleteFile(szDumpFileName);
|
|
}
|
|
}
|
|
FreeLibrary(hDbgHelpDll);
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// any possible nested exception is not handled
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/** @} */
|