xref: /trunk/main/crashrep/source/unx/main.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 #include <cstdio>
28 #include <stdlib.h>
29 #include <sys/utsname.h>
30 #include <_version.h>
31 #include <errno.h>
32 #include <string>
33 #include <string.h>
34 #include <assert.h>
35 
36 #include <sys/socket.h>
37 #include <netdb.h>
38 #include <unistd.h>
39 #include <pwd.h>
40 #include <pthread.h>
41 #include <limits.h>
42 
43 #include <hash_map>
44 #include <vector>
45 #include <string>
46 
47 #if defined (LINUX) || (FREEBSD)
48 #include <netinet/in.h>
49 #endif
50 
51 typedef int SOCKET;
52 
53 #define closesocket     close
54 #define SOCKET_ERROR    -1
55 
56 #ifdef SOLARIS
57 const char *basename( const char *filename )
58 {
59     const char *pSlash = strrchr( filename, '/' );
60 
61     return pSlash ? pSlash + 1 : pSlash;
62 }
63 #endif
64 
65 using namespace std;
66 
67 static bool g_bNoUI = false;
68 static bool g_bSendReport = false;
69 static bool g_bLoadReport = false;
70 
71 static bool g_bDebugMode = false;
72 static int  g_signal = 0;
73 
74 static string g_strProductKey;
75 static string g_strReportServer;
76 static unsigned short g_uReportPort = 80;
77 static string g_buildid;
78 static string g_strDefaultLanguage;
79 static string g_strXMLFileName;
80 static string g_strPStackFileName;
81 static string g_strChecksumFileName;
82 static string g_strProgramDir;
83 
84 static char g_szStackFile[L_tmpnam] = "";
85 static char g_szDescriptionFile[2048] = "";
86 static char g_szReportFile[2048] = "";
87 
88 #define SO_CRASHREPORT_MAIL "so-report@sun.com"
89 #define PSTACK_CMD          "pstack %d"
90 
91 #ifdef LINUX
92 #define PMAP_CMD            "cat /proc/%d/maps"
93 #else
94 #define PMAP_CMD            "pmap %d"
95 #endif
96 
97 #define REPORT_SERVER   (g_strReportServer.c_str())
98 #define REPORT_PORT     g_uReportPort
99 
100 static string getprogramdir()
101 {
102     return g_strProgramDir;
103 }
104 
105 static const char *getlocale()
106 {
107     const char * locale = getenv( "LC_ALL" );
108 
109     if( NULL == locale )
110         locale = getenv( "LC_CTYPE" );
111 
112     if( NULL == locale )
113         locale = getenv( "LANG" );
114 
115     if( NULL == locale )
116         locale = "C";
117 
118     return locale;
119 }
120 
121 static const char *get_home_dir()
122 {
123     struct passwd *ppwd = getpwuid( getuid() );
124 
125     return ppwd ? (ppwd->pw_dir ? ppwd->pw_dir : "/") : "/";
126 }
127 
128 static string trim_string( const string& rString )
129 {
130     string temp = rString;
131 
132     while ( temp.length() && (temp[0] == ' ' || temp[0] == '\t') )
133         temp.erase( 0, 1 );
134 
135     string::size_type   len = temp.length();
136 
137     while ( len && (temp[len-1] == ' ' || temp[len-1] == '\t') )
138     {
139         temp.erase( len - 1, 1 );
140         len = temp.length();
141     }
142 
143     return temp;
144 }
145 
146 static string xml_encode( const string &rString )
147 {
148     string temp = rString;
149     string::size_type pos = 0;
150 
151     // First replace all occurences of '&' because it may occur in further
152     // encoded chardters too
153 
154     for( pos = 0; (pos = temp.find( '&', pos )) != string::npos; pos += 4 )
155         temp.replace( pos, 1, "&amp;" );
156 
157     for( pos = 0; (pos = temp.find( '<', pos )) != string::npos; pos += 4 )
158         temp.replace( pos, 1, "&lt;" );
159 
160     for( pos = 0; (pos = temp.find( '>', pos )) != string::npos; pos += 4 )
161         temp.replace( pos, 1, "&gt;" );
162 
163     return temp;
164 }
165 
166 static size_t fcopy( FILE *fpout, FILE *fpin )
167 {
168     char buffer[1024];
169     size_t nBytes;
170     size_t nBytesWritten = 0;
171 
172     while ( 0 != (nBytes = fread( buffer, 1, sizeof(buffer), fpin )) )
173     {
174         nBytesWritten += fwrite( buffer, 1, nBytes, fpout );
175     }
176 
177     return nBytesWritten;
178 }
179 
180 /*
181    writes the report to a temp-file
182    from which it can be reviewed and sent
183 */
184 
185 bool write_report( const hash_map< string, string >& rSettings )
186 {
187     FILE    *fp = fopen( tmpnam( g_szReportFile ), "w" );
188     const char *pszUserType = getenv( "STAROFFICE_USERTYPE" );
189 
190     fprintf( fp,
191        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
192        "<!DOCTYPE errormail:errormail PUBLIC \"-//OpenOffice.org//DTD ErrorMail 1.0//EN\" \"errormail.dtd\">\n"
193        "<errormail:errormail xmlns:errormail=\"http://openoffice.org/2002/errormail\" usertype=\"%s\">\n"
194        "<reportmail:mail xmlns:reportmail=\"http://openoffice.org/2002/reportmail\" version=\"1.1\" feedback=\"%s\" email=\"%s\">\n"
195        "<reportmail:title>%s</reportmail:title>\n"
196        "<reportmail:attachment name=\"description.txt\" media-type=\"text/plain\" class=\"UserComment\"/>\n"
197        "<reportmail:attachment name=\"stack.txt\" media-type=\"text/plain\" class=\"pstack output\"/>\n"
198        "</reportmail:mail>\n"
199        "<officeinfo:officeinfo xmlns:officeinfo=\"http://openoffice.org/2002/officeinfo\" build=\"%s\" platform=\"%s\" language=\"%s\" exceptiontype=\"%d\" product=\"%s\" procpath=\"%s\"/>\n"
200        ,
201        pszUserType ? xml_encode( pszUserType ).c_str() : "",
202        xml_encode(rSettings.find( "CONTACT" )->second).c_str(),
203        xml_encode(rSettings.find( "EMAIL" )->second).c_str(),
204        xml_encode(rSettings.find( "TITLE" )->second).c_str(),
205        g_buildid.length() ? xml_encode( g_buildid ).c_str() : "unknown",
206        _INPATH,
207        g_strDefaultLanguage.c_str(),
208        g_signal,
209        g_strProductKey.length() ? xml_encode(g_strProductKey).c_str() : "unknown",
210        xml_encode(getprogramdir()).c_str()
211        );
212 
213     struct utsname  info;
214 
215     memset( &info, 0, sizeof(info) );
216     uname( &info );
217 
218     fprintf( fp,
219        "<systeminfo:systeminfo xmlns:systeminfo=\"http://openoffice.org/2002/systeminfo\">\n"
220        "<systeminfo:System name=\"%s\" version=\"%s\" build=\"%s\" locale=\"%s\"/>\n"
221        ,
222        xml_encode( info.sysname ).c_str(),
223        xml_encode( info.version ).c_str(),
224        xml_encode( info.release ).c_str(),
225        xml_encode( getlocale() ).c_str()
226        );
227     fprintf( fp, "<systeminfo:CPU type=\"%s\"/>\n", xml_encode( info.machine ).c_str() );
228     fprintf( fp, "</systeminfo:systeminfo>\n" );
229 
230     FILE *fpxml = fopen( g_strXMLFileName.c_str(), "r" );
231     if ( fpxml )
232     {
233         fcopy( fp, fpxml );
234         fclose( fpxml );
235     }
236 
237     FILE *fpchk = fopen( g_strChecksumFileName.c_str(), "r" );
238     if ( fpchk )
239     {
240         fcopy( fp, fpchk );
241         fclose( fpchk );
242     }
243 
244     fprintf( fp, "</errormail:errormail>\n" );
245 
246     fclose( fp );
247 
248     return true;
249 }
250 
251 
252 bool write_description( const hash_map< string, string >& rSettings )
253 {
254     bool    bSuccess = false;
255     FILE    *fp = fopen( tmpnam( g_szDescriptionFile ), "w" );
256 
257     if ( fp )
258     {
259         bSuccess = true;
260         fprintf( fp, "\xEF\xBB\xBF" );
261         fprintf( fp, "%s\n", rSettings.find( "DESCRIPTION" )->second.c_str() );
262         fclose( fp );
263     }
264 
265     return bSuccess;
266 }
267 
268 #if 0
269 // unused
270 static void printSettings( const hash_map<string,string>& rSettings )
271 {
272     printf( "Settings:\n" );
273     for( hash_map<string,string>::const_iterator it = rSettings.begin(); it != rSettings.end(); ++it )
274     {
275         printf( "%s=\"%s\"\n", it->first.c_str(), it->second.c_str() );
276     }
277 }
278 #endif
279 
280 bool save_crash_report( const string& rFileName, const hash_map< string, string >& /*rSettings*/ )
281 {
282     bool bSuccess = false;
283     FILE    *fpout = fopen( rFileName.c_str(), "w" );
284 
285     if ( fpout )
286     {
287         FILE *fpin = fopen( g_szStackFile, "r" );
288 
289         if ( fpin )
290         {
291             char    buf[1024];
292 
293             while (fgets(buf, sizeof(buf), fpin) != NULL)
294             {
295                 fputs(buf, fpout);
296             }
297 
298             bSuccess = true;
299 
300             fclose ( fpin );
301         }
302 
303         fclose( fpout );
304     }
305 
306     return bSuccess;
307 }
308 
309 bool SendHTTPRequest(
310                 FILE *fp,
311                 const char *pszServer,
312                 unsigned short uPort = 80,
313                 const char *pszProxyServer = NULL,
314                 unsigned short uProxyPort = 8080 )
315 {
316     bool success = false;
317 
318     struct hostent *hp;
319 
320     if ( pszProxyServer )
321         hp = gethostbyname( pszProxyServer );
322     else
323         hp = gethostbyname( pszServer );
324 
325     if ( hp )
326     {
327         SOCKET  s = socket( AF_INET, SOCK_STREAM, 0 );
328 
329         if ( s )
330         {
331             struct sockaddr_in address;
332 
333             memcpy(&(address.sin_addr.s_addr), *(hp->h_addr_list),sizeof(struct in_addr));
334             address.sin_family = AF_INET;
335 
336             if ( pszProxyServer )
337                 address.sin_port = ntohs( uProxyPort );
338             else
339                 address.sin_port = ntohs( uPort );
340 
341             if ( 0 == connect( s, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) )
342             {
343                 fseek( fp, 0, SEEK_END );
344                 size_t length = ftell( fp );
345                 fseek( fp, 0, SEEK_SET );
346 
347                 char buffer[2048];
348 
349                 if ( pszProxyServer )
350                     sprintf( buffer,
351                     "POST http://%s:%d/soap/servlet/rpcrouter HTTP/1.0\r\n"
352                         "Content-Type: text/xml; charset=\"utf-8\"\r\n"
353                         "Content-Length: %d\r\n"
354                         "SOAPAction: \"\"\r\n\r\n",
355                         pszServer,
356                         uPort,
357                         static_cast<int>(length)
358                         );
359                 else
360                     sprintf( buffer,
361                         "POST /soap/servlet/rpcrouter HTTP/1.0\r\n"
362                         "Content-Type: text/xml; charset=\"utf-8\"\r\n"
363                         "Content-Length: %d\r\n"
364                         "SOAPAction: \"\"\r\n\r\n",
365                         static_cast<int>(length)
366                         );
367 
368                 if ( g_bDebugMode )
369                 {
370                     printf( "*** Sending HTTP request ***\n\n" );
371                     printf( buffer );
372                 }
373 
374                 if ( SOCKET_ERROR != send( s, buffer, strlen(buffer), 0 ) )
375                 {
376                     size_t nBytes;
377 
378                     do
379                     {
380                         nBytes = fread( buffer, 1, sizeof(buffer), fp );
381 
382                         if ( nBytes )
383                         {
384                             if ( g_bDebugMode )
385                                 fwrite( buffer, 1, nBytes, stdout );
386                             success = SOCKET_ERROR != send( s, buffer, nBytes, 0 );
387                         }
388                     } while( nBytes && success );
389 
390                     if ( success )
391                     {
392                         if ( g_bDebugMode )
393                             printf( "*** Receiving HTTP response ***\n\n" );
394 
395                         memset( buffer, 0, sizeof(buffer) );
396                         success = SOCKET_ERROR != recv( s, buffer, sizeof(buffer), 0 );
397                         if ( success )
398                         {
399                             char szHTTPSignature[sizeof(buffer)] = "";
400                             unsigned uHTTPReturnCode = 0;
401 
402                             sscanf( buffer, "%s %d ", szHTTPSignature, &uHTTPReturnCode );
403                             success = uHTTPReturnCode == 200;
404                         }
405                         if ( g_bDebugMode )
406                             do
407                             {
408                                 printf( buffer );
409                                 memset( buffer, 0, sizeof(buffer) );
410                             } while ( 0 < recv( s, buffer, sizeof(buffer), 0 ) );
411                     }
412                 }
413 
414             }
415 
416             closesocket( s );
417         }
418     }
419 
420     return success;
421 }
422 
423 static void WriteSOAPRequest( FILE *fp )
424 {
425     fprintf( fp,
426         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
427         "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
428         "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"\n"
429         "xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"\n"
430         "xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"\n"
431         "xmlns:rds=\"urn:ReportDataService\"\n"
432         "xmlns:apache=\"http://xml.apache.org/xml-soap\"\n"
433         "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
434         "<SOAP-ENV:Body>\n"
435         );
436 
437     fprintf( fp, "<rds:submitReport>\n"  );
438     fprintf( fp, "<body xsi:type=\"xsd:string\">This is an autogenerated crash report mail.</body>\n" );
439     fprintf( fp, "<hash xsi:type=\"apache:Map\">\n" );
440 
441     FILE    *fpin = fopen( g_szReportFile, "r" );
442     if ( fpin )
443     {
444         fprintf( fp,
445             "<item>\n"
446             "<key xsi:type=\"xsd:string\">reportmail.xml</key>\n"
447             "<value xsi:type=\"xsd:string\"><![CDATA[" );
448         fcopy( fp, fpin );
449         fprintf( fp, "]]></value></item>\n" );
450         fclose( fpin );
451     }
452 
453     fpin = fopen( g_szDescriptionFile, "r" );
454     if ( fpin )
455     {
456         fprintf( fp,
457             "<item>\n"
458             "<key xsi:type=\"xsd:string\">description.txt</key>\n"
459             "<value xsi:type=\"xsd:string\"><![CDATA[" );
460         fcopy( fp, fpin );
461         fprintf( fp, "]]></value></item>\n" );
462         fclose( fpin );
463     };
464 
465     fpin = fopen( g_szStackFile, "r" );
466     if ( fpin )
467     {
468         fprintf( fp,
469             "<item>\n"
470             "<key xsi:type=\"xsd:string\">stack.txt</key>\n"
471             "<value xsi:type=\"xsd:string\"><![CDATA[" );
472         fcopy( fp, fpin );
473         fprintf( fp, "]]></value></item>\n" );
474         fclose( fpin );
475     };
476 
477     fprintf( fp,
478         "</hash>\n"
479         "</rds:submitReport>\n"
480         "</SOAP-ENV:Body>\n"
481         "</SOAP-ENV:Envelope>\n"
482         );
483 }
484 
485 struct RequestParams
486 {
487     bool    success;
488     FILE    *fpin;
489     const char *pServer;
490     unsigned short uPort;
491     const char *pProxyServer;
492     unsigned short uProxyPort;
493 };
494 
495 
496 bool send_crash_report( const hash_map< string, string >& rSettings )
497 {
498     if ( 0 == strcasecmp( rSettings.find( "CONTACT" )->second.c_str(), "true" ) &&
499          !trim_string(rSettings.find( "EMAIL" )->second).length() )
500     {
501         return false;
502     }
503 
504     char *endptr = NULL;
505 
506     const char *pProxyServer = rSettings.find( "SERVER" )->second.c_str();
507     unsigned short uProxyPort = (unsigned short)strtoul( rSettings.find( "PORT" )->second.c_str(), &endptr, 10 );
508 
509     bool bUseProxy = !strcasecmp( "true", rSettings.find( "USEPROXY" )->second.c_str() );
510 
511 
512     write_description( rSettings );
513     write_report( rSettings );
514 
515     bool bSuccess = false;
516 
517     FILE    *fptemp = tmpfile();
518     if ( fptemp )
519     {
520         WriteSOAPRequest( fptemp );
521         fseek( fptemp, 0, SEEK_SET );
522 
523         bSuccess = SendHTTPRequest(
524             fptemp,
525             REPORT_SERVER, REPORT_PORT,
526             bUseProxy ? pProxyServer : NULL,
527             uProxyPort ? uProxyPort : 8080
528             );
529 
530         fclose( fptemp );
531 
532     }
533 
534     unlink( g_szDescriptionFile );
535     unlink( g_szReportFile );
536 
537     return bSuccess;
538 }
539 
540 
541 static bool append_file( const char *filename, string& rString )
542 {
543     char    buf[1024];
544     bool    bSuccess = false;
545 
546     FILE *fp = fopen( filename, "r" );
547     if ( fp )
548     {
549         bSuccess = true;
550         while (fgets(buf, sizeof(buf), fp) != NULL)
551         {
552             rString.append( buf );
553         }
554         fclose( fp );
555     }
556 
557     return true;
558 }
559 
560 string crash_get_details( const hash_map< string, string >& rSettings )
561 {
562     string aRet;
563 
564     write_description( rSettings );
565     write_report( rSettings );
566 
567     aRet.append( rSettings.find( "TITLE" )->second.c_str() );
568     aRet.append( "\n\n" );
569     append_file( g_szDescriptionFile, aRet );
570     aRet.append( "\n\n-------\n\n" );
571     append_file( g_szReportFile, aRet );
572     aRet.append( "\n\n-------\n\n" );
573     append_file( g_szStackFile, aRet );
574 
575     unlink( g_szDescriptionFile );
576     unlink( g_szReportFile );
577 
578     return aRet;
579 }
580 
581 
582 // ensure validity of program relative paths
583 static void setup_program_dir( const char* progname )
584 {
585     char    szCanonicProgPath[PATH_MAX];
586 
587 
588     if ( realpath( progname, szCanonicProgPath ) )
589     {
590         string aDir = szCanonicProgPath;
591 
592         size_t pos = aDir.rfind( '/' );
593         // FIXME: search PATH if necessary
594         assert( pos != string::npos );
595 
596         g_strProgramDir = aDir.substr( 0, pos + 1 );
597         aDir.erase( pos );
598         chdir( aDir.c_str() );
599     }
600 }
601 
602 //*************************************************************************
603 
604 static long setup_commandline_arguments( int argc, char** argv, int *pSignal )
605 {
606     long    pid = 0;
607     int     signal = 0;
608 
609     for ( int n = 1; n < argc; n++ )
610     {
611         if ( 0 == strcmp( argv[n], "-p" ) )
612         {
613             if ( ++n < argc )
614                 pid = strtol( argv[n], NULL, 0 );
615         }
616         else if ( 0 == strcmp( argv[n], "-s" ) )
617         {
618             if ( ++n < argc )
619                 signal = strtol( argv[n], NULL, 0 );
620         }
621         else if ( 0 == strcmp( argv[n], "-debug" ) )
622         {
623             g_bDebugMode = true;
624         }
625         else if ( 0 == strcmp( argv[n], "-xml" ) )
626         {
627             if ( ++n < argc )
628                 g_strXMLFileName = argv[n];
629         }
630         else if ( 0 == strcmp( argv[n], "-stack" ) )
631         {
632             if ( ++n < argc )
633                 g_strPStackFileName = argv[n];
634         }
635         else if ( 0 == strcmp( argv[n], "-chksum" ) )
636         {
637             if ( ++n < argc )
638                 g_strChecksumFileName = argv[n];
639         }
640         else if ( 0 == strcmp( argv[n], "-noui" ) )
641         {
642             g_bNoUI = true;
643         }
644         else if ( 0 == strcmp( argv[n], "-send" ) )
645         {
646             g_bSendReport = true;
647         }
648         else if ( 0 == strcmp( argv[n], "-load" ) )
649         {
650             g_bLoadReport = true;
651         }
652         else if ( argv[n] && strlen(argv[n]) )
653         {
654             printf(
655                 "\n%s crash_report %s\n\n" \
656                 "/?, -h[elp]          %s\n\n" \
657                 "%-20s %s\n\n",
658                 "%MSG_CMDLINE_USAGE%",
659                 "%MSG_PARAM_PROCESSID%",
660                 "%MSG_PARAM_HELP_DESCRIPTION%",
661                 "%MSG_PARAM_PROCESSID%",
662                 "%MSG_PARAM_PROCESSID_DESCRIPTION%"
663                 );
664             break;
665         }
666     }
667 
668     *pSignal = signal;
669 
670     return pid;
671 }
672 
673 //*************************************************************************
674 
675 static bool read_line( FILE *fp, string& rLine )
676 {
677     char szBuffer[1024];
678     bool bSuccess = false;
679     bool bEOL = false;
680     string  line;
681 
682 
683     while ( !bEOL && fgets( szBuffer, sizeof(szBuffer), fp ) )
684     {
685         int len = strlen(szBuffer);
686 
687         bSuccess = true;
688 
689         while ( len && szBuffer[len - 1] == '\n' )
690         {
691             szBuffer[--len] = 0;
692             bEOL = true;
693         }
694 
695         line.append( szBuffer );
696     }
697 
698     rLine = line;
699     return bSuccess;
700 }
701 
702 static string get_script_string( const char *pFileName, const char *pKeyName )
703 {
704     FILE    *fp = fopen( pFileName, "r" );
705     string  retValue;
706 
707     if ( fp )
708     {
709         string line;
710         string section;
711 
712         while ( read_line( fp, line ) )
713         {
714             line = trim_string( line );
715 
716 
717             string::size_type iEqualSign = line.find( '=', 0 );
718 
719             if ( iEqualSign != string::npos )
720             {
721                 string  keyname = line.substr( 0, iEqualSign );
722                 keyname = trim_string( keyname );
723 
724                 string  value = line.substr( iEqualSign + 1, string::npos );
725                 value = trim_string( value );
726 
727                 if ( value.length() && '\"' == value[0] )
728                 {
729                     value.erase( 0, 1 );
730 
731                     string::size_type iQuotes = value.find( '"', 0 );
732 
733                     if ( iQuotes != string::npos )
734                         value.erase( iQuotes );
735                 }
736 
737                 if ( 0 == strcasecmp( keyname.c_str(), pKeyName ) )
738                 {
739                     retValue = value;
740                     break;
741                 }
742             }
743         }
744 
745         fclose( fp );
746     }
747 
748     return retValue;
749 }
750 
751 static string get_profile_string( const char *pFileName, const char *pSectionName, const char *pKeyName, const char *pDefault = NULL )
752 {
753     FILE    *fp = fopen( pFileName, "r" );
754     string  retValue = pDefault ? pDefault : "";
755 
756     if ( fp )
757     {
758         string line;
759         string section;
760 
761         while ( read_line( fp, line ) )
762         {
763             line = trim_string( line );
764 
765             if ( line.length() && line[0] == '[' )
766             {
767                 line.erase( 0, 1 );
768                 string::size_type end = line.find( ']', 0 );
769 
770                 if ( string::npos != end )
771                     section = trim_string( line.substr( 0, end ) );
772             }
773             else
774             {
775 
776                 string::size_type iEqualSign = line.find( '=', 0 );
777 
778                 if ( iEqualSign != string::npos )
779                 {
780                     string  keyname = line.substr( 0, iEqualSign );
781                     keyname = trim_string( keyname );
782 
783                     string  value = line.substr( iEqualSign + 1, string::npos );
784                     value = trim_string( value );
785 
786                     if (
787                         0 == strcasecmp( section.c_str(), pSectionName ) &&
788                         0 == strcasecmp( keyname.c_str(), pKeyName )
789                          )
790                     {
791                         retValue = value;
792                         break;
793                     }
794                 }
795             }
796         }
797 
798         fclose( fp );
799     }
800 
801     return retValue;
802 }
803 
804 static string get_environment_string( const char *pEnvName )
805 {
806     const char *pEnvValue = getenv( pEnvName );
807 
808     if ( pEnvValue )
809         return pEnvValue;
810     else
811         return "";
812 }
813 
814 static string read_from_file( const string& rFileName )
815 {
816     string  content;
817     FILE *fp = fopen( rFileName.c_str(), "r" );
818 
819     if ( fp )
820     {
821         char    buffer[256 + 1];
822         size_t  nBytesRead;
823 
824         while( 0 != ( nBytesRead = fread( buffer, 1, sizeof(buffer) - 1,  fp ) ) )
825         {
826             buffer[nBytesRead] = 0;
827             content += buffer;
828         }
829 
830         fclose( fp );
831     }
832 
833     return content;
834 }
835 
836 #define RCFILE ".crash_reportrc"
837 #define XMLFILE ".crash_report_frames"
838 #define CHKFILE ".crash_report_checksum"
839 #define LCKFILE ".crash_report_unsent"
840 #define PRVFILE ".crash_report_preview"
841 
842 static void load_crash_data()
843 {
844     g_strXMLFileName = get_home_dir();
845     g_strXMLFileName += "/";
846     g_strXMLFileName += string(XMLFILE);
847 
848     g_strChecksumFileName = get_home_dir();
849     g_strChecksumFileName += "/";
850     g_strChecksumFileName += string(CHKFILE);
851 }
852 
853 static bool write_crash_data()
854 {
855     bool success = true;
856     string  sFile = get_home_dir();
857 
858     sFile += "/";
859     sFile += string(XMLFILE);
860 
861     FILE *fp = fopen( sFile.c_str(), "w" );
862 
863     if ( fp )
864     {
865         FILE    *fpin = fopen( g_strXMLFileName.c_str(), "r" );
866 
867         if ( fpin )
868         {
869             fcopy( fp, fpin );
870             fclose( fpin );
871         }
872 
873         fclose( fp );
874     }
875 
876     sFile = get_home_dir();
877 
878     sFile += "/";
879     sFile += string(CHKFILE);
880 
881     fp = fopen( sFile.c_str(), "w" );
882 
883     if ( fp )
884     {
885         FILE    *fpin = fopen( g_strChecksumFileName.c_str(), "r" );
886 
887         if ( fpin )
888         {
889             fcopy( fp, fpin );
890             fclose( fpin );
891         }
892 
893         fclose( fp );
894     }
895 
896     sFile = get_home_dir();
897 
898     sFile += "/";
899     sFile += string(LCKFILE);
900 
901     fp = fopen( sFile.c_str(), "w" );
902 
903     if ( fp )
904     {
905         fprintf( fp, "Unsent\n" );
906         fclose( fp );
907     }
908 
909     return success;
910 }
911 
912 #if 0
913 // unused
914 static bool write_settings( const hash_map< string, string >& rSettings )
915 {
916     bool success = false;
917     string  sRCFile = get_home_dir();
918 
919     sRCFile += "/";
920     sRCFile += string(RCFILE);
921 
922     FILE *fp = fopen( sRCFile.c_str(), "w" );
923 
924     if ( fp )
925     {
926         fprintf( fp, "[Options]\n" );
927         fprintf( fp, "UseProxy=%s\n", rSettings.find( "USEPROXY" )->second.c_str() );
928         fprintf( fp, "ProxyServer=%s\n", rSettings.find( "SERVER" )->second.c_str() );
929         fprintf( fp, "ProxyPort=%s\n", rSettings.find( "PORT" )->second.c_str() );
930         fprintf( fp, "ReturnAddress=%s\n", rSettings.find( "EMAIL" )->second.c_str() );
931         fprintf( fp, "AllowContact=%s\n", rSettings.find( "CONTACT" )->second.c_str() );
932         fclose( fp );
933     }
934 
935     return success;
936 }
937 #endif
938 
939 static void read_settings( hash_map< string, string >& rSettings )
940 {
941     string  sRCFile = get_home_dir();
942 
943     sRCFile += "/";
944     sRCFile += string(RCFILE);
945 
946     rSettings[ "EMAIL" ] = get_profile_string( sRCFile.c_str(), "Options", "ReturnAddress" );
947     rSettings[ "SERVER" ] = get_profile_string( sRCFile.c_str(), "Options", "ProxyServer" );
948     rSettings[ "PORT" ] = get_profile_string( sRCFile.c_str(), "Options", "ProxyPort" );
949     rSettings[ "USEPROXY" ] = get_profile_string( sRCFile.c_str(), "Options", "UseProxy" );
950     rSettings[ "CONTACT" ] = get_profile_string( sRCFile.c_str(), "Options", "AllowContact" );
951     rSettings[ "DESCRIPTION" ] = "";
952     rSettings[ "TITLE" ] = "";
953 }
954 
955 static void read_settings_from_environment( hash_map< string, string >& rSettings )
956 {
957     string  strEnv;
958 
959     strEnv = get_environment_string( "ERRORREPORT_RETURNADDRESS" );
960     if ( strEnv.length() )
961     {
962         rSettings[ "EMAIL" ] = strEnv;
963         if ( !(rSettings.find( "CONTACT" )->second).length() )
964             rSettings[ "CONTACT" ] = "true";
965     }
966     else if ( !(rSettings.find( "CONTACT" )->second).length() )
967         rSettings[ "CONTACT" ] = "false";
968 
969 
970     strEnv = get_environment_string( "ERRORREPORT_HTTPPROXYSERVER" );
971     if ( strEnv.length() )
972         rSettings[ "SERVER" ] = strEnv;
973 
974     strEnv = get_environment_string( "ERRORREPORT_HTTPPROXYPORT" );
975     if ( strEnv.length() )
976         rSettings[ "PORT" ] = strEnv;
977 
978     strEnv = get_environment_string( "ERRORREPORT_HTTPCONNECTIONTYPE" );
979     if ( strEnv.length() )
980         rSettings[ "USEPROXY" ] = 0 == strcasecmp( strEnv.c_str(), "MANUALPROXY" ) ? "true" : "false";
981 
982     strEnv = get_environment_string( "ERRORREPORT_BODYFILE" );
983     if ( strEnv.length() )
984         rSettings[ "DESCRIPTION" ] = read_from_file( strEnv );
985 
986     strEnv = get_environment_string( "ERRORREPORT_SUBJECT" );
987     if ( strEnv.length() )
988         rSettings[ "TITLE" ] = strEnv;
989 }
990 
991 static bool setup_version()
992 {
993     if ( !getenv( "PRODUCTNAME" ) )
994     {
995         string productkey = get_profile_string( "bootstraprc", "Bootstrap", "ProductKey" );
996 
997         g_strProductKey = productkey;
998 
999         if ( productkey.length() )
1000         {
1001             static string productname;
1002             static string productversion;
1003             string::size_type   iSpace = productkey.find( ' ', 0 );
1004 
1005             if ( string::npos != iSpace )
1006             {
1007                 productname = productkey.substr( 0, iSpace );
1008                 productversion = productkey.substr( iSpace + 1, string::npos );
1009             }
1010             else
1011                 productname = productkey;
1012 
1013             productname.insert( 0, "PRODUCTNAME=" );
1014             putenv( (char *)productname.c_str() );
1015 
1016             productversion.insert( 0, "PRODUCTVERSION=" );
1017             putenv( (char *)productversion.c_str() );
1018         }
1019     }
1020 
1021     g_buildid = get_profile_string( "versionrc", "Version", "BuildId" );
1022     g_strDefaultLanguage = get_script_string( "instdb.ins", "DefaultLanguage"  );
1023 
1024     g_strReportServer = get_profile_string( "bootstraprc", "ErrorReport", "ErrorReportServer" );
1025 
1026     string strReportPort = get_profile_string( "bootstraprc", "ErrorReport", "ErrorReportPort", "80" );
1027     char *endptr = NULL;
1028     unsigned short uReportPort = (unsigned short)strtoul( strReportPort.c_str(), &endptr, 10 );
1029     g_uReportPort = uReportPort ? uReportPort : 80;
1030 
1031     return 0 != g_strReportServer.length();
1032 }
1033 
1034 #if 0
1035 // Use gconftool-2 to determine if gnome accessiblity is enabled
1036 // unused
1037 static bool get_accessibility_state()
1038 {
1039     bool bAccessible = false;
1040     FILE *fin = popen( "gconftool-2 -g /desktop/gnome/interface/accessibility", "r");
1041 
1042     if ( fin )
1043     {
1044         char buffer[sizeof("true")];
1045 
1046         bAccessible = fgets( buffer, sizeof(buffer), fin ) && 0 == strcmp( buffer, "true" );
1047 
1048         pclose( fin );
1049     }
1050 
1051     return bAccessible;
1052 }
1053 #endif
1054 
1055 int main( int argc, char** argv )
1056 {
1057     freopen( "/dev/null", "w", stderr );
1058 
1059     setup_program_dir( argv[0] );
1060 
1061     // Don't start if accessiblity is enabled or report server is not given
1062 
1063     if ( setup_version() )
1064     {
1065         /*long pid =*/ setup_commandline_arguments( argc, argv, &g_signal );
1066 
1067         if ( g_bLoadReport )
1068         {
1069             load_crash_data();
1070         }
1071 
1072         if ( g_bSendReport )
1073         {
1074             hash_map< string, string > aDialogSettings;
1075 
1076             read_settings( aDialogSettings );
1077             read_settings_from_environment( aDialogSettings );
1078 
1079             send_crash_report( aDialogSettings );
1080         }
1081         else
1082         {
1083             hash_map< string, string > aDialogSettings;
1084 
1085             read_settings( aDialogSettings );
1086             read_settings_from_environment( aDialogSettings );
1087 
1088             write_crash_data();
1089             write_report( aDialogSettings );
1090 
1091             string  sPreviewFile = get_home_dir();
1092             sPreviewFile += "/";
1093             sPreviewFile += string(PRVFILE);
1094 
1095             FILE *fpout = fopen( sPreviewFile.c_str(), "w+" );
1096             if ( fpout )
1097             {
1098                 FILE *fpin = fopen( g_szReportFile, "r" );
1099                 if ( fpin )
1100                 {
1101                     fcopy( fpout, fpin );
1102                     fclose( fpin );
1103                 }
1104                 fclose( fpout );
1105             }
1106 
1107             unlink( g_szReportFile );
1108         }
1109 
1110         if ( g_bLoadReport )
1111         {
1112             unlink( g_strXMLFileName.c_str() );
1113             unlink( g_strChecksumFileName.c_str() );
1114         }
1115 
1116         unlink( g_szStackFile );
1117 
1118         return 0;
1119     }
1120 
1121     return -1;
1122 }
1123