xref: /aoo41x/main/crashrep/source/unx/main.cxx (revision cdf0e10c)
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