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