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