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, "&" ); 156 157 for( pos = 0; (pos = temp.find( '<', pos )) != string::npos; pos += 4 ) 158 temp.replace( pos, 1, "<" ); 159 160 for( pos = 0; (pos = temp.find( '>', pos )) != string::npos; pos += 4 ) 161 temp.replace( pos, 1, ">" ); 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