/*************************************************************************
 *
 * 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.
 *
 ************************************************************************/
#include <cstdio>
#include <stdlib.h>
#include <sys/utsname.h>
#include <_version.h>
#include <errno.h>
#include <string>
#include <string.h>
#include <assert.h>

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <pwd.h>
#include <pthread.h>
#include <limits.h>

#include <hash_map>
#include <vector>
#include <string>

#if defined (LINUX) || (FREEBSD)
#include <netinet/in.h>
#endif

typedef int SOCKET;

#define closesocket     close
#define SOCKET_ERROR    -1

#ifdef SOLARIS
const char *basename( const char *filename )
{
    const char *pSlash = strrchr( filename, '/' );

    return pSlash ? pSlash + 1 : pSlash;
}
#endif

using namespace std;

static bool g_bNoUI = false;
static bool g_bSendReport = false;
static bool g_bLoadReport = false;

static bool g_bDebugMode = false;
static int  g_signal = 0;

static string g_strProductKey;
static string g_strReportServer;
static unsigned short g_uReportPort = 80;
static string g_buildid;
static string g_strDefaultLanguage;
static string g_strXMLFileName;
static string g_strPStackFileName;
static string g_strChecksumFileName;
static string g_strProgramDir;

static char g_szStackFile[L_tmpnam] = "";
static char g_szDescriptionFile[2048] = "";
static char g_szReportFile[2048] = "";

#define SO_CRASHREPORT_MAIL "so-report@sun.com"
#define PSTACK_CMD          "pstack %d"

#ifdef LINUX
#define PMAP_CMD            "cat /proc/%d/maps"
#else
#define PMAP_CMD            "pmap %d"
#endif

#define REPORT_SERVER   (g_strReportServer.c_str())
#define REPORT_PORT     g_uReportPort

static string getprogramdir()
{
    return g_strProgramDir;
}

static const char *getlocale()
{
    const char * locale = getenv( "LC_ALL" );

    if( NULL == locale )
        locale = getenv( "LC_CTYPE" );

    if( NULL == locale )
        locale = getenv( "LANG" );

    if( NULL == locale )
        locale = "C";

    return locale;
}

static const char *get_home_dir()
{
    struct passwd *ppwd = getpwuid( getuid() );

    return ppwd ? (ppwd->pw_dir ? ppwd->pw_dir : "/") : "/";
}

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 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 *fpout, FILE *fpin )
{
    char buffer[1024];
    size_t nBytes;
    size_t nBytesWritten = 0;

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

    return nBytesWritten;
}

/*
   writes the report to a temp-file
   from which it can be reviewed and sent
*/

bool write_report( const hash_map< string, string >& rSettings )
{
    FILE    *fp = fopen( tmpnam( g_szReportFile ), "w" );
    const char *pszUserType = getenv( "STAROFFICE_USERTYPE" );

    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"
       "<reportmail:title>%s</reportmail:title>\n"
       "<reportmail:attachment name=\"description.txt\" media-type=\"text/plain\" class=\"UserComment\"/>\n"
       "<reportmail:attachment name=\"stack.txt\" media-type=\"text/plain\" class=\"pstack output\"/>\n"
       "</reportmail:mail>\n"
       "<officeinfo:officeinfo xmlns:officeinfo=\"http://openoffice.org/2002/officeinfo\" build=\"%s\" platform=\"%s\" language=\"%s\" exceptiontype=\"%d\" product=\"%s\" procpath=\"%s\"/>\n"
       ,
       pszUserType ? xml_encode( pszUserType ).c_str() : "",
       xml_encode(rSettings.find( "CONTACT" )->second).c_str(),
       xml_encode(rSettings.find( "EMAIL" )->second).c_str(),
       xml_encode(rSettings.find( "TITLE" )->second).c_str(),
       g_buildid.length() ? xml_encode( g_buildid ).c_str() : "unknown",
       _INPATH,
       g_strDefaultLanguage.c_str(),
       g_signal,
       g_strProductKey.length() ? xml_encode(g_strProductKey).c_str() : "unknown",
       xml_encode(getprogramdir()).c_str()
       );

    struct utsname  info;

    memset( &info, 0, sizeof(info) );
    uname( &info );

    fprintf( fp,
       "<systeminfo:systeminfo xmlns:systeminfo=\"http://openoffice.org/2002/systeminfo\">\n"
       "<systeminfo:System name=\"%s\" version=\"%s\" build=\"%s\" locale=\"%s\"/>\n"
       ,
       xml_encode( info.sysname ).c_str(),
       xml_encode( info.version ).c_str(),
       xml_encode( info.release ).c_str(),
       xml_encode( getlocale() ).c_str()
       );
    fprintf( fp, "<systeminfo:CPU type=\"%s\"/>\n", xml_encode( info.machine ).c_str() );
    fprintf( fp, "</systeminfo:systeminfo>\n" );

    FILE *fpxml = fopen( g_strXMLFileName.c_str(), "r" );
    if ( fpxml )
    {
        fcopy( fp, fpxml );
        fclose( fpxml );
    }

    FILE *fpchk = fopen( g_strChecksumFileName.c_str(), "r" );
    if ( fpchk )
    {
        fcopy( fp, fpchk );
        fclose( fpchk );
    }

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

    fclose( fp );

    return true;
}


bool write_description( const hash_map< string, string >& rSettings )
{
    bool    bSuccess = false;
    FILE    *fp = fopen( tmpnam( g_szDescriptionFile ), "w" );

    if ( fp )
    {
        bSuccess = true;
        fprintf( fp, "\xEF\xBB\xBF" );
        fprintf( fp, "%s\n", rSettings.find( "DESCRIPTION" )->second.c_str() );
        fclose( fp );
    }

    return bSuccess;
}

#if 0
// unused
static void printSettings( const hash_map<string,string>& rSettings )
{
    printf( "Settings:\n" );
    for( hash_map<string,string>::const_iterator it = rSettings.begin(); it != rSettings.end(); ++it )
    {
        printf( "%s=\"%s\"\n", it->first.c_str(), it->second.c_str() );
    }
}
#endif

bool save_crash_report( const string& rFileName, const hash_map< string, string >& /*rSettings*/ )
{
    bool bSuccess = false;
    FILE    *fpout = fopen( rFileName.c_str(), "w" );

    if ( fpout )
    {
        FILE *fpin = fopen( g_szStackFile, "r" );

        if ( fpin )
        {
            char    buf[1024];

            while (fgets(buf, sizeof(buf), fpin) != NULL)
            {
                fputs(buf, fpout);
            }

            bSuccess = true;

            fclose ( fpin );
        }

        fclose( fpout );
    }

    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,
                        static_cast<int>(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",
                        static_cast<int>(length)
                        );

                if ( g_bDebugMode )
                {
                    printf( "*** Sending HTTP request ***\n\n" );
                    printf( buffer );
                }

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

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

                        if ( nBytes )
                        {
                            if ( g_bDebugMode )
                                fwrite( buffer, 1, nBytes, stdout );
                            success = SOCKET_ERROR != send( s, buffer, nBytes, 0 );
                        }
                    } while( nBytes && success );

                    if ( success )
                    {
                        if ( g_bDebugMode )
                            printf( "*** Receiving HTTP response ***\n\n" );

                        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;
                        }
                        if ( g_bDebugMode )
                            do
                            {
                                printf( buffer );
                                memset( buffer, 0, sizeof(buffer) );
                            } while ( 0 < recv( s, buffer, sizeof(buffer), 0 ) );
                    }
                }

            }

            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_szReportFile, "r" );
    if ( fpin )
    {
        fprintf( fp,
            "<item>\n"
            "<key xsi:type=\"xsd:string\">reportmail.xml</key>\n"
            "<value xsi:type=\"xsd:string\"><![CDATA[" );
        fcopy( fp, fpin );
        fprintf( fp, "]]></value></item>\n" );
        fclose( fpin );
    }

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

    fpin = fopen( g_szStackFile, "r" );
    if ( fpin )
    {
        fprintf( fp,
            "<item>\n"
            "<key xsi:type=\"xsd:string\">stack.txt</key>\n"
            "<value xsi:type=\"xsd:string\"><![CDATA[" );
        fcopy( fp, fpin );
        fprintf( fp, "]]></value></item>\n" );
        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 *pServer;
    unsigned short uPort;
    const char *pProxyServer;
    unsigned short uProxyPort;
};


bool send_crash_report( const hash_map< string, string >& rSettings )
{
    if ( 0 == strcasecmp( rSettings.find( "CONTACT" )->second.c_str(), "true" ) &&
         !trim_string(rSettings.find( "EMAIL" )->second).length() )
    {
        return false;
    }

    char *endptr = NULL;

    const char *pProxyServer = rSettings.find( "SERVER" )->second.c_str();
    unsigned short uProxyPort = (unsigned short)strtoul( rSettings.find( "PORT" )->second.c_str(), &endptr, 10 );

    bool bUseProxy = !strcasecmp( "true", rSettings.find( "USEPROXY" )->second.c_str() );


    write_description( rSettings );
    write_report( rSettings );

    bool bSuccess = false;

    FILE    *fptemp = tmpfile();
    if ( fptemp )
    {
        WriteSOAPRequest( fptemp );
        fseek( fptemp, 0, SEEK_SET );

        bSuccess = SendHTTPRequest(
            fptemp,
            REPORT_SERVER, REPORT_PORT,
            bUseProxy ? pProxyServer : NULL,
            uProxyPort ? uProxyPort : 8080
            );

        fclose( fptemp );

    }

    unlink( g_szDescriptionFile );
    unlink( g_szReportFile );

    return bSuccess;
}


static bool append_file( const char *filename, string& rString )
{
    char    buf[1024];
    bool    bSuccess = false;

    FILE *fp = fopen( filename, "r" );
    if ( fp )
    {
        bSuccess = true;
        while (fgets(buf, sizeof(buf), fp) != NULL)
        {
            rString.append( buf );
        }
        fclose( fp );
    }

    return true;
}

string crash_get_details( const hash_map< string, string >& rSettings )
{
    string aRet;

    write_description( rSettings );
    write_report( rSettings );

    aRet.append( rSettings.find( "TITLE" )->second.c_str() );
    aRet.append( "\n\n" );
    append_file( g_szDescriptionFile, aRet );
    aRet.append( "\n\n-------\n\n" );
    append_file( g_szReportFile, aRet );
    aRet.append( "\n\n-------\n\n" );
    append_file( g_szStackFile, aRet );

    unlink( g_szDescriptionFile );
    unlink( g_szReportFile );

    return aRet;
}


// ensure validity of program relative paths
static void setup_program_dir( const char* progname )
{
    char    szCanonicProgPath[PATH_MAX];


    if ( realpath( progname, szCanonicProgPath ) )
    {
        string aDir = szCanonicProgPath;

        size_t pos = aDir.rfind( '/' );
        // FIXME: search PATH if necessary
        assert( pos != string::npos );

        g_strProgramDir = aDir.substr( 0, pos + 1 );
        aDir.erase( pos );
        chdir( aDir.c_str() );
    }
}

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

static long setup_commandline_arguments( int argc, char** argv, int *pSignal )
{
    long    pid = 0;
    int     signal = 0;

    for ( int n = 1; n < argc; n++ )
    {
        if ( 0 == strcmp( argv[n], "-p" ) )
        {
            if ( ++n < argc )
                pid = strtol( argv[n], NULL, 0 );
        }
        else if ( 0 == strcmp( argv[n], "-s" ) )
        {
            if ( ++n < argc )
                signal = strtol( argv[n], NULL, 0 );
        }
        else if ( 0 == strcmp( argv[n], "-debug" ) )
        {
            g_bDebugMode = true;
        }
        else if ( 0 == strcmp( argv[n], "-xml" ) )
        {
            if ( ++n < argc )
                g_strXMLFileName = argv[n];
        }
        else if ( 0 == strcmp( argv[n], "-stack" ) )
        {
            if ( ++n < argc )
                g_strPStackFileName = argv[n];
        }
        else if ( 0 == strcmp( argv[n], "-chksum" ) )
        {
            if ( ++n < argc )
                g_strChecksumFileName = argv[n];
        }
        else if ( 0 == strcmp( argv[n], "-noui" ) )
        {
            g_bNoUI = true;
        }
        else if ( 0 == strcmp( argv[n], "-send" ) )
        {
            g_bSendReport = true;
        }
        else if ( 0 == strcmp( argv[n], "-load" ) )
        {
            g_bLoadReport = true;
        }
        else if ( argv[n] && strlen(argv[n]) )
        {
            printf(
                "\n%s crash_report %s\n\n" \
                "/?, -h[elp]          %s\n\n" \
                "%-20s %s\n\n",
                "%MSG_CMDLINE_USAGE%",
                "%MSG_PARAM_PROCESSID%",
                "%MSG_PARAM_HELP_DESCRIPTION%",
                "%MSG_PARAM_PROCESSID%",
                "%MSG_PARAM_PROCESSID_DESCRIPTION%"
                );
            break;
        }
    }

    *pSignal = signal;

    return pid;
}

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

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, "r" );
    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( iEqualSign + 1, string::npos );
                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 == strcasecmp( keyname.c_str(), pKeyName ) )
                {
                    retValue = value;
                    break;
                }
            }
        }

        fclose( fp );
    }

    return retValue;
}

static string get_profile_string( const char *pFileName, const char *pSectionName, const char *pKeyName, const char *pDefault = NULL )
{
    FILE    *fp = fopen( pFileName, "r" );
    string  retValue = pDefault ? pDefault : "";

    if ( fp )
    {
        string line;
        string section;

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

            if ( line.length() && line[0] == '[' )
            {
                line.erase( 0, 1 );
                string::size_type end = line.find( ']', 0 );

                if ( string::npos != end )
                    section = trim_string( line.substr( 0, end ) );
            }
            else
            {

                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( iEqualSign + 1, string::npos );
                    value = trim_string( value );

                    if (
                        0 == strcasecmp( section.c_str(), pSectionName ) &&
                        0 == strcasecmp( keyname.c_str(), pKeyName )
                         )
                    {
                        retValue = value;
                        break;
                    }
                }
            }
        }

        fclose( fp );
    }

    return retValue;
}

static string get_environment_string( const char *pEnvName )
{
    const char *pEnvValue = getenv( pEnvName );

    if ( pEnvValue )
        return pEnvValue;
    else
        return "";
}

static string read_from_file( const string& rFileName )
{
    string  content;
    FILE *fp = fopen( rFileName.c_str(), "r" );

    if ( fp )
    {
        char    buffer[256 + 1];
        size_t  nBytesRead;

        while( 0 != ( nBytesRead = fread( buffer, 1, sizeof(buffer) - 1,  fp ) ) )
        {
            buffer[nBytesRead] = 0;
            content += buffer;
        }

        fclose( fp );
    }

    return content;
}

#define RCFILE ".crash_reportrc"
#define XMLFILE ".crash_report_frames"
#define CHKFILE ".crash_report_checksum"
#define LCKFILE ".crash_report_unsent"
#define PRVFILE ".crash_report_preview"

static void load_crash_data()
{
    g_strXMLFileName = get_home_dir();
    g_strXMLFileName += "/";
    g_strXMLFileName += string(XMLFILE);

    g_strChecksumFileName = get_home_dir();
    g_strChecksumFileName += "/";
    g_strChecksumFileName += string(CHKFILE);
}

static bool write_crash_data()
{
    bool success = true;
    string  sFile = get_home_dir();

    sFile += "/";
    sFile += string(XMLFILE);

    FILE *fp = fopen( sFile.c_str(), "w" );

    if ( fp )
    {
        FILE    *fpin = fopen( g_strXMLFileName.c_str(), "r" );

        if ( fpin )
        {
            fcopy( fp, fpin );
            fclose( fpin );
        }

        fclose( fp );
    }

    sFile = get_home_dir();

    sFile += "/";
    sFile += string(CHKFILE);

    fp = fopen( sFile.c_str(), "w" );

    if ( fp )
    {
        FILE    *fpin = fopen( g_strChecksumFileName.c_str(), "r" );

        if ( fpin )
        {
            fcopy( fp, fpin );
            fclose( fpin );
        }

        fclose( fp );
    }

    sFile = get_home_dir();

    sFile += "/";
    sFile += string(LCKFILE);

    fp = fopen( sFile.c_str(), "w" );

    if ( fp )
    {
        fprintf( fp, "Unsent\n" );
        fclose( fp );
    }

    return success;
}

#if 0
// unused
static bool write_settings( const hash_map< string, string >& rSettings )
{
    bool success = false;
    string  sRCFile = get_home_dir();

    sRCFile += "/";
    sRCFile += string(RCFILE);

    FILE *fp = fopen( sRCFile.c_str(), "w" );

    if ( fp )
    {
        fprintf( fp, "[Options]\n" );
        fprintf( fp, "UseProxy=%s\n", rSettings.find( "USEPROXY" )->second.c_str() );
        fprintf( fp, "ProxyServer=%s\n", rSettings.find( "SERVER" )->second.c_str() );
        fprintf( fp, "ProxyPort=%s\n", rSettings.find( "PORT" )->second.c_str() );
        fprintf( fp, "ReturnAddress=%s\n", rSettings.find( "EMAIL" )->second.c_str() );
        fprintf( fp, "AllowContact=%s\n", rSettings.find( "CONTACT" )->second.c_str() );
        fclose( fp );
    }

    return success;
}
#endif

static void read_settings( hash_map< string, string >& rSettings )
{
    string  sRCFile = get_home_dir();

    sRCFile += "/";
    sRCFile += string(RCFILE);

    rSettings[ "EMAIL" ] = get_profile_string( sRCFile.c_str(), "Options", "ReturnAddress" );
    rSettings[ "SERVER" ] = get_profile_string( sRCFile.c_str(), "Options", "ProxyServer" );
    rSettings[ "PORT" ] = get_profile_string( sRCFile.c_str(), "Options", "ProxyPort" );
    rSettings[ "USEPROXY" ] = get_profile_string( sRCFile.c_str(), "Options", "UseProxy" );
    rSettings[ "CONTACT" ] = get_profile_string( sRCFile.c_str(), "Options", "AllowContact" );
    rSettings[ "DESCRIPTION" ] = "";
    rSettings[ "TITLE" ] = "";
}

static void read_settings_from_environment( hash_map< string, string >& rSettings )
{
    string  strEnv;

    strEnv = get_environment_string( "ERRORREPORT_RETURNADDRESS" );
    if ( strEnv.length() )
    {
        rSettings[ "EMAIL" ] = strEnv;
        if ( !(rSettings.find( "CONTACT" )->second).length() )
            rSettings[ "CONTACT" ] = "true";
    }
    else if ( !(rSettings.find( "CONTACT" )->second).length() )
        rSettings[ "CONTACT" ] = "false";


    strEnv = get_environment_string( "ERRORREPORT_HTTPPROXYSERVER" );
    if ( strEnv.length() )
        rSettings[ "SERVER" ] = strEnv;

    strEnv = get_environment_string( "ERRORREPORT_HTTPPROXYPORT" );
    if ( strEnv.length() )
        rSettings[ "PORT" ] = strEnv;

    strEnv = get_environment_string( "ERRORREPORT_HTTPCONNECTIONTYPE" );
    if ( strEnv.length() )
        rSettings[ "USEPROXY" ] = 0 == strcasecmp( strEnv.c_str(), "MANUALPROXY" ) ? "true" : "false";

    strEnv = get_environment_string( "ERRORREPORT_BODYFILE" );
    if ( strEnv.length() )
        rSettings[ "DESCRIPTION" ] = read_from_file( strEnv );

    strEnv = get_environment_string( "ERRORREPORT_SUBJECT" );
    if ( strEnv.length() )
        rSettings[ "TITLE" ] = strEnv;
}

static bool setup_version()
{
    if ( !getenv( "PRODUCTNAME" ) )
    {
        string productkey = get_profile_string( "bootstraprc", "Bootstrap", "ProductKey" );

        g_strProductKey = productkey;

        if ( productkey.length() )
        {
            static string productname;
            static string productversion;
            string::size_type   iSpace = productkey.find( ' ', 0 );

            if ( string::npos != iSpace )
            {
                productname = productkey.substr( 0, iSpace );
                productversion = productkey.substr( iSpace + 1, string::npos );
            }
            else
                productname = productkey;

            productname.insert( 0, "PRODUCTNAME=" );
            putenv( (char *)productname.c_str() );

            productversion.insert( 0, "PRODUCTVERSION=" );
            putenv( (char *)productversion.c_str() );
        }
    }

    g_buildid = get_profile_string( "versionrc", "Version", "BuildId" );
    g_strDefaultLanguage = get_script_string( "instdb.ins", "DefaultLanguage"  );

    g_strReportServer = get_profile_string( "bootstraprc", "ErrorReport", "ErrorReportServer" );

    string strReportPort = get_profile_string( "bootstraprc", "ErrorReport", "ErrorReportPort", "80" );
    char *endptr = NULL;
    unsigned short uReportPort = (unsigned short)strtoul( strReportPort.c_str(), &endptr, 10 );
    g_uReportPort = uReportPort ? uReportPort : 80;

    return 0 != g_strReportServer.length();
}

#if 0
// Use gconftool-2 to determine if gnome accessiblity is enabled
// unused
static bool get_accessibility_state()
{
    bool bAccessible = false;
    FILE *fin = popen( "gconftool-2 -g /desktop/gnome/interface/accessibility", "r");

    if ( fin )
    {
        char buffer[sizeof("true")];

        bAccessible = fgets( buffer, sizeof(buffer), fin ) && 0 == strcmp( buffer, "true" );

        pclose( fin );
    }

    return bAccessible;
}
#endif

int main( int argc, char** argv )
{
    freopen( "/dev/null", "w", stderr );

    setup_program_dir( argv[0] );

    // Don't start if accessiblity is enabled or report server is not given

    if ( setup_version() )
    {
        /*long pid =*/ setup_commandline_arguments( argc, argv, &g_signal );

        if ( g_bLoadReport )
        {
            load_crash_data();
        }

        if ( g_bSendReport )
        {
            hash_map< string, string > aDialogSettings;

            read_settings( aDialogSettings );
            read_settings_from_environment( aDialogSettings );

            send_crash_report( aDialogSettings );
        }
        else
        {
            hash_map< string, string > aDialogSettings;

            read_settings( aDialogSettings );
            read_settings_from_environment( aDialogSettings );

            write_crash_data();
            write_report( aDialogSettings );

            string  sPreviewFile = get_home_dir();
            sPreviewFile += "/";
            sPreviewFile += string(PRVFILE);

            FILE *fpout = fopen( sPreviewFile.c_str(), "w+" );
            if ( fpout )
            {
                FILE *fpin = fopen( g_szReportFile, "r" );
                if ( fpin )
                {
                    fcopy( fpout, fpin );
                    fclose( fpin );
                }
                fclose( fpout );
            }

            unlink( g_szReportFile );
        }

        if ( g_bLoadReport )
        {
            unlink( g_strXMLFileName.c_str() );
            unlink( g_strChecksumFileName.c_str() );
        }

        unlink( g_szStackFile );

        return 0;
    }

    return -1;
}
