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 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 96 static string getprogramdir() 97 { 98 return g_strProgramDir; 99 } 100 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 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 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 142 static string xml_encode( const string &rString ) 143 { 144 string temp = rString; 145 string::size_type pos = 0; 146 147 // First replace all occurrences 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, "&" ); 152 153 for( pos = 0; (pos = temp.find( '<', pos )) != string::npos; pos += 4 ) 154 temp.replace( pos, 1, "<" ); 155 156 for( pos = 0; (pos = temp.find( '>', pos )) != string::npos; pos += 4 ) 157 temp.replace( pos, 1, ">" ); 158 159 return temp; 160 } 161 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 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 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 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 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( "%s", 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( "%s", 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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