/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#define UNICODE
#define WIN32_LEAN_AND_MEAN
#if defined _MSC_VER
#pragma warning(push, 1)
#pragma warning(disable:4917)
#endif
#include <windows.h>
#include <windowsx.h>

#include <mapi.h>
#include <commctrl.h>
#include <commdlg.h>
#include <psapi.h>

#include <shellapi.h>
#include <shlobj.h>

#define _UNICODE
#include <tchar.h>

#define _RICHEDIT_VER   0x0200
#include <richedit.h>

#if defined _MSC_VER
#pragma warning(pop)
#endif

#if _RICHEDIT_VER >= 0x0200
#define RICHEDIT    TEXT("riched20.dll")
#else
#define RICHEDIT    TEXT("riched32.dll")
#endif

#include <systools/win32/uwinapi.h>
#include <rtl/digest.h>
#include <rtl/bootstrap.hxx>
#include <osl/file.hxx>
#include <osl/process.h>

#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <string>
#include <hash_map>
#include <winsock.h>
#include <malloc.h>
#include <process.h>

#include <_version.h>

#include "resource.h"
#include "base64.h"

#define FORMATBUFSIZE   (8*1024)
#define MAX_TEXT_BUFFER (32*1024-1)
#define MAX_HOSTNAME    (1024)

#ifdef __MINGW32__
#include <imagehlp.h>
#else
#include <dbghelp.h>
#endif

#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif

using namespace ::std;


wstring  g_wstrProductKey;
string  g_strDefaultLanguage;
FILE    *g_fpStackFile = NULL;
FILE    *g_fpChecksumFile = NULL;
DWORD   g_dwExceptionCode = 0;

CHAR    g_szReportServerA[MAX_HOSTNAME] = "";
USHORT  g_uReportPort = 80;

TCHAR   g_szBuildId[256] = TEXT("");

TCHAR   g_szDumpFileName[MAX_PATH] = TEXT("");

CHAR    g_szDumpFileNameA[MAX_PATH] = "";
CHAR    g_szCommentFileNameA[MAX_PATH] = "";
CHAR    g_szReportFileNameA[MAX_PATH] = "";


bool    g_bNoUserInterface = false;
bool    g_bSendReport = false;
bool    g_bLoadReport = false;

#define REPORT_SERVER   g_szReportServerA
#define REPORT_PORT     g_uReportPort


//***************************************************************************
// tmpfile from msvcrt creates the temporary file in the root of the current
// volume and can fail.

static FILE *_xfopen( const _TCHAR *file, const _TCHAR *mode )
{
#ifdef UNICODE
    if ( (LONG)GetVersion() < 0 )
    {
        char    afile[MAX_PATH];
        char    amode[16];

        WideCharToMultiByte( CP_ACP, 0, file, -1, afile, MAX_PATH, NULL, NULL );
        WideCharToMultiByte( CP_ACP, 0, mode, -1, amode, 16, NULL, NULL );


        return fopen( afile, amode );
    }
    else
#endif
        return _tfopen( file, mode );
}


static FILE *_tmpfile(void)
{
    FILE *fp = NULL;

    TCHAR   szTempPath[MAX_PATH];

    if ( GetTempPath( elementsof(szTempPath), szTempPath ) )
    {
        TCHAR   szFileName[MAX_PATH];

        if ( GetTempFileName( szTempPath, TEXT("CRT"), 0, szFileName ) )
        {
            HANDLE  hFile =  CreateFile(
                szFileName,
                GENERIC_READ | GENERIC_WRITE,
                0, NULL,
                OPEN_EXISTING,
                FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_NORMAL,
                NULL );

            if ( IsValidHandle( hFile ) )
            {
                int fd = _open_osfhandle( (int)hFile, 0 );

                fp = _fdopen( fd, "w+b" );
            }
        }
    }

    return fp;
}
//***************************************************************************

static BOOL GetCrashDataPath( LPTSTR szBuffer )
{
    ::rtl::OUString ustrValue = ::rtl::OUString::createFromAscii("${$BRAND_BASE_DIR/program/bootstrap.ini:UserInstallation}");
    ::rtl::Bootstrap::expandMacros( ustrValue );

    if ( ustrValue.getLength() )
    {
        ustrValue += ::rtl::OUString::createFromAscii("/user/crashdata");

        ::osl::FileBase::RC result = ::osl::Directory::createPath( ustrValue );

        if ( ::osl::FileBase::E_None == result || ::osl::FileBase::E_EXIST == result )
        {
            ::rtl::OUString ustrPath;

            result = ::osl::FileBase::getSystemPathFromFileURL( ustrValue, ustrPath );
            if (  ::osl::FileBase::E_None == result  )
            {
                _tcsncpy( szBuffer, reinterpret_cast<LPCWSTR>(ustrPath.getStr()), MAX_PATH );
                return TRUE;
            }
        }
    }

    return FALSE;
}


static FILE *_open_reportfile( LPCTSTR lpExt, LPCTSTR lpMode )
{
    FILE    *fp = NULL;
    TCHAR   szAppDataPath[MAX_PATH] = _T("");

    if ( GetCrashDataPath( szAppDataPath ) )
    {
        _tcscat( szAppDataPath, _T("\\crashdat") );
        _tcscat( szAppDataPath, lpExt );

        fp = _xfopen( szAppDataPath, lpMode );
    }

    return fp;
}

//***************************************************************************

struct CrashReportParams
{
    BOOL                fAllowContact;
    tstring             sEmail;
    tstring             sTitle;
    tstring             sComment;
    ULONG               uInternetConnection;
    tstring             sProxyServer;
    tstring             sProxyPort;

    CrashReportParams();

    void WriteToRegistry();
    void ReadFromRegistry();
    void ReadFromEnvironment();
};

bool SendCrashReport( HWND hwndParent, const CrashReportParams &rParams );
BOOL WriteCommentFile( LPCTSTR lpComment );

//***************************************************************************

LONG RegReadValue( HKEY hBaseKey, LPCTSTR lpSubKey, LPCTSTR lpValueName, LPVOID lpData, DWORD cbData )
{
    HKEY    hKey = NULL;
    LONG    lResult;

    lResult = RegOpenKeyEx( hBaseKey, lpSubKey, 0, KEY_QUERY_VALUE, &hKey );

    if ( ERROR_SUCCESS == lResult )
    {
        lResult = RegQueryValueEx( hKey, lpValueName, NULL, NULL, (LPBYTE)lpData, &cbData );
        RegCloseKey( hKey );
    }

    return lResult;
}

//***************************************************************************

LONG RegWriteValue( HKEY hBaseKey, LPCTSTR lpSubKey, LPCTSTR lpValueName, DWORD dwType, LPCVOID lpData, DWORD cbData )
{
    HKEY    hKey = NULL;
    LONG    lResult;

    lResult = RegCreateKeyEx( hBaseKey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL );

    if ( ERROR_SUCCESS == lResult )
    {
        lResult = RegSetValueEx( hKey, lpValueName, NULL, dwType, (CONST BYTE *)lpData, cbData );
        RegCloseKey( hKey );
    }

    return lResult;
}

//***************************************************************************

CrashReportParams::CrashReportParams()
{
    fAllowContact = FALSE;
    sTitle = TEXT("");
    sComment = TEXT("");
    sEmail = TEXT("");
    uInternetConnection = 0;
    sProxyServer = TEXT("");
    sProxyPort = TEXT("");
}

//***************************************************************************

void CrashReportParams::ReadFromRegistry()
{
    TCHAR   szBuffer[2048];

    if ( ERROR_SUCCESS == RegReadValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPProxyServer"),
        szBuffer,
        sizeof(szBuffer) ) )
        sProxyServer = szBuffer;

    DWORD   dwProxyPort;

    if ( ERROR_SUCCESS == RegReadValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPProxyPort"),
        &dwProxyPort,
        sizeof(dwProxyPort) ) )
    {
        _stprintf( szBuffer, TEXT("%d"), dwProxyPort );
        sProxyPort = szBuffer;
    }

    if ( ERROR_SUCCESS == RegReadValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("ReturnAddress"),
        szBuffer,
        sizeof(szBuffer) ) )
        sEmail = szBuffer;

    RegReadValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("AllowContact"),
        &fAllowContact,
        sizeof(fAllowContact) );

    RegReadValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPConnection"),
        &uInternetConnection,
        sizeof(uInternetConnection) );
}

//***************************************************************************

void CrashReportParams::WriteToRegistry()
{
    RegWriteValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPProxyServer"), REG_SZ,
        sProxyServer.c_str(),
        sizeof(TCHAR) * (sProxyServer.length() + 1) );

    LPTSTR endptr = NULL;
    DWORD dwProxyPort = _tcstoul( sProxyPort.c_str(), &endptr, 10 );

    RegWriteValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPProxyPort"), REG_DWORD,
        &dwProxyPort,
        sizeof(DWORD) );

    RegWriteValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("AllowContact"), REG_DWORD,
        &fAllowContact,
        sizeof(DWORD) );


    RegWriteValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("HTTPConnection"), REG_DWORD,
        &uInternetConnection,
        sizeof(DWORD) );

    RegWriteValue(
        HKEY_CURRENT_USER,
        TEXT("SOFTWARE\\OpenOffice.org\\CrashReport"),
        TEXT("ReturnAddress"), REG_SZ,
        sEmail.c_str(),
        sizeof(TCHAR) * (sEmail.length() + 1) );
}

//***************************************************************************

void CrashReportParams::ReadFromEnvironment()
{
    TCHAR   szBuffer[2048];

    DWORD dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_HTTPPROXYSERVER"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
        sProxyServer = szBuffer;

    dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_HTTPPROXYPORT"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
        sProxyPort = szBuffer;

    dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_RETURNADDRESS"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
    {
        sEmail = szBuffer;
        // fAllowContact = TRUE;
    }

    dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_HTTPCONNECTIONTYPE"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
    {
        if ( 0 == _tcsicmp( szBuffer, _T("DIRECT") ) )
            uInternetConnection = 1;
        else if ( 0 == _tcsicmp( szBuffer, _T("MANUALPROXY") ) )
            uInternetConnection = 2;
        else if ( 0 == _tcsicmp( szBuffer, _T("SYSTEMDEFAULT") ) )
            uInternetConnection = 0;
    }

    dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_SUBJECT"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
        sTitle = szBuffer;


    dwResult = GetEnvironmentVariable( TEXT("ERRORREPORT_BODYFILE"), szBuffer, elementsof(szBuffer) );

    if ( dwResult && dwResult < elementsof(szBuffer) )
    {
        FILE *fp = _xfopen( szBuffer, _T("rb") );

        if ( fp )
        {
            CHAR    aUTF8Buffer[256];
            size_t  nBytesRead;

            sComment = TEXT("");

            while ( 0 != (nBytesRead = fread( aUTF8Buffer, sizeof(aUTF8Buffer[0]), elementsof(aUTF8Buffer), fp )) )
            {
                TCHAR   aBuffer[256+1];

                DWORD   dwCharacters = MultiByteToWideChar( CP_UTF8, 0, aUTF8Buffer, nBytesRead, aBuffer, elementsof(aBuffer) - 1 );
                aBuffer[dwCharacters] = 0;
                sComment += aBuffer;
            }

            fclose( fp );
        }
    }
}

//***************************************************************************

typedef BOOL (WINAPI *MiniDumpWriteDump_PROC)(
    IN HANDLE hProcess,
    IN DWORD ProcessId,
    IN HANDLE hFile,
    IN MINIDUMP_TYPE DumpType,
    IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
    IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
    IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
    );

//***************************************************************************

static BOOL WINAPI InitRichEdit()
{
    return (NULL != LoadLibrary( RICHEDIT ));
}

//***************************************************************************

static BOOL WINAPI DeinitRichEdit()
{
    return FreeLibrary( GetModuleHandle( RICHEDIT ) );
}

//***************************************************************************

static string trim_string( const string& rString )
{
    string temp = rString;

    while ( temp.length() && temp[0] == ' ' || temp[0] == '\t' )
        temp.erase( 0, 1 );

    string::size_type   len = temp.length();

    while ( len && temp[len-1] == ' ' || temp[len-1] == '\t' )
    {
        temp.erase( len - 1, 1 );
        len = temp.length();
    }

    return temp;
}

//***************************************************************************

static int LoadAndFormatString( HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax )
{
    TCHAR   szBuffer[FORMATBUFSIZE];
    TCHAR   szBuffer2[FORMATBUFSIZE];

    LoadString( hInstance, uID, szBuffer, elementsof(szBuffer) );

    LPCTSTR src;
    LPTSTR  dest;
    for ( dest = szBuffer2, src = szBuffer; *src; src++, dest++ )
    {
        switch ( *src )
        {
        case '~':
            *dest = '&';
            break;
        case '\\':
            switch ( *(++src) )
            {
            case 'n':
                *dest = '\n';
                break;
            case 'r':
                *dest = '\r';
                break;
            default:
                *dest = *src;
                break;
            }
            break;
        default:
            *dest = *src;
            break;
        }
    }

    *dest = *src;

    return ExpandEnvironmentStrings( szBuffer2, lpBuffer, nBufferMax );
}


//***************************************************************************

static string wstring2utf8( const wstring &rString )
{
    int nBufSize = WideCharToMultiByte( CP_UTF8, 0, rString.c_str(), -1, NULL, 0, NULL, FALSE );

    LPSTR   pBuffer = (LPSTR)alloca( nBufSize );

    WideCharToMultiByte(  CP_UTF8, 0, rString.c_str(), -1, pBuffer, nBufSize, NULL, FALSE );

    return string( pBuffer );
}

//***************************************************************************

static string xml_encode( const string &rString )
{
    string temp = rString;
    string::size_type pos = 0;

    // First replace all occurences of '&' because it may occur in further
    // encoded chardters too

    for( pos = 0; (pos = temp.find( '&', pos )) != string::npos; pos += 4 )
        temp.replace( pos, 1, "&amp;" );

    for( pos = 0; (pos = temp.find( '<', pos )) != string::npos; pos += 4 )
        temp.replace( pos, 1, "&lt;" );

    for( pos = 0; (pos = temp.find( '>', pos )) != string::npos; pos += 4 )
        temp.replace( pos, 1, "&gt;" );

    return temp;
}

//***************************************************************************

static size_t fcopy( FILE *fpin, FILE *fpout )
{
    char buffer[1024];
    size_t nBytes;
    size_t nBytesWritten = 0;

    if ( fpin && fpout )
    {
        while ( 0 != (nBytes = fread( buffer, 1, sizeof(buffer), fpin )) )
        {
            nBytesWritten += fwrite( buffer, 1, nBytes, fpout );
        }
    }

    return nBytesWritten;
}

//***************************************************************************

static string GetModuleDirectory( HMODULE hModule )
{
    TCHAR   szModuleName[MAX_PATH] = TEXT("");
    TCHAR   szDrive[_MAX_DRIVE];
    TCHAR   szDir[_MAX_DIR];
    TCHAR   szFName[_MAX_FNAME];
    TCHAR   szExt[_MAX_EXT];

    if ( GetModuleFileName( hModule, szModuleName, MAX_PATH ) )
    {
        _tsplitpath( szModuleName, szDrive, szDir, szFName, szExt );
        _tmakepath( szModuleName, szDrive, szDir, _T(""), _T("") );
    }

    CHAR    szModuleNameUTF8[MAX_PATH] = "";

    WideCharToMultiByte( CP_UTF8, 0, szModuleName, -1, szModuleNameUTF8, elementsof(szModuleNameUTF8), NULL, NULL );
    return string( szModuleNameUTF8 );
}

//***************************************************************************

string GetFileDirectory( const string& rFilePath )
{
    string aDir = rFilePath;
    size_t pos = aDir.rfind( '\\' );

    if ( string::npos != pos )
        aDir.erase( pos + 1 );
    else
        aDir = "";

    return aDir;
}

//***************************************************************************

string GetFileName( const string& rFilePath )
{
    string aName = rFilePath;
    size_t pos = aName.rfind( '\\' );

    if ( string::npos != pos )
        return aName.substr( pos + 1 );
    else
        return aName;
}

//***************************************************************************

BOOL WriteReportFile( CrashReportParams *pParams )
{
    BOOL    fSuccess = FALSE;
    TCHAR   szTempPath[MAX_PATH];

    if ( GetTempPath( elementsof(szTempPath), szTempPath ) )
    {
        TCHAR   szFileName[MAX_PATH];

        if ( GetTempFileName( szTempPath, TEXT("RPM"), 0, szFileName ) )
        {
            HANDLE  hFile =  CreateFile( szFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

            if ( hFile )
            {
                int fd = _open_osfhandle( (LONG)hFile, _O_TEXT );
                FILE    *fp = _fdopen( fd, "w+t" );
                CHAR    szTitle[1024] = "";
                CHAR    szBuildId[1024] = "";
                CHAR    szEmail[1024] = "";
                const char *pszUserType = getenv( "STAROFFICE_USERTYPE" );

                WideCharToMultiByte( CP_UTF8, 0, pParams->sTitle.c_str(), -1, szTitle, sizeof(szTitle), NULL, NULL );
                WideCharToMultiByte( CP_UTF8, 0, g_szBuildId, -1, szBuildId, sizeof(szBuildId), NULL, NULL );
                WideCharToMultiByte( CP_UTF8, 0, pParams->sEmail.c_str(), -1, szEmail, sizeof(szEmail), NULL, NULL );

                fprintf( fp,
                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                    "<!DOCTYPE errormail:errormail PUBLIC \"-//OpenOffice.org//DTD ErrorMail 1.0//EN\" \"errormail.dtd\">\n"
                    "<errormail:errormail xmlns:errormail=\"http://openoffice.org/2002/errormail\" usertype=\"%s\">\n"
                    "<reportmail:mail xmlns:reportmail=\"http://openoffice.org/2002/reportmail\" version=\"1.1\" feedback=\"%s\" email=\"%s\">\n",
                    pszUserType ? pszUserType : "",
                    pParams->fAllowContact ? "true" : "false",
                    pParams->fAllowContact ? xml_encode(szEmail).c_str() : ""
                    );

                fprintf( fp,
                    "<reportmail:title>%s</reportmail:title>\n",
                    xml_encode(szTitle).c_str() );

                fprintf( fp,
                    "<reportmail:attachment name=\"description.txt\" media-type=\"text/plain;charset=UTF-8\" class=\"UserComment\"/>\n"
                    "<reportmail:attachment name=\"user.dmp\" media-type=\"application/octet-stream\" class=\"UserDump\"/>\n"
                    "</reportmail:mail>\n"

                    "<officeinfo:officeinfo xmlns:officeinfo=\"http://openoffice.org/2002/officeinfo\" build=\"%s\" platform=\"%s\" language=\"%s\" procpath=\"%s\" exceptiontype=\"0x%08X\" product=\"%s\"/>\n",
                    szBuildId,
                    _INPATH,
                    xml_encode(g_strDefaultLanguage).c_str(),
                    xml_encode(GetModuleDirectory( NULL )).c_str(),
                    g_dwExceptionCode,
                    xml_encode(wstring2utf8(g_wstrProductKey)).c_str()
                    );

                OSVERSIONINFO   VersionInfo;

                ZeroMemory( &VersionInfo, sizeof(VersionInfo) );
                VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo );

                GetVersionEx( &VersionInfo );

                fprintf( fp,
                    "<systeminfo:systeminfo xmlns:systeminfo=\"http://openoffice.org/2002/systeminfo\">\n"
                    "<systeminfo:System name=\"%s\" version=\"%d.%d\" build=\"%d\" locale=\"0x%08x\"/>\n"
                    ,
                    VER_PLATFORM_WIN32_NT == VersionInfo.dwPlatformId ? "Windows NT" : "Windows",
                    VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion,
                    VersionInfo.dwBuildNumber,
                    GetUserDefaultLangID()

                    );
                fprintf( fp, "<systeminfo:CPU type=\"x86\"/>\n" );
                fprintf( fp, "</systeminfo:systeminfo>\n" );

                fseek( g_fpStackFile, 0, SEEK_SET );
                fcopy( g_fpStackFile, fp );

                fseek( g_fpChecksumFile, 0, SEEK_SET );
                fcopy( g_fpChecksumFile, fp );

                fprintf( fp, "</errormail:errormail>\n" );

                fclose( fp );

                fSuccess = TRUE;

                WideCharToMultiByte( CP_ACP, 0, szFileName, -1, g_szReportFileNameA, MAX_PATH, NULL, NULL );
            }

            if ( !fSuccess )
                DeleteFile( szFileName );
        }
    }

    return fSuccess;
}

//***************************************************************************

static BOOL SaveDumpFile( HWND hwndOwner )
{
    OPENFILENAME    ofn;
    TCHAR   szFileName[MAX_PATH] = TEXT("");

    ZeroMemory( &ofn, sizeof(ofn) );
    ofn.lStructSize = sizeof(ofn);

    ofn.hwndOwner = hwndOwner;
    ofn.lpstrFilter = TEXT("*.dmp\0*.dmp\0*.*\0*.*\0");
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_ENABLESIZING | OFN_LONGNAMES | OFN_OVERWRITEPROMPT;
    ofn.lpstrDefExt = TEXT("dmp");

    if ( GetSaveFileName( &ofn ) )
    {
        return CopyFile( g_szDumpFileName, szFileName, FALSE );
    }


    return FALSE;
}

//***************************************************************************

static BOOL ScreenToClientRect( HWND hwnd, LPRECT lprc )
{
    return ScreenToClient( hwnd, (LPPOINT)&lprc->left ) && ScreenToClient( hwnd, (LPPOINT)&lprc->right );
}

static BOOL SetWindowRect( HWND hwnd, const RECT *lprc, BOOL fRepaint )
{
    return MoveWindow( hwnd, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, fRepaint );
}

#define GM_LOX  0x01
#define GM_HIX  0x02
#define GM_LOY  0x04
#define GM_HIY  0x08

static BOOL SetGrowMode( HWND hwnd, DWORD dwGrowMode )
{
    return SetProp( hwnd, TEXT("GrowMode"), (HANDLE)dwGrowMode );
}

static DWORD GetGrowMode( HWND hwnd )
{
    return (DWORD)GetProp( hwnd, TEXT("GrowMode") );
}

static BOOL GrowWindow( HWND hwnd, LONG dxClient, LONG dyClient, BOOL fRepaint )
{
    DWORD   dwGrowMode = GetGrowMode( hwnd );
    RECT    rc;

    GetWindowRect( hwnd, &rc );

    if ( dwGrowMode & GM_LOX )
        rc.left += dxClient;
    if ( dwGrowMode & GM_HIX )
        rc.right += dxClient;
    if ( dwGrowMode & GM_LOY )
        rc.top += dyClient;
    if ( dwGrowMode & GM_HIY )
        rc.bottom += dyClient;

    ScreenToClientRect( GetParent( hwnd ), &rc );
    SetWindowRect( hwnd, &rc, fRepaint );

    return TRUE;
}

BOOL CALLBACK GrowChildWindows(
  HWND hwnd,      // handle to child window
  LPARAM lParam   // application-defined value
)
{
    LONG    cx = (SHORT)LOWORD( lParam );
    LONG    cy = (SHORT)HIWORD( lParam );

    GrowWindow( hwnd, cx, cy, TRUE );

    return TRUE;
}

/*
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
    HFONT aFont = *((HFONT*) lParam);
    HDC hDC = GetDC( hwndChild );
    SelectObject( hDC, aFont );
    ReleaseDC( hwndChild, hDC );
    return TRUE;
}

void ApplySystemFont( HWND hwndDlg )
{
    NONCLIENTMETRICSA aNonClientMetrics;
    aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
    if ( SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
    {
        HFONT aSysFont = CreateFontIndirectA( &aNonClientMetrics.lfMessageFont );
        EnumChildWindows(hwndDlg, EnumChildProc, (LPARAM) &aSysFont);
    }
}
*/

BOOL CALLBACK PreviewDialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static RECT rcClient;

    switch ( uMsg )
    {
    case WM_SIZE:
        {
        LONG    cx = LOWORD( lParam );
        LONG    cy = HIWORD( lParam );
        LONG    dxClient, dyClient;

        dxClient = cx - rcClient.right;
        dyClient = cy - rcClient.bottom;

        EnumChildWindows( hwndDlg, GrowChildWindows, MAKELONG( (SHORT)dxClient, (SHORT)dyClient) );

        GetClientRect( hwndDlg, &rcClient );
        }
        break;
    case WM_INITDIALOG:
        {
            GetClientRect( hwndDlg, &rcClient );
            SetGrowMode( GetDlgItem(hwndDlg, IDC_EDIT_PREVIEW), GM_HIX | GM_HIY );
            SetGrowMode( GetDlgItem(hwndDlg, IDOK), GM_LOX | GM_HIX | GM_LOY | GM_HIY );

            CrashReportParams *pParams = (CrashReportParams *)lParam;

            TCHAR   szBuffer[256] = TEXT("");
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong( hwndDlg, GWL_HINSTANCE );
            HWND    hwndParent = (HWND)GetWindowLong( hwndDlg, GWL_HWNDPARENT );

            GetWindowText( hwndParent, szBuffer, elementsof(szBuffer) );
            SetWindowText( hwndDlg, szBuffer );

            LoadAndFormatString( hInstance, IDS_OK_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDOK), szBuffer );

            basic_string<TCHAR> aString;

            aString.append( pParams->sTitle );
            aString.append( _T("\r\n\r\n") );
            aString.append( pParams->sComment );
            aString.append( _T("\r\n---------- report ----------\r\n") );

            FILE    *fp = fopen( g_szReportFileNameA, "r" );

            if ( fp )
            {
                char    buf[1024];

                while ( fgets( buf, elementsof(buf), fp ) != NULL )
                {
                    WCHAR   bufW[1024];

                    MultiByteToWideChar( CP_UTF8, 0, buf, -1, bufW, elementsof(bufW) );

                    aString.append( bufW );
                }

                fclose( fp );
            }

            aString.append( _T("\r\n---------- stack ----------\r\n") );

            fp = fopen( g_szDumpFileNameA, "rb" );

            if ( fp )
            {
                unsigned char   buf[16];
                int     count;

                do
                {
                    int i;

                    count = fread( buf, sizeof(buf[0]), sizeof(buf)/sizeof(buf[0]), fp );

                    for ( i = 0; i < count; i++ )
                    {
                        TCHAR   output[16];

                        _sntprintf( output, elementsof(output), _T("%02X\x20"), buf[i] );
                        aString.append( output );
                    }
                    for ( ; i < elementsof(buf); i++ )
                    {
                        aString.append( _T("\x20\x20\x20") );
                    }

                    for ( i = 0; i < count; i++ )
                    {
                        TCHAR   output[2];

                        if ( (int)buf[i] >= 0x20 && (int)buf[i] <= 0x7F )
                            output[0] = (TCHAR)buf[i];
                        else
                            output[0] = '.';
                        output[1] = 0;
                        aString.append( output );
                    }

                    aString.append( _T("\r\n") );

                } while ( count );

                fclose( fp );
            }

            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_PREVIEW), aString.c_str() );


            SetWindowFont( GetDlgItem(hwndDlg, IDC_EDIT_PREVIEW), GetStockObject( SYSTEM_FIXED_FONT ), TRUE );
        }
        return TRUE;
    case WM_COMMAND:
        switch ( LOWORD(wParam) )
        {
        case IDOK:
        case IDCANCEL:
            EndDialog( hwndDlg, wParam );
            return TRUE;
        }
        break;
    default:
        break;
    }

    return FALSE;
}
//***************************************************************************

static void PreviewReport( HWND hwndParent, CrashReportParams *pParams )
{
    HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndParent, GWL_HINSTANCE );

    WriteReportFile( pParams );

    DialogBoxParam(
        hInstance,
        MAKEINTRESOURCE(IDD_PREVIEW_FRAME),
        hwndParent,
        PreviewDialogProc,
        (LPARAM)pParams
        );

    DeleteFileA( g_szReportFileNameA );
}
//***************************************************************************
void UpdateOptionsDialogControls( HWND hwndDlg )
{
    if ( Button_GetCheck( GetDlgItem(hwndDlg, IDC_RADIO_MANUAL) ) & BST_CHECKED )
    {
        EnableWindow( GetDlgItem(hwndDlg, IDC_EDIT_PROXYSERVER), TRUE );
        EnableWindow( GetDlgItem(hwndDlg, IDC_EDIT_PROXYPORT), TRUE );
    }
    else
    {
        EnableWindow( GetDlgItem(hwndDlg, IDC_EDIT_PROXYSERVER), FALSE );
        EnableWindow( GetDlgItem(hwndDlg, IDC_EDIT_PROXYPORT), FALSE );
    }
}

//***************************************************************************

BOOL CALLBACK OptionsDialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static CrashReportParams *pParams;

    switch ( uMsg )
    {
    case WM_INITDIALOG:
        {
            TCHAR   szBuffer[1024] = TEXT("");
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong( hwndDlg, GWL_HINSTANCE );
            //HWND  hwndParent = (HWND)GetWindowLong( hwndDlg, GWL_HWNDPARENT );

            pParams = (CrashReportParams *)lParam;

            LoadAndFormatString( hInstance, IDS_OPTIONS_CAPTION, szBuffer, elementsof(szBuffer) );
            SetWindowText( hwndDlg, szBuffer );

            LoadAndFormatString( hInstance, IDS_PROXY_SETTINGS_HEADER, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_PROXY_SETTINGS), szBuffer );

            LoadAndFormatString( hInstance, IDS_PROXY_SYSTEM, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_RADIO_SYSTEM), szBuffer );

            LoadAndFormatString( hInstance, IDS_PROXY_DIRECT, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_RADIO_DIRECT), szBuffer );

            LoadAndFormatString( hInstance, IDS_PROXY_MANUAL, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_RADIO_MANUAL), szBuffer );

            LoadAndFormatString( hInstance, IDS_LABEL_PROXYSERVER, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_LABEL_PROXYSERVER), szBuffer );

            LoadAndFormatString( hInstance, IDS_LABEL_PROXYPORT, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_LABEL_PROXYPORT), szBuffer );

            LoadAndFormatString( hInstance, IDS_OK_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDOK), szBuffer );

            LoadAndFormatString( hInstance, IDS_CANCEL_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDCANCEL), szBuffer );

            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_PROXYSERVER), pParams->sProxyServer.c_str() );
            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_PROXYPORT), pParams->sProxyPort.c_str() );

            Button_SetCheck( GetDlgItem(hwndDlg, IDC_RADIO_SYSTEM + pParams->uInternetConnection), BST_CHECKED );

            SendMessage(
                GetDlgItem(hwndDlg, IDC_PROXY_DESCRIPTION),
                EM_SETBKGNDCOLOR,
                (WPARAM)FALSE,
                GetSysColor( COLOR_3DFACE ) );
            LoadAndFormatString( hInstance, IDS_PROXY_DESCRIPTION, szBuffer, elementsof(szBuffer) );
            Edit_SetText( GetDlgItem(hwndDlg, IDC_PROXY_DESCRIPTION), szBuffer );

            UpdateOptionsDialogControls( hwndDlg );
        }
        return TRUE;
    case WM_COMMAND:
        switch ( LOWORD(wParam) )
        {
        case IDC_RADIO_SYSTEM:
        case IDC_RADIO_DIRECT:
        case IDC_RADIO_MANUAL:
            if ( BN_CLICKED == HIWORD(wParam) )
                UpdateOptionsDialogControls( hwndDlg );
            break;
        case IDOK:
            {
            TCHAR szBuffer[1024];

            Edit_GetText( GetDlgItem(hwndDlg, IDC_EDIT_PROXYSERVER), szBuffer, elementsof(szBuffer) );
            pParams->sProxyServer = szBuffer;

            Edit_GetText( GetDlgItem(hwndDlg, IDC_EDIT_PROXYPORT), szBuffer, elementsof(szBuffer) );
            pParams->sProxyPort = szBuffer;

            if ( Button_GetCheck( GetDlgItem(hwndDlg, IDC_RADIO_DIRECT) ) & BST_CHECKED )
                pParams->uInternetConnection = 1;
            else if ( Button_GetCheck( GetDlgItem(hwndDlg, IDC_RADIO_MANUAL) ) & BST_CHECKED )
                pParams->uInternetConnection = 2;
            else
                pParams->uInternetConnection = 0;
            }
        case IDCANCEL:
            EndDialog( hwndDlg, wParam );
            return TRUE;
        }
        break;
    default:
        break;
    }

    return FALSE;
}

//***************************************************************************

static void OptionsDialog( HWND hwndParent, CrashReportParams *pParams )
{
    HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndParent, GWL_HINSTANCE );

    if ( IDOK == DialogBoxParam(
        hInstance,
        MAKEINTRESOURCE(IDD_OPTIONS_FRAME),
        hwndParent,
        OptionsDialogProc,
        (LPARAM)pParams
        ) )
        pParams->WriteToRegistry();

}
//***************************************************************************

void UpdateReportDialogControls( HWND hwndDlg )
{
    EnableWindow(
        GetDlgItem(hwndDlg, IDC_EDIT_EMAIL),
        Button_GetCheck(GetDlgItem(hwndDlg, IDC_ALLOW_CONTACT)) & BST_CHECKED ? TRUE : FALSE );
    EnableWindow(
        GetDlgItem(hwndDlg, IDC_LABEL_EMAIL),
        Button_GetCheck(GetDlgItem(hwndDlg, IDC_ALLOW_CONTACT)) & BST_CHECKED ? TRUE : FALSE );
}

//***************************************************************************

BOOL CALLBACK ReportDialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM
    )
{
    switch ( uMsg )
    {
    case WM_INITDIALOG:
        {
            CrashReportParams   *pParams = (CrashReportParams*)GetWindowLong( GetParent(hwndDlg), GWL_USERDATA );
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE );
            TCHAR       szBuffer[FORMATBUFSIZE];

            LoadAndFormatString( hInstance, IDS_REPORT_INTRO, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_REPORT_INTRO), szBuffer );

            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT3), szBuffer );

            LoadAndFormatString( hInstance, IDS_ENTER_TITLE, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_ENTER_TITLE), szBuffer );

            LoadAndFormatString( hInstance, IDS_ENTER_DESCRIPTION, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_ENTER_DESCRIPTION), szBuffer );

            LoadAndFormatString( hInstance, IDS_SHOW_REPORT_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_SHOW_REPORT), szBuffer );

            LoadAndFormatString( hInstance, IDS_SAVE_REPORT_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_SAVE_REPORT), szBuffer );

            const char *pszUserType = getenv( "STAROFFICE_USERTYPE" );
            if ( pszUserType )
                ShowWindow( GetDlgItem(hwndDlg, IDC_SAVE_REPORT), SW_SHOW );
            else
                ShowWindow( GetDlgItem(hwndDlg, IDC_SAVE_REPORT), SW_HIDE );

            LoadAndFormatString( hInstance, IDS_OPTIONS_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_OPTIONS), szBuffer );

            LoadAndFormatString( hInstance, IDS_ALLOW_CONTACT, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_ALLOW_CONTACT), szBuffer );
            Button_SetCheck( GetDlgItem(hwndDlg, IDC_ALLOW_CONTACT), pParams->fAllowContact ? BST_CHECKED : BST_UNCHECKED );

            LoadAndFormatString( hInstance, IDS_LABEL_EMAIL, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDC_LABEL_EMAIL), szBuffer );

            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_EMAIL), pParams->sEmail.c_str() );

            UpdateReportDialogControls( hwndDlg );
        }
        return TRUE;
    case WM_SHOWWINDOW:
        if ( (BOOL)wParam )
        {
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE );
            CrashReportParams   *pParams = (CrashReportParams*)GetWindowLong( GetParent(hwndDlg), GWL_USERDATA );
            TCHAR       szBuffer[FORMATBUFSIZE];

            LoadAndFormatString( hInstance, IDS_REPORT_CAPTION, szBuffer, elementsof(szBuffer) );
            SetWindowText( GetParent(hwndDlg), szBuffer );

            LoadAndFormatString( hInstance, IDS_REPORT_HEADER, szBuffer, elementsof(szBuffer) );
            SetWindowText( GetDlgItem(GetParent(hwndDlg), IDC_HEADER), szBuffer );

            LoadAndFormatString( hInstance, IDS_DONOT_SEND_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(GetParent(hwndDlg), IDCANCEL), szBuffer );


            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDBACK), TRUE );
            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDFINISH), TRUE );
            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDNEXT), FALSE );

            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_TITLE), pParams->sTitle.c_str() );
            Edit_SetText( GetDlgItem(hwndDlg, IDC_EDIT_DESCRIPTION), pParams->sComment.c_str() );

            /*
            SetWindowLong( GetDlgItem(GetParent(hwndDlg),IDFINISH), GWL_STYLE,
                GetWindowLong( GetDlgItem(GetParent(hwndDlg),IDFINISH), GWL_STYLE) | BS_DEFPUSHBUTTON );
            SetWindowLong( GetDlgItem(GetParent(hwndDlg),IDBACK), GWL_STYLE,
                GetWindowLong( GetDlgItem(GetParent(hwndDlg),IDBACK), GWL_STYLE) &~ BS_DEFPUSHBUTTON );
                */
            SetFocus( GetDlgItem(hwndDlg,IDC_EDIT_TITLE) );
        }
        break;
    case WM_COMMAND:
        switch ( LOWORD(wParam) )
        {
        case IDC_SHOW_REPORT:
            {
                TCHAR   szBuffer[32767];

                CrashReportParams   *pParams = (CrashReportParams*)GetWindowLong( GetParent(hwndDlg), GWL_USERDATA );

                pParams->fAllowContact = Button_GetCheck( GetDlgItem(hwndDlg, IDC_ALLOW_CONTACT) ) ? TRUE : FALSE;

                Edit_GetText( GetDlgItem(hwndDlg, IDC_EDIT_TITLE), szBuffer, elementsof(szBuffer) );
                pParams->sTitle = szBuffer;

                Edit_GetText( GetDlgItem(hwndDlg, IDC_EDIT_DESCRIPTION), szBuffer, elementsof(szBuffer) );
                pParams->sComment = szBuffer;

                Edit_GetText( GetDlgItem(hwndDlg, IDC_EDIT_EMAIL), szBuffer, elementsof(szBuffer) );
                pParams->sEmail = szBuffer;

                PreviewReport( GetParent(hwndDlg), pParams );
            }
            return TRUE;
        case IDC_SAVE_REPORT:
            SaveDumpFile( GetParent(hwndDlg) );
            return TRUE;
        case IDC_OPTIONS:
            {
                CrashReportParams   *pParams = (CrashReportParams*)GetWindowLong( GetParent(hwndDlg), GWL_USERDATA );
                OptionsDialog( GetParent(hwndDlg), pParams );
            }
            return TRUE;
        case IDC_ALLOW_CONTACT:
            if ( BN_CLICKED == HIWORD(wParam) )
                UpdateReportDialogControls( hwndDlg );
            return TRUE;
        }
        break;
    default:
        break;
    }

    return FALSE;
}
//***************************************************************************

BOOL CALLBACK WelcomeDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch ( uMsg )
    {
    case WM_INITDIALOG:
        {
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE );
            HWND    hwndRichEdit = GetDlgItem(hwndDlg, IDC_RICHEDIT21);
            TCHAR   szBuffer[FORMATBUFSIZE];
            TCHAR   szBuffer2[FORMATBUFSIZE];
            TCHAR   szURL[256];
            TCHAR   szCaption[256];

            SendMessage(
                hwndRichEdit,
                EM_SETBKGNDCOLOR,
                (WPARAM)FALSE,
                GetSysColor( COLOR_3DFACE ) );

            SendMessage( hwndRichEdit, EM_SETEVENTMASK, 0, ENM_LINK );
            SendMessage( hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0 );

            LoadAndFormatString( hInstance, IDS_WELCOME_BODY1, szBuffer, elementsof(szBuffer) );
            LoadAndFormatString( hInstance, IDS_WELCOME_BODY2, szBuffer2, elementsof(szBuffer2) );
            _tcsncat( szBuffer, szBuffer2, elementsof(szBuffer) );
            LoadAndFormatString( hInstance, IDS_WELCOME_BODY3, szBuffer2, elementsof(szBuffer2) );
            _tcsncat( szBuffer, szBuffer2, elementsof(szBuffer) );
            LoadString( hInstance, IDS_PRIVACY_URL, szURL, elementsof(szURL) );
            _tcsncat( szBuffer, szURL, elementsof(szBuffer) );
            SetWindowText( hwndRichEdit, szBuffer );

            LoadAndFormatString( hInstance, IDS_WELCOME_CAPTION, szCaption, elementsof(szCaption) );
            SetWindowText( GetParent(hwndDlg), szCaption );

        }
        return TRUE;
    case WM_SHOWWINDOW:
        if ( (BOOL)wParam )
        {
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE );
            TCHAR       szBuffer[FORMATBUFSIZE];

            LoadAndFormatString( hInstance, IDS_WELCOME_CAPTION, szBuffer, elementsof(szBuffer) );
            SetWindowText( GetParent(hwndDlg), szBuffer );

            LoadAndFormatString( hInstance, IDS_WELCOME_HEADER, szBuffer, elementsof(szBuffer) );
            SetWindowText( GetDlgItem(GetParent(hwndDlg), IDC_HEADER), szBuffer );

            LoadAndFormatString( hInstance, IDS_CANCEL_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(GetParent(hwndDlg), IDCANCEL), szBuffer );

            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDBACK), FALSE );
            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDFINISH), FALSE );
            ShowWindow( GetDlgItem(GetParent(hwndDlg),IDNEXT), TRUE );

            SetFocus( GetDlgItem(GetParent(hwndDlg),IDNEXT) );
        }
        break;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR)lParam;

            if ( pnmh->idFrom == IDC_RICHEDIT21 && pnmh->code == EN_LINK )
            {
                ENLINK  *plink = (ENLINK*)lParam;

                if ( plink->msg == WM_LBUTTONUP )
                {
                    TCHAR   szBuffer[256];
                    TEXTRANGE   range;

                    range.chrg = plink->chrg;
                    range.lpstrText = szBuffer;

                    SendMessage( pnmh->hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&range );

                    ShellExecute( hwndDlg, NULL, szBuffer, NULL, NULL, SW_SHOWDEFAULT );
                }

            }
        }
        break;
    default:
        break;
    }

    return FALSE;
}
//***************************************************************************

BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    static  HWND    hwndPages[2] = { NULL };
    static  int     iActualPage = 0;

    switch ( uMsg )
    {
    case WM_INITDIALOG:
        {
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE );
            TCHAR       szBuffer[FORMATBUFSIZE];

            SetWindowLong( hwndDlg, GWL_USERDATA, (LONG)lParam );
            hwndPages[0] = CreateDialog(
                hInstance,
                MAKEINTRESOURCE(IDD_WELCOME_PAGE),
                hwndDlg,
                WelcomeDialogProc );

            hwndPages[1] = CreateDialog(
                hInstance,
                MAKEINTRESOURCE(IDD_REPORT_PAGE),
                hwndDlg,
                ReportDialogProc );

            CHARFORMAT  chfmt;

            chfmt.cbSize = sizeof(chfmt);
            chfmt.dwMask = CFM_BOLD;
            chfmt.dwEffects = CFE_BOLD;

            SendMessage(
                GetDlgItem(hwndDlg, IDC_HEADER),
                EM_SETCHARFORMAT,
                SCF_ALL,
                (LPARAM)&chfmt );

            LoadAndFormatString( hInstance, IDS_CANCEL_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDCANCEL), szBuffer );

            LoadAndFormatString( hInstance, IDS_NEXT_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDNEXT), szBuffer );

            LoadAndFormatString( hInstance, IDS_SEND_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDFINISH), szBuffer );

            LoadAndFormatString( hInstance, IDS_BACK_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDBACK), szBuffer );

            ShowWindow( hwndPages[1], SW_HIDE );
            ShowWindow( hwndPages[0], SW_SHOW );

            // Let Crash Reporter window stay on top of all other windows
            SetWindowPos( hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
        }
        return FALSE;
    case WM_CTLCOLORSTATIC:
        return (BOOL)CreateSolidBrush(GetSysColor(COLOR_WINDOW));
    case WM_COMMAND:
        switch ( LOWORD(wParam) )
        {
        case IDBACK:
            if ( iActualPage > 0 )
            {
                ShowWindow( hwndPages[iActualPage], SW_HIDE );
                ShowWindow( hwndPages[--iActualPage], SW_SHOW );
            }
            return TRUE;
        case IDNEXT:
            if ( iActualPage < elementsof(hwndPages) - 1 )
            {
                ShowWindow( hwndPages[iActualPage], SW_HIDE );
                ShowWindow( hwndPages[++iActualPage], SW_SHOW );
            }
            return TRUE;
        case IDFINISH:
            {
                TCHAR   szBuffer[32767];
                CrashReportParams   *pParams = (CrashReportParams*)GetWindowLong( hwndDlg, GWL_USERDATA );

                pParams->fAllowContact = Button_GetCheck( GetDlgItem(hwndPages[1], IDC_ALLOW_CONTACT) ) ? TRUE : FALSE;

                Edit_GetText( GetDlgItem(hwndPages[1], IDC_EDIT_TITLE), szBuffer, elementsof(szBuffer) );
                pParams->sTitle = szBuffer;

                Edit_GetText( GetDlgItem(hwndPages[1], IDC_EDIT_DESCRIPTION), szBuffer, elementsof(szBuffer) );
                pParams->sComment = szBuffer;

                Edit_GetText( GetDlgItem(hwndPages[1], IDC_EDIT_EMAIL), szBuffer, elementsof(szBuffer) );
                pParams->sEmail = szBuffer;

                if ( pParams->fAllowContact && !pParams->sEmail.length() )
                {
                    TCHAR   szMessage[MAX_TEXT_BUFFER];

                    LoadAndFormatString( GetModuleHandle(NULL), IDS_ERROR_MSG_NOEMAILADDRESS, szMessage, elementsof(szMessage) );

                    MessageBox( hwndDlg, szMessage, NULL, MB_ICONERROR | MB_OK );
                    break;  // Don't end the dialog
                }
                else
                {
                    pParams->WriteToRegistry();

                    WriteCommentFile( pParams->sComment.c_str() );
                    WriteReportFile( pParams );

                    if ( !SendCrashReport( hwndDlg, *pParams ) )
                        break; // Don't end the dialog
                }
            }
            // Fallthrough !!!
        case IDCANCEL:
            EndDialog( hwndDlg, wParam );
            return TRUE;
        }
        break;
    default:
        break;
    }

    return FALSE;
}



//*****************************************************************************
//* Generate MD5 checksum
//*****************************************************************************

#define MAGIC_DESCRIPTION_FILLER    'x'
#define MAGIC_DESCRIPTION_COUNT     80

static void repatch_soffice_exe( void *pBuffer, size_t nBufSize )
{
    wchar_t DescriptionBuffer[MAGIC_DESCRIPTION_COUNT];

    memset( DescriptionBuffer, 0, sizeof(DescriptionBuffer) );
    wcsncpy( DescriptionBuffer, g_wstrProductKey.c_str(), elementsof(DescriptionBuffer) - 1 );

    bool bPatched = false;

    do
    {
        void *pFound = memchr( pBuffer, ((char *)DescriptionBuffer)[0], nBufSize );

        if ( pFound )
        {
            size_t distance = (char *)pFound - (char *)pBuffer;

            if ( nBufSize >= distance )
            {
                nBufSize -= distance;

                if ( nBufSize >= sizeof(DescriptionBuffer) &&
                    0 == memcmp( pFound, DescriptionBuffer, sizeof(DescriptionBuffer) ) )
                {
                    for ( int i = 0; i < 80; i++ )
                    {
                        ((wchar_t *)pFound)[i] = MAGIC_DESCRIPTION_FILLER;
                    }
                    bPatched = true;
                }
                else
                {
                    pBuffer = (void *)(((char *)pFound) + 1);
                    nBufSize--;
                }
            }
            else
                nBufSize = 0;
        }
        else
            nBufSize = 0;
    } while ( !bPatched && nBufSize );
}

// Normalize executable/library images to prevent different MD5 checksums due
// to a different PE header date/checksum (this doesn't affect the code/data
// sections of a executable/library. Please see tools/source/bootstrp/md5.cxx
// where the same method is also used. The tool so_checksum creates the MD5
// checksums during build time. You have to make sure that both methods use the
// same algorithm otherwise there could be problems with stack reports.
static void normalize_pe_image(sal_uInt8* buffer, size_t nBufferSize)
{
    const int OFFSET_PE_OFFSET                  = 0x3c;
    const int OFFSET_COFF_TIMEDATESTAMP         = 4;
    const int PE_SIGNATURE_SIZE                 = 4;
    const int COFFHEADER_SIZE                   = 20;
    const int OFFSET_PE_OPTIONALHEADER_CHECKSUM = 64;

    // Check the header part of the file buffer
    if (buffer[0] == 'M' && buffer[1] == 'Z')
    {
        unsigned long PEHeaderOffset = (long)buffer[OFFSET_PE_OFFSET];
        if (PEHeaderOffset < nBufferSize-4)
        {
            if ( buffer[PEHeaderOffset] == 'P' &&
                 buffer[PEHeaderOffset+1] == 'E' &&
                 buffer[PEHeaderOffset+2] == 0 &&
                 buffer[PEHeaderOffset+3] == 0 )
            {
                PEHeaderOffset += PE_SIGNATURE_SIZE;
                if (PEHeaderOffset+OFFSET_COFF_TIMEDATESTAMP < nBufferSize-4)
                {
                    // Set timedatestamp and checksum fields to a normalized
                    // value to enforce the same MD5 checksum for identical
                    // Windows  executables/libraries.
                    buffer[PEHeaderOffset+OFFSET_COFF_TIMEDATESTAMP] = 0;
                    buffer[PEHeaderOffset+OFFSET_COFF_TIMEDATESTAMP+1] = 0;
                    buffer[PEHeaderOffset+OFFSET_COFF_TIMEDATESTAMP+2] = 0;
                    buffer[PEHeaderOffset+OFFSET_COFF_TIMEDATESTAMP+3] = 0;
                }

                if (PEHeaderOffset+COFFHEADER_SIZE+OFFSET_PE_OPTIONALHEADER_CHECKSUM < nBufferSize-4)
                {
                    // Set checksum to a normalized value
                    buffer[PEHeaderOffset+COFFHEADER_SIZE+OFFSET_PE_OPTIONALHEADER_CHECKSUM] = 0;
                    buffer[PEHeaderOffset+COFFHEADER_SIZE+OFFSET_PE_OPTIONALHEADER_CHECKSUM+1] = 0;
                    buffer[PEHeaderOffset+COFFHEADER_SIZE+OFFSET_PE_OPTIONALHEADER_CHECKSUM+2] = 0;
                    buffer[PEHeaderOffset+COFFHEADER_SIZE+OFFSET_PE_OPTIONALHEADER_CHECKSUM+3] = 0;
                }
            }
        }
    }
}

static sal_uInt32 calc_md5_checksum(  const char *filename, sal_uInt8 *pChecksum, sal_uInt32 nChecksumLen )
{
    const int MINIMAL_FILESIZE = 512;

    sal_uInt32  nBytesProcessed = 0;

    FILE *fp = fopen( filename, "rb" );

    if ( fp )
    {
        long    nFileSize;

        if ( 0 == fseek( fp, 0, SEEK_END ) && -1 != (nFileSize = ftell(fp)) )
        {
            rewind( fp );

            sal_uInt8 *pBuffer = new sal_uInt8[nFileSize];
            size_t nBytesRead = fread( pBuffer, 1, nFileSize, fp );

            if ( sal::static_int_cast<long>(nBytesRead) == nFileSize )
            {
                if ( 0 == stricmp( GetFileName(filename).c_str(), "soffice.bin" ) )
                    repatch_soffice_exe( pBuffer, nBytesRead );
                else if ( nFileSize > MINIMAL_FILESIZE )
                    normalize_pe_image( pBuffer, nBytesRead );

                rtlDigestError error = rtl_digest_MD5 (
                    pBuffer,   nBytesRead,
                    pChecksum, nChecksumLen );

                if ( rtl_Digest_E_None == error )
                    nBytesProcessed = nBytesRead;
            }

            delete[] pBuffer;
        }

        fclose( fp );

    }

    return nBytesProcessed;
}

#if 0
static sal_uInt32 calc_md5_checksum( const char *filename, sal_uInt8 *pChecksum, sal_uInt32 nChecksumLen )
{
    sal_uInt32  nBytesProcessed = 0;

    FILE *fp = fopen( filename, "rb" );

    if ( fp )
    {
        rtlDigest digest = rtl_digest_createMD5();

        if ( digest )
        {
            size_t          nBytesRead;
            sal_uInt8       buffer[4096];
            rtlDigestError  error = rtl_Digest_E_None;

            while ( rtl_Digest_E_None == error &&
                0 != (nBytesRead = fread( buffer, 1, sizeof(buffer), fp )) )
            {
                error = rtl_digest_updateMD5( digest, buffer, nBytesRead );
                nBytesProcessed += nBytesRead;
            }

            if ( rtl_Digest_E_None == error )
            {
                error = rtl_digest_getMD5( digest, pChecksum, nChecksumLen );
            }

            if ( rtl_Digest_E_None != error )
                nBytesProcessed = 0;

            rtl_digest_destroyMD5( digest );
        }

        fclose( fp );
    }

    return nBytesProcessed;
}

#endif
//***************************************************************************

static bool WriteStackFile( FILE *fout, hash_map< string, string >& rLibraries, DWORD dwProcessId, PEXCEPTION_POINTERS pExceptionPointers )
{
    bool    fSuccess = false;

    if ( fout && dwProcessId && pExceptionPointers )
    {
        HANDLE  hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId );

        if ( IsValidHandle(hProcess) )
        {
            EXCEPTION_POINTERS  aExceptionPointers;
            CONTEXT             aContextRecord;

            ReadProcessMemory(
                hProcess,
                pExceptionPointers,
                &aExceptionPointers,
                sizeof(aExceptionPointers),
                NULL );

            ReadProcessMemory(
                hProcess,
                aExceptionPointers.ContextRecord,
                &aContextRecord,
                sizeof(aContextRecord),
                NULL );

            STACKFRAME  frame;

            ZeroMemory( &frame, sizeof(frame) );
            frame.AddrPC.Offset = aContextRecord.Eip;
            frame.AddrPC.Mode = AddrModeFlat;
            frame.AddrFrame.Offset = aContextRecord.Ebp;
            frame.AddrFrame.Mode = AddrModeFlat;

            BOOL bSuccess;
            int frameNum = 0;

            SymInitialize( hProcess, NULL, TRUE );

            fprintf( fout, "<errormail:Stack type=\"Win32\">\n" );

            do
            {
                fSuccess = true;

                bSuccess = StackWalk( IMAGE_FILE_MACHINE_I386,
                    hProcess,
                    NULL,
                    &frame,
                    &aContextRecord,
                    (PREAD_PROCESS_MEMORY_ROUTINE)ReadProcessMemory,
                    SymFunctionTableAccess,
                    SymGetModuleBase,
                    NULL );

                if ( bSuccess )
                {
                    // Note: ImageHelp ANSI functions do not have an A postfix while
                    //       Unicode versions have a W postfix. There's no macro
                    //       that depends on define UNICODE

                    IMAGEHLP_MODULE moduleInfo;

                    ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
                    moduleInfo.SizeOfStruct = sizeof(moduleInfo);

                    if ( SymGetModuleInfo( hProcess, frame.AddrPC.Offset, &moduleInfo ) )
                    {
                        rLibraries[ GetFileName( moduleInfo.LoadedImageName ).c_str() ] = moduleInfo.LoadedImageName;

                        DWORD   dwRelOffset = 0;
                        BYTE    symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 256 ];
                        PIMAGEHLP_SYMBOL    pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;

                        ZeroMemory( symbolBuffer, sizeof(symbolBuffer) );
                        pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
                        pSymbol->MaxNameLength = 256;

                        if ( SymGetSymFromAddr( hProcess, frame.AddrPC.Offset, &dwRelOffset, pSymbol ) )
                            fprintf( fout, "<errormail:StackInfo " \
                                "pos=\"%d\" ip=\"0x%p\" rel=\"0x%p\" ordinal=\"%s+0x%p\" name=\"%s\" path=\"%s\"/>\n",
                                frameNum,
                                frame.AddrPC.Offset,
                                frame.AddrPC.Offset - moduleInfo.BaseOfImage,
                                xml_encode(pSymbol->Name).c_str(),
                                frame.AddrPC.Offset - pSymbol->Address,
                                xml_encode(GetFileName( moduleInfo.LoadedImageName )).c_str(),
                                xml_encode( GetFileDirectory( moduleInfo.LoadedImageName )).c_str()
                                );
                        else
                            fprintf( fout, "<errormail:StackInfo " \
                                "pos=\"%d\" ip=\"0x%p\" rel=\"0x%p\" name=\"%s\" path=\"%s\"/>\n",
                                frameNum,
                                frame.AddrPC.Offset,
                                frame.AddrPC.Offset - moduleInfo.BaseOfImage,
                                xml_encode(GetFileName( moduleInfo.LoadedImageName )).c_str(),
                                xml_encode(GetFileDirectory( moduleInfo.LoadedImageName )).c_str()
                                );
                    }
                    else
                        fprintf( fout, "<errormail:StackInfo pos=\"%d\" ip=\"0x%p\"/>\n",
                            frameNum,
                            frame.AddrPC.Offset
                            );

                    frameNum++;
                }

            } while ( bSuccess );

            fprintf( fout, "</errormail:Stack>\n" );

            SymCleanup( hProcess );

            CloseHandle( hProcess );
        }

    }

    return fSuccess;
}

bool WriteChecksumFile( FILE *fchksum, const hash_map< string, string >& rLibraries )
{
    bool success = false;

    if ( fchksum && rLibraries.size() )
    {
        fprintf( fchksum, "<errormail:Checksums type=\"MD5\">\n" );

        hash_map< string, string >::const_iterator iter;

        for ( iter = rLibraries.begin();
            iter != rLibraries.end();
            iter++ )
        {
            sal_uInt8 checksum[RTL_DIGEST_LENGTH_MD5];
            sal_uInt32 nBytesProcessed = calc_md5_checksum(
                iter->second.c_str(),
                checksum, sizeof(checksum) );

            if ( nBytesProcessed )
            {
                fprintf( fchksum, "<errormail:Checksum sum=\"0x" );
                for ( int i = 0; i < sizeof(checksum); fprintf( fchksum, "%02X", checksum[i++] ) );
                fprintf( fchksum, "\" bytes=\"%d\" file=\"%s\"/>\n",
                    nBytesProcessed,
                    GetFileName( iter->first ) );
            }
        }

        fprintf( fchksum, "</errormail:Checksums>\n" );

        success = true;
    }

    return success;
}

//***************************************************************************

BOOL FindDumpFile()
{
    TCHAR   szFileName[MAX_PATH];

    if ( GetCrashDataPath( szFileName ) )
    {
        _tcscat( szFileName, _T("\\crashdat.dmp") );

        HANDLE  hFile =  CreateFile(
            szFileName,
            GENERIC_READ,
            0, NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, NULL );

        if ( hFile )
        {
            CloseHandle( hFile );

            WideCharToMultiByte( CP_ACP, 0, szFileName, -1, g_szDumpFileNameA, MAX_PATH, NULL, NULL );
            _tcscpy( g_szDumpFileName, szFileName );

            return TRUE;
        }
    }

    return FALSE;
}

BOOL WriteDumpFile( DWORD dwProcessId, PEXCEPTION_POINTERS pExceptionPointers, DWORD dwThreadId )
{
    BOOL    fSuccess = FALSE;
    PMINIDUMP_EXCEPTION_INFORMATION lpExceptionParam = NULL;
    MINIDUMP_EXCEPTION_INFORMATION  ExceptionParam;

    HMODULE hDbgHelp = LoadLibrary( _T("DBGHELP.DLL" ) );
    MiniDumpWriteDump_PROC  pMiniDumpWriteDump = NULL;

    if ( hDbgHelp )
    {
        pMiniDumpWriteDump = (MiniDumpWriteDump_PROC)GetProcAddress( hDbgHelp, "MiniDumpWriteDump" );

        if ( !pMiniDumpWriteDump )
        {
            FreeLibrary( hDbgHelp );
            return false;
        }
    }

    if ( !pMiniDumpWriteDump )
        return false;

    HANDLE  hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId );

    if ( IsValidHandle(hProcess) )
    {
        TCHAR   szTempPath[MAX_PATH];

//      if ( GetTempPath( elementsof(szTempPath), szTempPath ) )
        if ( GetCrashDataPath( szTempPath ) )
        {
            TCHAR   szFileName[MAX_PATH];

//          if ( GetTempFileName( szTempPath, TEXT("DMP"), 0, szFileName ) )
            _tcscpy( szFileName, szTempPath );
            _tcscat( szFileName, _T("\\crashdat.dmp") );
            {
                HANDLE  hFile =  CreateFile(
                    szFileName,
                    GENERIC_READ | GENERIC_WRITE,
                    0, NULL,
//                  OPEN_EXISTING,
                    CREATE_ALWAYS,
                    FILE_ATTRIBUTE_NORMAL, NULL );

                if ( hFile )
                {
                    if ( pExceptionPointers && dwThreadId )
                    {
                        ExceptionParam.ThreadId = dwThreadId;
                        ExceptionParam.ExceptionPointers = pExceptionPointers;
                        ExceptionParam.ClientPointers = TRUE;

                        EXCEPTION_POINTERS  aExceptionPointers;
                        EXCEPTION_RECORD    aExceptionRecord;

                        ReadProcessMemory(
                            hProcess,
                            pExceptionPointers,
                            &aExceptionPointers,
                            sizeof(aExceptionPointers),
                            NULL );


                        ReadProcessMemory(
                            hProcess,
                            aExceptionPointers.ExceptionRecord,
                            &aExceptionRecord,
                            sizeof(aExceptionRecord),
                            NULL );

                        g_dwExceptionCode = aExceptionRecord.ExceptionCode;

                        lpExceptionParam = &ExceptionParam;
                    }

                    fSuccess = pMiniDumpWriteDump( hProcess, dwProcessId, hFile, MiniDumpNormal, lpExceptionParam, NULL, NULL );

                    CloseHandle( hFile );

                    WideCharToMultiByte( CP_ACP, 0, szFileName, -1, g_szDumpFileNameA, MAX_PATH, NULL, NULL );
                    _tcscpy( g_szDumpFileName, szFileName );
                }

                if ( !fSuccess )
                    DeleteFile( szFileName );
            }
        }

        CloseHandle( hProcess );
    }

    FreeLibrary( hDbgHelp );

    return fSuccess;
}

//***************************************************************************

static DWORD FindProcessForImage( LPCTSTR lpImagePath )
{
    DWORD   dwProcessId = 0;
    DWORD   aProcesses[1024];
    DWORD   dwSize = 0;
    TCHAR   szShortImagePath[MAX_PATH];

    if ( GetShortPathName( lpImagePath, szShortImagePath, elementsof(szShortImagePath) ) &&
        EnumProcesses( aProcesses, sizeof(aProcesses), &dwSize ) )
    {
        unsigned nProcesses = dwSize / sizeof(aProcesses[0]);

        for ( unsigned i = 0; !dwProcessId && i < nProcesses; i++ )
        {
            HANDLE  hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );

            if ( IsValidHandle(hProcess) )
            {
                TCHAR   szModulePath[MAX_PATH+1];

                if ( GetModuleFileNameEx( hProcess, NULL, szModulePath, MAX_PATH ) )
                {
                    TCHAR   szShortModulePath[MAX_PATH];

                    if ( GetShortPathName( szModulePath, szShortModulePath, elementsof(szShortModulePath) ) )
                    {
                        if ( 0 == _tcsicmp( szShortModulePath, szShortImagePath ) )
                            dwProcessId = aProcesses[i];
                    }
                }

                CloseHandle( hProcess );
            }
        }
    }

    return dwProcessId;
}
//***************************************************************************

static bool ParseCommandArgs( LPDWORD pdwProcessId, PEXCEPTION_POINTERS* ppException, LPDWORD pdwThreadId )
{
    int     argc = __argc;
#ifdef __MINGW32__
#ifdef _UNICODE
    TCHAR   **argv = reinterpret_cast<TCHAR **>(alloca((argc+1)*sizeof(WCHAR*)));
    int *sizes = reinterpret_cast<int *>(alloca(argc*sizeof(int)));
    int argsize=0;
    char **ptr;
    int i;
    ptr=__argv;
    for (i = 0; i < argc; ++i)
    {
        sizes[i]=MultiByteToWideChar(CP_ACP, 0, *ptr, -1, NULL, 0);
        argsize+=sizes[i]+1;
        ++ptr;
    }
    ++argsize;
    TCHAR   *args = reinterpret_cast<TCHAR *>(alloca(argsize*sizeof(WCHAR)));
    ptr=__argv;
    TCHAR *cptr=args;
    for (i = 0; i < argc; ++i)
    {
        argv[i]=cptr;
        MultiByteToWideChar( CP_ACP, 0, *ptr, -1, cptr, sizes[i] );
        ++ptr;
        cptr+=sizes[i];
        *cptr=0;
        ++cptr;
    }
    argv[i]=cptr;
    *cptr=0;
#else
    TCHAR   **argv = __argv;
#endif
#else
    TCHAR   **argv = __targv;
#endif
    bool    bSuccess = true;

    for ( int argn = 1; bSuccess && argn < argc; argn++ )
    {
        if ( 0 == _tcsicmp( argv[argn], _T("-h") ) ||
             0 == _tcsicmp( argv[argn], _T("/h") ) ||
             0 == _tcsicmp( argv[argn], _T("-?") ) ||
             0 == _tcsicmp( argv[argn], _T("/?") ) ||
             0 == _tcsicmp( argv[argn], _T("/help") ) ||
             0 == _tcsicmp( argv[argn], _T("-help") ) ||
             0 == _tcsicmp( argv[argn], _T("--help") )
             )
        {
            HINSTANCE   hInstance = GetModuleHandle(NULL);
            TCHAR   szUsage[FORMATBUFSIZE];
            TCHAR   szProcess[FORMATBUFSIZE];
            TCHAR   szProcessDescription[FORMATBUFSIZE];
            TCHAR   szHelpDescription[FORMATBUFSIZE];

            LoadAndFormatString( hInstance, IDS_MSG_CMDLINE_USAGE, szUsage, elementsof(szUsage) );
            LoadAndFormatString( hInstance, IDS_MSG_PARAM_PROCESSID, szProcess, elementsof(szProcess) );
            LoadAndFormatString( hInstance, IDS_MSG_PARAM_PROCESSID_DESCRIPTION, szProcessDescription, elementsof(szProcessDescription) );
            LoadAndFormatString( hInstance, IDS_MSG_PARAM_HELP_DESCRIPTION, szHelpDescription, elementsof(szHelpDescription) );

            _tprintf(
                TEXT("\n%s: crashrep %s\n\n")
                TEXT("/?, -h[elp]          %s\n\n")
                TEXT("%-20s %s\n\n"),
                szUsage, szProcess, szHelpDescription, szProcess, szProcessDescription
                );

            return true;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-p") ) ||
             0 == _tcsicmp( argv[argn], _T("/p") ) )
        {
            if ( ++argn < argc )
                *pdwProcessId = _tcstoul( argv[argn], NULL, 0 );
            else
                bSuccess = false;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-excp") ) ||
                  0 == _tcsicmp( argv[argn], _T("/excp") ) )
        {
            if ( ++argn < argc )
                *ppException = (PEXCEPTION_POINTERS)_tcstoul( argv[argn], NULL, 0 );
            else
                bSuccess = false;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-t") ) ||
                  0 == _tcsicmp( argv[argn], _T("/t") ) )
        {
            if ( ++argn < argc )
                *pdwThreadId = _tcstoul( argv[argn], NULL, 0 );
            else
                bSuccess = false;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-noui") ) ||
                  0 == _tcsicmp( argv[argn], _T("/noui") ) )
        {
            g_bNoUserInterface = true;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-send") ) ||
                  0 == _tcsicmp( argv[argn], _T("/send") ) )
        {
            g_bSendReport = true;
        }
        else if ( 0 == _tcsicmp( argv[argn], _T("-load") ) ||
                  0 == _tcsicmp( argv[argn], _T("/load") ) )
        {
            g_bLoadReport = true;
        }
        else // treat parameter as image path
        {
            TCHAR   szImagePath[MAX_PATH];
            LPTSTR  lpImageName;

            if ( GetFullPathName( argv[argn], MAX_PATH, szImagePath, &lpImageName ) )
            {
                DWORD   dwProcessId = FindProcessForImage( szImagePath );

                if ( dwProcessId )
                    *pdwProcessId = dwProcessId;
                else
                    bSuccess = false;
            }
        }
    }

    if ( !*pdwProcessId && !g_bLoadReport )
    {
        TCHAR   szImagePath[MAX_PATH];
        LPTSTR  lpImageName;

        if ( GetFullPathName( TEXT("soffice.exe"), MAX_PATH, szImagePath, &lpImageName ) )
        {
            DWORD   dwProcessId = FindProcessForImage( szImagePath );

            if ( dwProcessId )
                *pdwProcessId = dwProcessId;
            else
                bSuccess = false;
        }
    }

    return bSuccess;
}

//***************************************************************************

BOOL WriteCommentFile( LPCTSTR lpComment )
{
    BOOL    fSuccess = FALSE;
    TCHAR   szTempPath[MAX_PATH];

    if ( GetTempPath( elementsof(szTempPath), szTempPath ) )
    {
        TCHAR   szFileName[MAX_PATH];

        if ( GetTempFileName( szTempPath, TEXT("CMT"), 0, szFileName ) )
        {
            HANDLE  hFile =  CreateFile( szFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

            if ( hFile )
            {
                DWORD   dwBytesWritten;

                int needed = WideCharToMultiByte( CP_UTF8, 0, lpComment, -1, NULL, 0, NULL, NULL );
                if ( needed )
                {
                    char *lpCommentUTF8 = (char *)alloca( needed );
                    WideCharToMultiByte( CP_UTF8, 0, lpComment, -1, lpCommentUTF8, needed, NULL, NULL );
                    fSuccess = WriteFile( hFile, lpCommentUTF8, strlen(lpCommentUTF8), &dwBytesWritten, NULL );
                }
                else
                    fSuccess = TRUE;


                CloseHandle( hFile );

                WideCharToMultiByte( CP_ACP, 0, szFileName, -1, g_szCommentFileNameA, MAX_PATH, NULL, NULL );
            }

            if ( !fSuccess )
                DeleteFile( szFileName );
        }
    }

    return fSuccess;
}

//***************************************************************************

static int _tsetenv( const _TCHAR *lpVar, const _TCHAR *lpValue )
{
    if ( !lpValue )
        lpValue = _T("");

    _TCHAR  *envstr = (TCHAR *)alloca( (_tcslen( lpVar ) + _tcslen( lpValue ) + 2) * sizeof(_TCHAR) );

    _tcscpy( envstr, lpVar );
    _tcscat( envstr, _T("=") );
    _tcscat( envstr, lpValue );

    return _tputenv( envstr );
}

static bool read_line( FILE *fp, string& rLine )
{
    char szBuffer[1024];
    bool bSuccess = false;
    bool bEOL = false;
    string  line;


    while ( !bEOL && fgets( szBuffer, sizeof(szBuffer), fp ) )
    {
        int len = strlen(szBuffer);

        bSuccess = true;

        while ( len && szBuffer[len - 1] == '\n' )
        {
            szBuffer[--len] = 0;
            bEOL = true;
        }

        line.append( szBuffer );
    }

    rLine = line;
    return bSuccess;
}

static string get_script_string( const char *pFileName, const char *pKeyName )
{
    FILE    *fp = fopen( pFileName, "rt" );
    string  retValue;

    if ( fp )
    {
        string line;
        string section;

        while ( read_line( fp, line ) )
        {
            line = trim_string( line );


            string::size_type iEqualSign = line.find( '=', 0 );

            if ( iEqualSign != string::npos )
            {
                string  keyname = line.substr( 0, iEqualSign );
                keyname = trim_string( keyname );

                string  value = line.substr( sal::static_int_cast<string::size_type>(iEqualSign + 1) );
                value = trim_string( value );

                if ( value.length() && '\"' == value[0] )
                {
                    value.erase( 0, 1 );

                    string::size_type iQuotes = value.find( '"', 0 );

                    if ( iQuotes != string::npos )
                        value.erase( iQuotes );
                }

                if ( 0 == stricmp( keyname.c_str(), pKeyName ) )
                {
                    retValue = value;
                    break;
                }
            }
        }

        fclose( fp );
    }

    return retValue;
}

static bool ReadBootstrapParams( CrashReportParams &rParams )
{
    TCHAR   szBuffer[256] = TEXT("");
    TCHAR   szModuleName[MAX_PATH];
    TCHAR   szModuleVersionName[MAX_PATH];
    TCHAR   szDrive[_MAX_DRIVE];
    TCHAR   szDir[_MAX_DIR];
    TCHAR   szFName[_MAX_FNAME];
    TCHAR   szExt[_MAX_EXT];
    TCHAR   szReportServer[MAX_HOSTNAME];
    TCHAR   szReportPort[256];
    bool    bSuccess = false;

    GetModuleFileName( NULL, szModuleName, MAX_PATH );
    _tsplitpath( szModuleName, szDrive, szDir, szFName, szExt );
    _tmakepath( szModuleName, szDrive, szDir, _T("bootstrap"), _T(".ini") );
    _tmakepath( szModuleVersionName, szDrive, szDir, _T("version"), _T(".ini") );

    if (
        GetPrivateProfileString(
        TEXT("Bootstrap"),
        TEXT("ProductKey"),
        TEXT("OpenOffice.org"),
        szBuffer,
        elementsof(szBuffer),
        szModuleName )
        )
    {
        TCHAR   *pVersion = _tcschr( szBuffer, ' ' );

        g_wstrProductKey = szBuffer;

        if ( pVersion )
        {
            *pVersion = 0;
            pVersion++;
        }
        else
            pVersion = TEXT("");

        if ( !_tgetenv( _T("PRODUCTNAME") ) )
        {
            _tsetenv( TEXT("PRODUCTNAME"), szBuffer );
        }
        if ( !_tgetenv( _T("PRODUCTVERSION") ) )
            _tsetenv( TEXT("PRODUCTVERSION"), pVersion );
    }

    GetPrivateProfileString(
        TEXT("Version"),
        TEXT("buildid"),
        TEXT("unknown"),
        g_szBuildId, elementsof(g_szBuildId),
        szModuleVersionName );

    g_strDefaultLanguage = get_script_string( "instdb.inf", "DefaultLanguage" );

    if ( GetPrivateProfileString(
        TEXT("ErrorReport"),
        TEXT("ErrorReportPort"),
        TEXT("80"),
        szReportPort, elementsof(szReportPort),
        szModuleName
        ) )
    {
        TCHAR *endptr = NULL;

        unsigned short uReportPort = (unsigned short)_tcstoul( szReportPort, &endptr, 10 );
        if ( uReportPort )
            g_uReportPort = uReportPort;
    }

    if ( GetPrivateProfileString(
        TEXT("ErrorReport"),
        TEXT("ErrorReportServer"),
        TEXT(""),
        szReportServer, elementsof(szReportServer),
        szModuleName
        ) )
    {
        bSuccess = 0 != WideCharToMultiByte( CP_ACP, 0, szReportServer, -1, g_szReportServerA, elementsof(g_szReportServerA), NULL, NULL );
    }

    LPCTSTR lpEnvString;

    if ( 0 != (lpEnvString = _tgetenv( _T("ERRORREPORT_PROXYSERVER") )) )
        rParams.sProxyServer = lpEnvString;

    if ( 0 != (lpEnvString = _tgetenv( _T("ERRORREPORT_PROXYPORT") )) )
        rParams.sProxyPort = lpEnvString;

    if ( 0 != (lpEnvString = _tgetenv( _T("ERRORREPORT_SENDERADDRESS") )) )
        rParams.sEmail = lpEnvString;

    return bSuccess;
}

//***************************************************************************

bool SendHTTPRequest(
                FILE *fp,
                const char *pszServer,
                unsigned short uPort = 80,
                const char *pszProxyServer = NULL,
                unsigned short uProxyPort = 8080 )
{
    bool success = false;

    struct hostent *hp;

    if ( pszProxyServer )
        hp = gethostbyname( pszProxyServer );
    else
        hp = gethostbyname( pszServer );

    if ( hp )
    {
        SOCKET  s = socket( AF_INET, SOCK_STREAM, 0 );

        if ( s )
        {
            struct sockaddr_in address;

            memcpy(&(address.sin_addr.s_addr), *(hp->h_addr_list),sizeof(struct in_addr));
            address.sin_family = AF_INET;

            if ( pszProxyServer )
                address.sin_port = ntohs( uProxyPort );
            else
                address.sin_port = ntohs( uPort );

            if ( 0 == connect( s, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) )
            {
                fseek( fp, 0, SEEK_END );
                size_t length = ftell( fp );
                fseek( fp, 0, SEEK_SET );

                char buffer[2048];

                if ( pszProxyServer )
                    sprintf( buffer,
                    "POST http://%s:%d/soap/servlet/rpcrouter HTTP/1.0\r\n"
                        "Content-Type: text/xml; charset=\"utf-8\"\r\n"
                        "Content-Length: %d\r\n"
                        "SOAPAction: \"\"\r\n\r\n",
                        pszServer,
                        uPort,
                        length
                        );
                else
                    sprintf( buffer,
                        "POST /soap/servlet/rpcrouter HTTP/1.0\r\n"
                        "Content-Type: text/xml; charset=\"utf-8\"\r\n"
                        "Content-Length: %d\r\n"
                        "SOAPAction: \"\"\r\n\r\n",
                        length
                        );

                if ( SOCKET_ERROR != send( s, buffer, strlen(buffer), 0 ) )
                {
                    size_t nBytes;

                    do
                    {
                        nBytes = fread( buffer, 1, sizeof(buffer), fp );

                        if ( nBytes )
                            success = SOCKET_ERROR != send( s, buffer, nBytes, 0 );
                    } while( nBytes && success );

                    if ( success )
                    {
                        memset( buffer, 0, sizeof(buffer) );
                        success = SOCKET_ERROR != recv( s, buffer, sizeof(buffer), 0 );
                        if ( success )
                        {
                            char szHTTPSignature[sizeof(buffer)] = "";
                            unsigned uHTTPReturnCode = 0;

                            sscanf( buffer, "%s %d ", szHTTPSignature, &uHTTPReturnCode );
                            success = uHTTPReturnCode == 200;
                        }
                    }
                }

            }

            closesocket( s );
        }
    }

    return success;
}

//***************************************************************************

static void WriteSOAPRequest( FILE *fp )
{
    fprintf( fp,
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
        "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"\n"
        "xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"\n"
        "xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"\n"
        "xmlns:rds=\"urn:ReportDataService\"\n"
        "xmlns:apache=\"http://xml.apache.org/xml-soap\"\n"
        "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
        "<SOAP-ENV:Body>\n"
        );

    fprintf( fp, "<rds:submitReport>\n"  );
    fprintf( fp, "<body xsi:type=\"xsd:string\">This is an autogenerated crash report mail.</body>\n" );
    fprintf( fp, "<hash xsi:type=\"apache:Map\">\n" );

    FILE    *fpin = fopen( g_szReportFileNameA, "r" );
    if ( fpin )
    {
        fprintf( fp,
            "<item>\n"
            "<key xsi:type=\"xsd:string\">reportmail.xml</key>\n"
            "<value xsi:type=\"xsd:string\"><![CDATA[" );
        fcopy( fpin, fp );
        fprintf( fp, "]]></value></item>\n" );
        fclose( fpin );
    }

    fpin = fopen( g_szCommentFileNameA, "r" );
    if ( fpin )
    {
        fprintf( fp,
            "<item>\n"
            "<key xsi:type=\"xsd:string\">description.txt</key>\n"
            "<value xsi:type=\"xsd:string\"><![CDATA[" );
        fcopy( fpin, fp );
        fprintf( fp, "]]></value></item>\n" );
        fclose( fpin );
    };


    fpin = fopen( g_szDumpFileNameA, "rb" );
    if ( fpin )
    {
        FILE *fptemp = _tmpfile();

        if ( fptemp )
        {
            if ( base64_encode( fpin, fptemp ) )
            {
                fseek( fptemp, 0, SEEK_SET );
                fprintf( fp,
                    "<item>\n"
                    "<key xsi:type=\"xsd:string\">user.dmp</key>\n"
                    "<value xsi:type=\"xsd:string\">" );
                fcopy( fptemp, fp );
                fprintf( fp, "</value></item>\n" );
            }
            fclose( fptemp );
        }
        fclose( fpin );
    }

    fprintf( fp,
        "</hash>\n"
        "</rds:submitReport>\n"
        "</SOAP-ENV:Body>\n"
        "</SOAP-ENV:Envelope>\n"
        );
}

//***************************************************************************

struct RequestParams
{
    bool    success;
    FILE    *fpin;
    const char *lpServer;
    unsigned short uPort;
    const char *lpProxyServer;
    unsigned short uProxyPort;
    HWND    hwndStatus;
};

void _cdecl SendingThread( void *lpArgs )
{
    RequestParams *pParams = (RequestParams *)lpArgs;

    pParams->success = SendHTTPRequest( pParams->fpin, pParams->lpServer, pParams->uPort, pParams->lpProxyServer, pParams->uProxyPort );

    PostMessage( pParams->hwndStatus, WM_COMMAND, IDOK, 0 );
}

//***************************************************************************

BOOL CALLBACK SendingStatusDialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static RequestParams *pRequest = NULL;
    static HANDLE hSendingThread = NULL;

    switch ( uMsg )
    {
    case WM_INITDIALOG:
        {
            TCHAR   szBuffer[1024] = TEXT("");
            HINSTANCE   hInstance = (HINSTANCE)GetWindowLong( hwndDlg, GWL_HINSTANCE );
            //HWND  hwndParent = (HWND)GetWindowLong( hwndDlg, GWL_HWNDPARENT );

            pRequest = (RequestParams *)lParam;

            LoadAndFormatString( hInstance, IDS_SENDING_REPORT_HEADER, szBuffer, elementsof(szBuffer) );
            SetWindowText( hwndDlg, szBuffer );

            LoadAndFormatString( hInstance, IDS_SENDING_REPORT_STATUS, szBuffer, elementsof(szBuffer) );
            Static_SetText( GetDlgItem(hwndDlg, IDC_SENDING_REPORT_STATUS), szBuffer );

            LoadAndFormatString( hInstance, IDS_CANCEL_BUTTON, szBuffer, elementsof(szBuffer) );
            Button_SetText( GetDlgItem(hwndDlg, IDCANCEL), szBuffer );

            pRequest->hwndStatus = hwndDlg;

            hSendingThread = (HANDLE)_beginthread( SendingThread, 0, pRequest );
        }
        return TRUE;
    case WM_COMMAND:
        switch ( LOWORD(wParam) )
        {
        case IDCANCEL:
            TerminateThread( hSendingThread, 0 );
        case IDOK:
            WaitForSingleObject( hSendingThread, INFINITE );
            CloseHandle( hSendingThread );
            EndDialog( hwndDlg, wParam );
            return TRUE;
        }
        break;
    default:
        break;
    }

    return FALSE;
}

//***************************************************************************

bool SendCrashReport( HWND hwndParent, const CrashReportParams &rParams )
{
    bool success = false;
    char szProxyServer[1024] = "";
    unsigned short uProxyPort = 8080;
    TCHAR *endptr = NULL;

    switch ( rParams.uInternetConnection )
    {
    case 2:
        {
            WideCharToMultiByte(
                CP_ACP, 0, rParams.sProxyServer.c_str(), -1,
                szProxyServer, sizeof(szProxyServer), NULL, NULL );
            uProxyPort = (unsigned short)_tcstoul( rParams.sProxyPort.c_str(), &endptr, 10 );
        }
        break;
    case 0:
        {
            DWORD   dwProxyEnable = 0;

            RegReadValue( HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"),
                TEXT("ProxyEnable"),
                &dwProxyEnable,
                sizeof(dwProxyEnable) );

            if ( dwProxyEnable )
            {
                TCHAR   tszProxyServers[1024] = TEXT("");

                if ( ERROR_SUCCESS == RegReadValue( HKEY_CURRENT_USER,
                    TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"),                TEXT("ProxyServer"),
                    tszProxyServers,
                    sizeof(tszProxyServers) ) )
                {
                    TCHAR *lpHttpStart = _tcsstr( tszProxyServers, TEXT("http=") );

                    if ( lpHttpStart )
                        lpHttpStart += 5;
                    else
                        lpHttpStart = tszProxyServers;

                    TCHAR *lpHttpEnd = _tcschr( lpHttpStart, ';' );

                    if ( lpHttpEnd )
                        *lpHttpEnd = 0;

                    char    szHTTPProxyServer[1024] = "";
                    WideCharToMultiByte( CP_ACP, 0, lpHttpStart, -1, szHTTPProxyServer, sizeof(szHTTPProxyServer), NULL, NULL );

                    char *lpColon = strchr( szHTTPProxyServer, ':' );

                    if ( lpColon )
                    {
                        char *endptr = NULL;

                        *lpColon = 0;
                        uProxyPort = (unsigned short)strtoul( lpColon + 1, &endptr, 10 );
                    }
                    else
                        uProxyPort = 8080;

                    strcpy( szProxyServer, szHTTPProxyServer );

                }
            }
        }
        break;
    default:
    case 1:
        break;
    }

    FILE    *fptemp = _tmpfile();
    if ( fptemp )
    {
        RequestParams   request;

        request.success = false;
        request.fpin = fptemp;
        request.lpServer = REPORT_SERVER;
        request.uPort = REPORT_PORT;
        request.lpProxyServer = szProxyServer[0] ? szProxyServer : NULL;
        request.uProxyPort = uProxyPort;
        request.hwndStatus = NULL;

        WriteSOAPRequest( fptemp );
        fseek( fptemp, 0, SEEK_SET );

        if ( hwndParent )
        {
            int retid = DialogBoxParam(
                GetModuleHandle(NULL),
                MAKEINTRESOURCE(IDD_SENDING_STATUS),
                hwndParent,
                SendingStatusDialogProc,
                (LPARAM)&request
                );

            success = request.success;

            if ( IDOK == retid )
            {
                if ( !success )
                {
                    TCHAR   szMessage[1024];

                    LoadAndFormatString( GetModuleHandle(NULL), IDS_ERROR_MSG_PROXY, szMessage, elementsof(szMessage) );

                    MessageBox( hwndParent, szMessage, NULL, MB_ICONERROR | MB_OK );
                }
                else
                {
                    TCHAR   szMessage[1024];
                    TCHAR   szTitle[1024];

                    LoadAndFormatString( GetModuleHandle(NULL), IDS_SENDING_REPORT_STATUS_FINISHED, szMessage, elementsof(szMessage) );
                    LoadAndFormatString( GetModuleHandle(NULL), IDS_SENDING_REPORT_HEADER, szTitle, elementsof(szTitle) );

                    MessageBox( hwndParent, szMessage, szTitle, MB_ICONINFORMATION | MB_OK );
                }
            }

        }
        else
        {
            HANDLE hSendingThread = (HANDLE)_beginthread( SendingThread, 0, (void *)&request );

            WaitForSingleObject( hSendingThread, INFINITE );

            success = request.success;
            if ( !success )
            {
                TCHAR   szMessage[1024];

                LoadAndFormatString( GetModuleHandle(NULL), IDS_ERROR_MSG_PROXY, szMessage, elementsof(szMessage) );
                _ftprintf( stderr, _T("ERROR: %s\n"), szMessage );
            }
            else
            {
                TCHAR   szMessage[1024];

                LoadAndFormatString( GetModuleHandle(NULL), IDS_SENDING_REPORT_STATUS_FINISHED, szMessage, elementsof(szMessage) );

                _ftprintf( stderr, _T("SUCCESS: %s\n"), szMessage );
            }
        }
        fclose( fptemp );
    }
    else
    {
        TCHAR   szMessage[1024];

        LoadAndFormatString( GetModuleHandle(NULL), IDS_ERROR_MSG_DISK_FULL, szMessage, elementsof(szMessage) );

        if ( hwndParent )
            MessageBox( hwndParent, szMessage, NULL, MB_ICONERROR | MB_OK );
        else
            _ftprintf( stderr, _T("ERROR: %s\n"), szMessage );
    }

    return success;
}

//***************************************************************************

#ifdef __MINGW32__
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR /*lpCmdLine*/, int )
#else
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE, LPTSTR /*lpCmdLine*/, int )
#endif
{
    int exitcode = -1;
    int argc = __argc;

#ifdef __MINGW32__
    char **argv = __argv;
#else
#ifdef _UNICODE
    char **argv = new char *[argc + 1];

    for ( int argn = 0; argn < argc; argn++ )
    {
        int nBytes = WideCharToMultiByte( CP_ACP, 0, __targv[argn], -1, NULL, 0, NULL, NULL );
        argv[argn] = new char[nBytes];
        WideCharToMultiByte( CP_ACP, 0, __targv[argn], -1, argv[argn], nBytes, NULL, NULL );
    }
    argv[argc] = NULL;
#else
    char **argv = __targv;
#endif
#endif

    osl_setCommandArgs( argc, argv );

    PEXCEPTION_POINTERS pExceptionPointers = NULL;
    DWORD               dwProcessId = 0;
    DWORD               dwThreadId = 0;

    WSADATA wsaData;
    WORD    wVersionRequested;

    wVersionRequested = MAKEWORD(1, 1);
    WSAStartup(wVersionRequested, &wsaData);

    CrashReportParams   Params;

    Params.ReadFromRegistry();
    Params.ReadFromEnvironment();

    if ( ReadBootstrapParams( Params ) &&
        ParseCommandArgs( &dwProcessId, &pExceptionPointers, &dwThreadId ) )
    {
        bool    bGotDumpFile;

        if ( g_bLoadReport )
            bGotDumpFile = FindDumpFile();
        else
            bGotDumpFile = WriteDumpFile( dwProcessId, pExceptionPointers, dwThreadId );

        if( bGotDumpFile )
        {
            hash_map< string, string > aLibraries;

            if ( g_bLoadReport )
            {
                g_fpStackFile = _open_reportfile( _T(".stk"), _T("rb") );
                g_fpChecksumFile = _open_reportfile( _T(".chk"), _T("rb") );
            }
            else
            {
                if ( g_bSendReport )
                {
                    g_fpStackFile = _tmpfile();
                    g_fpChecksumFile = _tmpfile();
                }
                else
                {
                    g_fpStackFile = _open_reportfile( _T(".stk"), _T("w+b") );
                    g_fpChecksumFile = _open_reportfile( _T(".chk"), _T("w+b") );

                    FILE    *fpUnsent = _open_reportfile( _T(".lck"), _T("w+b") );
                    if ( fpUnsent )
                    {
                        fprintf( fpUnsent, "Unsent\r\n" );
                        fclose( fpUnsent );
                    }
                }

                WriteStackFile( g_fpStackFile, aLibraries, dwProcessId, pExceptionPointers );
                WriteChecksumFile( g_fpChecksumFile, aLibraries );
                WriteReportFile( &Params );

                FILE    *fpPreview = _open_reportfile( _T(".prv"), _T("w+b") );

                if ( fpPreview )
                {
                    FILE     *fp = fopen( g_szReportFileNameA, "rb" );
                    if ( fp )
                    {
                        fcopy( fp, fpPreview );
                        fclose( fp );
                    }
                    fclose( fpPreview );
                }
            }

            if ( g_bSendReport )
            {
                InitCommonControls();

                // Actually this should never be true anymore
                if ( !g_bNoUserInterface && InitRichEdit() )
                {

                    INT_PTR result = DialogBoxParam( hInstance, MAKEINTRESOURCE(IDD_DIALOG_FRAME), NULL, DialogProc, (LPARAM)&Params );

                    if ( result > 0 )
                    {
                        exitcode = 0;
                    }
                    DeinitRichEdit();
                }
                else
                {
                    WriteCommentFile( Params.sComment.c_str() );
                    WriteReportFile( &Params );
                    if ( SendCrashReport( NULL, Params ) )
                        exitcode = 0;
                }


                if ( g_szReportFileNameA[0] )
                    DeleteFileA( g_szReportFileNameA );

                if ( g_szCommentFileNameA[0] )
                    DeleteFileA( g_szCommentFileNameA );
            }
            else
            {
                if ( g_szReportFileNameA[0] )
                    DeleteFileA( g_szReportFileNameA );
                exitcode = 0;
            }

            if ( g_szDumpFileNameA[0] && g_bSendReport )
                    DeleteFileA( g_szDumpFileNameA );

            if ( g_fpStackFile )
                fclose( g_fpStackFile );

            if ( g_fpChecksumFile )
                fclose( g_fpChecksumFile );
        }
    }


    return exitcode;
}

