bug 378581 - merge platform implementations of crash reporter client. Patch by Dave Camp <dcamp@mozilla.com>, r=me

This commit is contained in:
ted.mielczarek%gmail.com 2007-07-25 01:06:41 +00:00
Родитель 4284fd49bb
Коммит 7c3fed2a85
9 изменённых файлов: 781 добавлений и 511 удалений

Просмотреть файл

@ -53,8 +53,10 @@ DIST_FILES = crashreporter.ini
LOCAL_INCLUDES = -I$(srcdir)/../airbag/src
CPPSRCS = crashreporter.cpp
ifeq ($(OS_ARCH),WINNT)
CPPSRCS = crashreporter_win.cpp
CPPSRCS += crashreporter_win.cpp
LIBS += \
$(DEPTH)/toolkit/airbag/airbag/src/client/windows/sender/$(LIB_PREFIX)crash_report_sender_s.$(LIB_SUFFIX) \
$(DEPTH)/toolkit/airbag/airbag/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX) \

Просмотреть файл

@ -0,0 +1,278 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Toolkit Crash Reporter
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2006-2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
* Dave Camp <dcamp@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "crashreporter.h"
#include <fstream>
#include <sstream>
using std::string;
using std::istream;
using std::ifstream;
using std::istringstream;
using std::ostream;
using std::ofstream;
StringTable gStrings;
int gArgc;
const char** gArgv;
static string gSendURL;
static string gDumpFile;
static string gExtraFile;
static string gSettingsPath;
static bool gDeleteDump = true;
static string kExtraDataExtension = ".extra";
static bool ReadStrings(istream &in, StringTable &strings)
{
string currentSection;
while (!in.eof()) {
string line;
std::getline(in, line);
int sep = line.find('=');
if (sep >= 0) {
string key, value;
key = line.substr(0, sep);
value = line.substr(sep + 1);
strings[key] = value;
}
}
return true;
}
static bool ReadStringsFromFile(const string& path,
StringTable& strings)
{
ifstream f(path.c_str(), std::ios::in);
if (!f.is_open()) return false;
return ReadStrings(f, strings);
}
static bool ReadConfig()
{
string iniPath;
if (!UIGetIniPath(iniPath))
return false;
if (!ReadStringsFromFile(iniPath, gStrings))
return false;
gSendURL = gStrings["URL"];
string deleteSetting = gStrings["Delete"];
gDeleteDump = deleteSetting.empty() || atoi(deleteSetting.c_str()) != 0;
return true;
}
static string GetExtraDataFilename(const string& dumpfile)
{
string filename(dumpfile);
int dot = filename.rfind('.');
if (dot < 0)
return "";
filename.replace(dot, filename.length() - dot, kExtraDataExtension);
return filename;
}
static string Basename(const string& file)
{
int slashIndex = file.rfind(UI_DIR_SEPARATOR);
if (slashIndex >= 0)
return file.substr(slashIndex + 1);
else
return file;
}
static bool MoveCrashData(const string& toDir,
string& dumpfile,
string& extrafile)
{
if (!UIEnsurePathExists(toDir)) {
UIError(toDir.c_str());
return false;
}
string newDump = toDir + UI_DIR_SEPARATOR + Basename(dumpfile);
string newExtra = toDir + UI_DIR_SEPARATOR + Basename(extrafile);
if (!UIMoveFile(dumpfile, newDump) ||
!UIMoveFile(extrafile, newExtra)) {
UIError(dumpfile.c_str());
UIError(newDump.c_str());
return false;
}
dumpfile = newDump;
extrafile = newExtra;
return true;
}
static bool AddSubmittedReport(const string& serverResponse)
{
StringTable responseItems;
istringstream in(serverResponse);
ReadStrings(in, responseItems);
if (responseItems.find("CrashID") == responseItems.end())
return false;
string submittedDir =
gSettingsPath + UI_DIR_SEPARATOR + "submitted";
if (!UIEnsurePathExists(submittedDir)) {
return false;
}
string path = submittedDir + UI_DIR_SEPARATOR +
responseItems["CrashID"] + ".txt";
ofstream file(path.c_str());
if (!file.is_open())
return false;
char buf[1024];
UI_SNPRINTF(buf, 1024,
gStrings["CrashID"].c_str(),
responseItems["CrashID"].c_str());
file << buf << "\n";
if (responseItems.find("ViewURL") != responseItems.end()) {
UI_SNPRINTF(buf, 1024,
gStrings["ViewURL"].c_str(),
responseItems["ViewURL"].c_str());
file << buf << "\n";
}
file.close();
return true;
}
bool CrashReporterSendCompleted(bool success,
const string& serverResponse)
{
if (success) {
if (gDeleteDump) {
if (!gDumpFile.empty())
UIDeleteFile(gDumpFile);
if (!gExtraFile.empty())
UIDeleteFile(gExtraFile);
}
return AddSubmittedReport(serverResponse);
}
return true;
}
int main(int argc, const char** argv)
{
gArgc = argc;
gArgv = argv;
if (!ReadConfig()) {
UIError("Couldn't read configuration");
return 0;
}
if (!UIInit())
return 0;
if (argc > 1) {
gDumpFile = argv[1];
}
if (gDumpFile.empty()) {
// no dump file specified, run the default UI
UIShowDefaultUI();
} else {
gExtraFile = GetExtraDataFilename(gDumpFile);
if (gExtraFile.empty()) {
UIError("Couldn't get extra data filename");
return 0;
}
StringTable queryParameters;
if (!ReadStringsFromFile(gExtraFile, queryParameters)) {
UIError("Couldn't read extra data");
return 0;
}
if (queryParameters.find("ProductName") == queryParameters.end()) {
UIError("No product name specified");
return 0;
}
string product = queryParameters["ProductName"];
string vendor = queryParameters["Vendor"];
if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
UIError("Couldn't get settings path");
return 0;
}
string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
if (!MoveCrashData(pendingDir, gDumpFile, gExtraFile)) {
UIError("Couldn't move crash data");
return 0;
}
UIShowCrashUI(gDumpFile, queryParameters, gSendURL);
}
UIShutdown();
return 0;
}
#if defined(XP_WIN) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is unused
// if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
{
// Do the real work.
return main(__argc, (const char**)__argv);
}
#endif

Просмотреть файл

@ -0,0 +1,74 @@
#ifndef CRASHREPORTER_H__
#define CRASHREPORTER_H__
#include <string>
#include <map>
#include <stdlib.h>
#include <stdio.h>
#if defined(XP_WIN32)
#include <windows.h>
#define UI_SNPRINTF _snprintf
#define UI_DIR_SEPARATOR "\\"
#else
#define UI_SNPRINTF snprintf
#define UI_DIR_SEPARATOR "/"
#endif
typedef std::map<std::string, std::string> StringTable;
#define ST_OK "Ok"
#define ST_CANCEL "Cancel"
#define ST_SEND "Send"
#define ST_DONTSEND "DontSend"
#define ST_CLOSE "Close"
#define ST_CRASHREPORTERTITLE "CrashReporterTitle"
#define ST_CRASHREPORTERDESCRIPTION "CrashReporterDescription"
#define ST_RADIOENABLE "RadioEnable"
#define ST_RADIODISABLE "RadioDisable"
#define ST_SENDTITLE "SendTitle"
#define ST_SUBMITSUCCESS "SubmitSuccess"
#define ST_SUBMITFAILED "SubmitFailed"
//=============================================================================
// implemented in crashreporter.cpp
//=============================================================================
extern StringTable gStrings;
extern int gArgc;
extern const char** gArgv;
// The UI finished sending the report
bool CrashReporterSendCompleted(bool success,
const std::string& serverResponse);
//=============================================================================
// implemented in the platform-specific files
//=============================================================================
bool UIInit();
void UIShutdown();
// Run the UI for when the app was launched without a dump file
void UIShowDefaultUI();
// Run the UI for when the app was launched with a dump file
void UIShowCrashUI(const std::string& dumpfile,
const StringTable& queryParameters,
const std::string& sendURL);
void UIError(const std::string& message);
bool UIGetIniPath(std::string& path);
bool UIGetSettingsPath(const std::string& vendor,
const std::string& product,
std::string& settingsPath);
bool UIEnsurePathExists(const std::string& path);
bool UIMoveFile(const std::string& oldfile, const std::string& newfile);
bool UIDeleteFile(const std::string& oldfile);
#endif

Просмотреть файл

@ -41,34 +41,48 @@
#include <Cocoa/Cocoa.h>
#include "HTTPMultipartUpload.h"
#include "crashreporter.h"
@interface CrashReporterUI : NSObject
{
/* Enabled view */
IBOutlet NSView *enableView;
IBOutlet NSWindow* window;
IBOutlet NSTextField *descriptionLabel;
IBOutlet NSButton *disableReportingButton;
IBOutlet NSButton *dontSendButton;
IBOutlet NSButton *sendButton;
/* Enabled view */
IBOutlet NSView* enableView;
IBOutlet NSTextField* descriptionLabel;
IBOutlet NSButton* disableReportingButton;
IBOutlet NSButton* dontSendButton;
IBOutlet NSButton* sendButton;
/* Upload progress view */
IBOutlet NSView *uploadingView;
IBOutlet NSView* uploadingView;
IBOutlet NSTextField *progressLabel;
IBOutlet NSProgressIndicator *progressBar;
IBOutlet NSButton *closeButton;
IBOutlet NSTextField* progressLabel;
IBOutlet NSProgressIndicator* progressBar;
IBOutlet NSButton* closeButton;
/* Error view */
IBOutlet NSView* errorView;
IBOutlet NSTextField* errorLabel;
IBOutlet NSButton* errorCloseButton;
HTTPMultipartUpload *mPost;
}
- (void)showDefaultUI;
- (void)showCrashUI:(const std::string&)dumpfile
queryParameters:(const StringTable&)queryParameters
sendURL:(const std::string&)sendURL;
- (void)showErrorUI:(const std::string&)dumpfile;
- (IBAction)closeClicked:(id)sender;
- (IBAction)sendClicked:(id)sender;
- (void)setView:(NSWindow *)w newView: (NSView *)v animate: (BOOL) animate;
- (void)setupPost;
- (bool)setupPost;
- (void)uploadThread:(id)post;
- (void)uploadComplete:(id)error;
- (void)uploadComplete:(id)data;
@end

Просмотреть файл

@ -37,91 +37,79 @@
* ***** END LICENSE BLOCK ***** */
#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#include "crashreporter.h"
#include "crashreporter_osx.h"
#include <string>
#include <map>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
using std::string;
using std::map;
using std::ifstream;
typedef map<string,string> StringTable;
typedef map<string,StringTable> StringTableSections;
static NSAutoreleasePool* gMainPool;
static CrashReporterUI* gUI = 0;
static string gDumpFile;
static StringTable gQueryParameters;
static string gSendURL;
static string kExtraDataExtension = ".extra";
static string gMinidumpPath;
static string gExtraDataPath;
static StringTableSections gStrings;
static StringTable gExtraData;
static BOOL gSendFailed;
#define NSSTR(s) [NSString stringWithUTF8String:(s).c_str()]
static NSString *
Str(const char *aName, const char *aSection="Strings")
Str(const char* aName)
{
string str = gStrings[aSection][aName];
string str = gStrings[aName];
if (str.empty()) str = "?";
return [NSString stringWithUTF8String:str.c_str()];
}
static bool
ReadStrings(const string &aPath, StringTableSections *aSections)
{
StringTableSections &sections = *aSections;
ifstream f(aPath.c_str());
if (!f.is_open()) return false;
string currentSection;
while (!f.eof()) {
string line;
std::getline(f, line);
if (line[0] == ';') continue;
if (line[0] == '[') {
int close = line.find(']');
if (close >= 0)
currentSection = line.substr(1, close - 1);
continue;
}
int sep = line.find('=');
if (sep >= 0)
sections[currentSection][line.substr(0, sep)] = line.substr(sep + 1);
}
return f.eof();
return NSSTR(str);
}
@implementation CrashReporterUI
-(void)awakeFromNib
{
NSWindow *w = [descriptionLabel window];
[w center];
gUI = self;
[window center];
[w setTitle:[[NSBundle mainBundle]
[window setTitle:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleName"]];
[descriptionLabel setStringValue:Str("CrashReporterDescription")];
[disableReportingButton setTitle:Str("RadioDisable")];
[descriptionLabel setStringValue:Str(ST_CRASHREPORTERDESCRIPTION)];
[disableReportingButton setTitle:Str(ST_RADIODISABLE)];
if (!gMinidumpPath.empty()) {
[sendButton setTitle:Str("Send")];
[sendButton setKeyEquivalent:@"\r"];
[dontSendButton setTitle:Str("DontSend")];
} else {
[dontSendButton setFrame:[sendButton frame]];
[dontSendButton setTitle:Str("Close")];
[dontSendButton setKeyEquivalent:@"\r"];
[sendButton removeFromSuperview];
}
[closeButton setTitle:Str(ST_CLOSE)];
[errorCloseButton setTitle:Str(ST_CLOSE)];
}
[closeButton setTitle:Str("Close")];
-(void)showDefaultUI
{
[dontSendButton setFrame:[sendButton frame]];
[dontSendButton setTitle:Str(ST_CLOSE)];
[dontSendButton setKeyEquivalent:@"\r"];
[sendButton removeFromSuperview];
[w makeKeyAndOrderFront:nil];
[window makeKeyAndOrderFront:nil];
}
-(void)showCrashUI:(const string&)dumpfile
queryParameters:(const StringTable&)queryParameters
sendURL:(const string&)sendURL
{
gDumpFile = dumpfile;
gQueryParameters = queryParameters;
gSendURL = sendURL;
[sendButton setTitle:Str(ST_SEND)];
[sendButton setKeyEquivalent:@"\r"];
[dontSendButton setTitle:Str(ST_DONTSEND)];
[window makeKeyAndOrderFront:nil];
}
-(void)showErrorUI:(const string&)message
{
[errorLabel setStringValue: NSSTR(message)];
[self setView: window newView: errorView animate: NO];
[window makeKeyAndOrderFront:nil];
}
-(IBAction)closeClicked:(id)sender
@ -131,29 +119,24 @@ ReadStrings(const string &aPath, StringTableSections *aSections)
-(IBAction)sendClicked:(id)sender
{
NSWindow *w = [descriptionLabel window];
[self setView: window newView: uploadingView animate: YES];
[progressBar startAnimation: self];
[progressLabel setStringValue:Str("SendTitle")];
[progressLabel setStringValue:Str(ST_SENDTITLE)];
[self setupPost];
if (![self setupPost])
[NSApp terminate];
if (mPost) {
[self setView: w newView: uploadingView animate: YES];
[progressBar startAnimation: self];
[NSThread detachNewThreadSelector:@selector(uploadThread:)
toTarget:self
withObject:nil];
}
[NSThread detachNewThreadSelector:@selector(uploadThread:)
toTarget:self
withObject:mPost];
}
-(void)setView:(NSWindow *)w newView: (NSView *)v animate: (BOOL)animate
-(void)setView:(NSWindow*)w newView: (NSView*)v animate: (BOOL)animate
{
NSRect frame = [w frame];
NSRect oldViewFrame = [[w contentView] frame];
NSRect newViewFrame = [uploadingView frame];
NSRect newViewFrame = [v frame];
frame.origin.y += oldViewFrame.size.height - newViewFrame.size.height;
frame.size.height -= oldViewFrame.size.height - newViewFrame.size.height;
@ -165,109 +148,181 @@ ReadStrings(const string &aPath, StringTableSections *aSections)
[w setFrame: frame display: true animate: animate];
}
-(void)setupPost
-(bool)setupPost
{
NSURL *url = [NSURL URLWithString:Str("URL", "Settings")];
if (!url) return;
NSURL* url = [NSURL URLWithString:NSSTR(gSendURL)];
if (!url) return false;
mPost = [[HTTPMultipartUpload alloc] initWithURL: url];
if (!mPost) return;
if (!mPost) return false;
NSMutableDictionary *parameters =
[[NSMutableDictionary alloc] initWithCapacity: gExtraData.size()];
NSMutableDictionary* parameters =
[[NSMutableDictionary alloc] initWithCapacity: gQueryParameters.size()];
if (!parameters) return false;
StringTable::const_iterator end = gExtraData.end();
for (StringTable::const_iterator i = gExtraData.begin(); i != end; i++) {
NSString *key = [NSString stringWithUTF8String: i->first.c_str()];
NSString *value = [NSString stringWithUTF8String: i->second.c_str()];
StringTable::const_iterator end = gQueryParameters.end();
for (StringTable::const_iterator i = gQueryParameters.begin();
i != end;
i++) {
NSString* key = NSSTR(i->first);
NSString* value = NSSTR(i->second);
[parameters setObject: value forKey: key];
}
[mPost addFileAtPath: NSSTR(gDumpFile) name: @"upload_file_minidump"];
[mPost setParameters: parameters];
[mPost addFileAtPath: [NSString stringWithUTF8String: gMinidumpPath.c_str()]
name: @"upload_file_minidump"];
return true;
}
-(void)uploadComplete:(id)error
-(void)uploadComplete:(id)data
{
[progressBar stopAnimation: self];
NSHTTPURLResponse *response = [mPost response];
NSHTTPURLResponse* response = [mPost response];
NSString *status;
if (error || !response || [response statusCode] != 200) {
status = Str("SubmitFailed");
gSendFailed = YES;
} else
status = Str("SubmitSuccess");
NSString* status;
bool success;
string reply;
if (!data || !response || [response statusCode] != 200) {
status = Str(ST_SUBMITFAILED);
success = false;
reply = "";
} else {
status = Str(ST_SUBMITSUCCESS);
success = true;
NSString* encodingName = [response textEncodingName];
NSStringEncoding encoding;
if (encodingName) {
encoding = CFStringConvertEncodingToNSStringEncoding(
CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName));
} else {
encoding = NSISOLatin1StringEncoding;
}
NSString* r = [[NSString alloc] initWithData: data encoding: encoding];
reply = [r UTF8String];
}
[progressLabel setStringValue: status];
[closeButton setEnabled: true];
[closeButton setKeyEquivalent:@"\r"];
CrashReporterSendCompleted(success, reply);
}
-(void)uploadThread:(id)post
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
[mPost send: &error];
NSAutoreleasePool* autoreleasepool = [[NSAutoreleasePool alloc] init];
NSError* error = nil;
NSData* data = [post send: &error];
if (error)
data = nil;
[self performSelectorOnMainThread: @selector(uploadComplete:)
withObject: error
waitUntilDone: nil];
withObject: data
waitUntilDone: YES];
[autoreleasepool release];
}
@end
string
GetExtraDataFilename(const string& dumpfile)
{
string filename(dumpfile);
int dot = filename.rfind('.');
if (dot < 0)
return "";
/* === Crashreporter UI Functions === */
filename.replace(dot, filename.length() - dot, kExtraDataExtension);
return filename;
bool UIInit()
{
gMainPool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
[NSBundle loadNibNamed:@"MainMenu" owner:NSApp];
return true;
}
int
main(int argc, char *argv[])
void UIShutdown()
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
string iniPath(argv[0]);
iniPath.append(".ini");
if (!ReadStrings(iniPath, &gStrings)) {
printf("couldn't read strings\n");
return -1;
}
if (argc > 1) {
gMinidumpPath = argv[1];
gExtraDataPath = GetExtraDataFilename(gMinidumpPath);
if (!gExtraDataPath.empty()) {
StringTableSections table;
ReadStrings(gExtraDataPath, &table);
gExtraData = table[""];
}
}
gSendFailed = NO;
int ret = NSApplicationMain(argc, (const char **) argv);
string deleteSetting = gStrings["Settings"]["Delete"];
if (!gSendFailed &&
(deleteSetting.empty() || atoi(deleteSetting.c_str()) > 0)) {
remove(gMinidumpPath.c_str());
if (!gExtraDataPath.empty())
remove(gExtraDataPath.c_str());
}
[autoreleasepool release];
return ret;
[gMainPool release];
}
void UIShowDefaultUI()
{
[gUI showDefaultUI];
[NSApp run];
}
void UIShowCrashUI(const string& dumpfile,
const StringTable& queryParameters,
const string& sendURL)
{
[gUI showCrashUI: dumpfile
queryParameters: queryParameters
sendURL: sendURL];
[NSApp run];
}
void UIError(const string& message)
{
if (!gUI) {
// UI failed to initialize, printing is the best we can do
printf("Error: %s\n", message.c_str());
return;
}
[gUI showErrorUI: message];
[NSApp run];
}
bool UIGetIniPath(string& path)
{
path = gArgv[0];
path.append(".ini");
return true;
}
bool UIGetSettingsPath(const string& vendor,
const string& product,
string& settingsPath)
{
NSArray* paths;
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
NSUserDomainMask,
YES);
if ([paths count] < 1)
return false;
NSString* destPath = [paths objectAtIndex:0];
// Note that MacOS ignores the vendor when creating the profile hierarchy -
// all application preferences directories live alongside one another in
// ~/Library/Application Support/
destPath = [destPath stringByAppendingPathComponent: NSSTR(product)];
destPath = [destPath stringByAppendingPathComponent: @"Crash Reports"];
settingsPath = [destPath UTF8String];
if (!UIEnsurePathExists(settingsPath))
return false;
return true;
}
bool UIEnsurePathExists(const string& path)
{
int ret = mkdir(path.c_str(), S_IRWXU);
int e = errno;
if (ret == -1 && e != EEXIST)
return false;
return true;
}
bool UIMoveFile(const string& file, const string& newfile)
{
return (rename(file.c_str(), newfile.c_str()) != -1);
}
bool UIDeleteFile(const string& file)
{
return (unlink(file.c_str()) != -1);
}

Просмотреть файл

@ -39,6 +39,8 @@
#undef WIN32_LEAN_AND_MEAN
#endif
#include "crashreporter.h"
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
@ -48,7 +50,6 @@
#include "resource.h"
#include "client/windows/sender/crash_report_sender.h"
#include "common/windows/string_utils-inl.h"
#include <fstream>
#define CRASH_REPORTER_KEY L"Software\\Mozilla\\Crash Reporter"
#define CRASH_REPORTER_VALUE L"Enabled"
@ -58,68 +59,25 @@
using std::string;
using std::wstring;
using std::map;
using std::ifstream;
using std::ofstream;
bool ReadConfig();
BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam,
LPARAM lParam);
BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam,
LPARAM lParam);
HANDLE CreateSendThread(HWND hDlg, LPCTSTR dumpFile);
bool CheckCrashReporterEnabled(bool* enabled);
void SetCrashReporterEnabled(bool enabled);
bool SendCrashReport(wstring dumpFile,
const map<wstring,wstring>* query_parameters,
wstring* server_response);
DWORD WINAPI SendThreadProc(LPVOID param);
static wstring UTF8ToWide(const string& utf8, bool *success = 0);
static string WideToUTF8(const wstring& wide, bool *success = 0);
static DWORD WINAPI SendThreadProc(LPVOID param);
typedef struct {
HWND hDlg;
wstring dumpFile;
const map<wstring,wstring>* query_parameters;
const StringTable* query_parameters;
wstring send_url;
wstring* server_response;
} SENDTHREADDATA;
TCHAR sendURL[2048] = L"\0";
bool deleteDump = true;
static wstring Str(const char* key)
{
return UTF8ToWide(gStrings[key]);
}
// Sort of a hack to get l10n
enum {
ST_OK,
ST_CANCEL,
ST_CRASHREPORTERTITLE,
ST_CRASHREPORTERDESCRIPTION,
ST_RADIOENABLE,
ST_RADIODISABLE,
ST_SENDTITLE,
ST_SUBMITSUCCESS,
ST_SUBMITFAILED,
ST_CRASHID,
ST_CRASHDETAILSURL,
NUM_STRINGS
};
LPCTSTR stringNames[] = {
L"Ok",
L"Cancel",
L"CrashReporterTitle",
L"CrashReporterDescription",
L"RadioEnable",
L"RadioDisable",
L"SendTitle",
L"SubmitSuccess",
L"SubmitFailed",
L"CrashID",
L"CrashDetailsURL"
};
LPTSTR strings[NUM_STRINGS];
const wchar_t* kExtraDataExtension = L".extra";
void DoInitCommonControls()
static void DoInitCommonControls()
{
INITCOMMONCONTROLSEX ic;
ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
@ -129,77 +87,15 @@ void DoInitCommonControls()
LoadLibrary(L"riched20.dll");
}
bool LoadStrings(LPCTSTR fileName)
{
for (int i=ST_OK; i<NUM_STRINGS; i++) {
strings[i] = new TCHAR[1024];
GetPrivateProfileString(L"Strings", stringNames[i], L"", strings[i], 1024, fileName);
if (stringNames[i][0] == '\0')
return false;
//XXX should probably check for strings > 1024...
}
return true;
}
bool GetSettingsPath(wstring vendor, wstring product,
wstring& settings_path)
{
wchar_t path[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(NULL,
CSIDL_APPDATA,
NULL,
0,
path))) {
if (!vendor.empty()) {
PathAppend(path, vendor.c_str());
}
PathAppend(path, product.c_str());
PathAppend(path, L"Crash Reports");
// in case it doesn't exist
CreateDirectory(path, NULL);
settings_path = path;
return true;
}
return false;
}
bool EnsurePathExists(wstring base_path, wstring sub_path)
{
wstring path = base_path + L"\\" + sub_path;
return CreateDirectory(path.c_str(), NULL) != 0;
}
bool ReadConfig()
{
TCHAR fileName[MAX_PATH];
if (GetModuleFileName(NULL, fileName, MAX_PATH)) {
// get crashreporter ini
LPTSTR s = wcsrchr(fileName, '.');
if (s) {
wcscpy(s, L".ini");
GetPrivateProfileString(L"Settings", L"URL", L"", sendURL, 2048, fileName);
TCHAR tmp[16];
GetPrivateProfileString(L"Settings", L"Delete", L"1", tmp, 16, fileName);
deleteDump = _wtoi(tmp) > 0;
return LoadStrings(fileName);
}
}
return false;
}
BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
static BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:
SetWindowText(hwndDlg, strings[ST_CRASHREPORTERTITLE]);
SetDlgItemText(hwndDlg, IDOK, strings[ST_OK]);
SetDlgItemText(hwndDlg, IDC_RADIOENABLE, strings[ST_RADIOENABLE]);
SetDlgItemText(hwndDlg, IDC_RADIODISABLE, strings[ST_RADIODISABLE]);
SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, strings[ST_CRASHREPORTERDESCRIPTION]);
SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
SetDlgItemText(hwndDlg, IDC_RADIOENABLE, Str(ST_RADIOENABLE).c_str());
SetDlgItemText(hwndDlg, IDC_RADIODISABLE, Str(ST_RADIODISABLE).c_str());
SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, Str(ST_CRASHREPORTERDESCRIPTION).c_str());
SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
SetFocus(GetDlgItem(hwndDlg, IDC_RADIOENABLE));
CheckRadioButton(hwndDlg, IDC_RADIOENABLE, IDC_RADIODISABLE, lParam ? IDC_RADIOENABLE : IDC_RADIODISABLE);
@ -218,7 +114,7 @@ BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM
}
}
bool GetRegValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
static bool GetRegValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
{
DWORD type, dataSize;
dataSize = sizeof(DWORD);
@ -229,7 +125,7 @@ bool GetRegValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
return false;
}
bool CheckCrashReporterEnabled(bool* enabled)
static bool CheckCrashReporterEnabled(bool* enabled)
{
*enabled = false;
bool found = false;
@ -263,7 +159,7 @@ bool CheckCrashReporterEnabled(bool* enabled)
return found;
}
void SetCrashReporterEnabled(bool enabled)
static void SetCrashReporterEnabled(bool enabled)
{
HKEY hRegKey;
if (RegCreateKey(HKEY_CURRENT_USER, CRASH_REPORTER_KEY, &hRegKey) == ERROR_SUCCESS) {
@ -273,7 +169,7 @@ void SetCrashReporterEnabled(bool enabled)
}
}
BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
static BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static bool finishedOk = false;
static HANDLE hThread = NULL;
@ -282,8 +178,8 @@ BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM l
case WM_INITDIALOG:
{
//init strings
SetWindowText(hwndDlg, strings[ST_SENDTITLE]);
SetDlgItemText(hwndDlg, IDCANCEL, strings[ST_CANCEL]);
SetWindowText(hwndDlg, Str(ST_SENDTITLE).c_str());
SetDlgItemText(hwndDlg, IDCANCEL, Str(ST_CANCEL).c_str());
// init progressmeter
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0);
@ -299,10 +195,16 @@ BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM l
finishedOk = (wParam == 1);
if (finishedOk) {
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 100, 0);
MessageBox(hwndDlg, strings[ST_SUBMITSUCCESS], strings[ST_CRASHREPORTERTITLE], MB_OK | MB_ICONINFORMATION);
MessageBox(hwndDlg,
Str(ST_SUBMITSUCCESS).c_str(),
Str(ST_CRASHREPORTERTITLE).c_str(),
MB_OK | MB_ICONINFORMATION);
}
else {
MessageBox(hwndDlg, strings[ST_SUBMITFAILED], strings[ST_CRASHREPORTERTITLE], MB_OK | MB_ICONERROR);
MessageBox(hwndDlg,
Str(ST_SUBMITFAILED).c_str(),
Str(ST_CRASHREPORTERTITLE).c_str(),
MB_OK | MB_ICONERROR);
}
EndDialog(hwndDlg, finishedOk ? 1 : 0);
return TRUE;
@ -318,14 +220,16 @@ BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM l
}
}
bool SendCrashReport(wstring dumpFile,
const map<wstring,wstring>* query_parameters,
wstring* server_response)
static bool SendCrashReport(wstring dumpFile,
const StringTable* query_parameters,
wstring send_url,
wstring* server_response)
{
SENDTHREADDATA td;
td.hDlg = NULL;
td.dumpFile = dumpFile;
td.query_parameters = query_parameters;
td.send_url = send_url;
td.server_response = server_response;
int res = (int)DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
@ -334,20 +238,26 @@ bool SendCrashReport(wstring dumpFile,
return (res >= 0);
}
DWORD WINAPI SendThreadProc(LPVOID param)
static DWORD WINAPI SendThreadProc(LPVOID param)
{
bool finishedOk;
SENDTHREADDATA* td = (SENDTHREADDATA*)param;
wstring url(sendURL);
if (url.empty()) {
if (td->send_url.empty()) {
finishedOk = false;
}
else {
map<wstring,wstring>query_parameters;
StringTable::const_iterator end = td->query_parameters->end();
for (StringTable::const_iterator i = td->query_parameters->begin();
i != end;
i++) {
query_parameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
}
finishedOk = (google_breakpad::CrashReportSender
::SendCrashReport(url,
*(td->query_parameters),
::SendCrashReport(td->send_url,
query_parameters,
td->dumpFile,
td->server_response)
== google_breakpad::RESULT_SUCCEEDED);
@ -357,243 +267,173 @@ DWORD WINAPI SendThreadProc(LPVOID param)
return 0;
}
bool ConvertUTF8ToWide(const char* utf8_string, wstring& ucs2_string)
static wstring UTF8ToWide(const string& utf8, bool *success)
{
wchar_t* buffer = NULL;
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8_string,
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
-1, NULL, 0);
if(buffer_size == 0)
return false;
if(buffer_size == 0) {
if (success)
*success = false;
return L"";
}
buffer = new wchar_t[buffer_size];
if(buffer == NULL)
return false;
MultiByteToWideChar(CP_UTF8, 0, utf8_string,
if(buffer == NULL) {
if (success)
*success = false;
return L"";
}
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
-1, buffer, buffer_size);
ucs2_string = buffer;
wstring str = buffer;
delete [] buffer;
return true;
if (success)
*success = true;
return str;
}
bool ConvertWideToUTF8(const wchar_t* ucs2_string, string& utf8_string)
static string WideToUTF8(const wstring& wide, bool *success)
{
char* buffer = NULL;
int buffer_size = WideCharToMultiByte(CP_UTF8, 0, ucs2_string,
int buffer_size = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
-1, NULL, 0, NULL, NULL);
if(buffer_size == 0)
return false;
if(buffer_size == 0) {
if (success)
*success = false;
return "";
}
buffer = new char[buffer_size];
if(buffer == NULL)
return false;
WideCharToMultiByte(CP_UTF8, 0, ucs2_string,
if(buffer == NULL) {
if (success)
*success = false;
return "";
}
WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
-1, buffer, buffer_size, NULL, NULL);
utf8_string = buffer;
string utf8 = buffer;
delete [] buffer;
return true;
if (success)
*success = true;
return utf8;
}
void ReadKeyValuePairs(const wstring& data,
map<wstring, wstring>& values)
/* === Crashreporter UI Functions === */
bool UIInit()
{
if (data.empty())
return;
string line;
int line_begin = 0;
int line_end = data.find('\n');
while(line_end >= 0) {
int pos = data.find('=', line_begin);
if (pos >= 0 && pos < line_end) {
wstring key, value;
key = data.substr(line_begin, pos - line_begin);
value = data.substr(pos + 1, line_end - pos - 1);
values[key] = value;
}
line_begin = line_end + 1;
line_end = data.find('\n', line_begin);
}
}
bool ReadExtraData(const wstring& filename,
map<wstring, wstring>& query_parameters)
{
#if _MSC_VER >= 1400 // MSVC 2005/8
ifstream file;
file.open(filename.c_str(), std::ios::in);
#else // _MSC_VER >= 1400
ifstream file(_wfopen(filename.c_str(), L"rb"));
#endif // _MSC_VER >= 1400
if (!file.is_open())
return false;
wstring data;
char* buf;
file.seekg(0, std::ios::end);
int file_size = file.tellg();
file.seekg(0, std::ios::beg);
buf = new char[file_size+1];
if (!buf)
return false;
file.read(buf, file_size);
buf[file_size] ='\0';
ConvertUTF8ToWide(buf, data);
delete buf;
ReadKeyValuePairs(data, query_parameters);
file.close();
return true;
}
wstring GetExtraDataFilename(const wstring& dumpfile)
{
wstring filename(dumpfile);
int dot = filename.rfind('.');
if (dot < 0)
return L"";
filename.replace(dot, filename.length() - dot, kExtraDataExtension);
return filename;
}
bool AddSubmittedReport(wstring settings_path, wstring server_response)
{
map<wstring, wstring> response_items;
ReadKeyValuePairs(server_response, response_items);
if (response_items.find(L"CrashID") == response_items.end())
return false;
wstring submitted_path = settings_path + L"\\submitted\\" +
response_items[L"CrashID"] + L".txt";
#if _MSC_VER >= 1400 // MSVC 2005/8
ofstream file;
file.open(submitted_path.c_str(), std::ios::out);
#else // _MSC_VER >= 1400
ofstream file(_wfopen(submitted_path.c_str(), L"wb"));
#endif // _MSC_VER >= 1400
if (!file.is_open())
return false;
wchar_t buf[1024];
wsprintf(buf, strings[ST_CRASHID], response_items[L"CrashID"].c_str());
wstring data = buf;
data += L"\n";
if (response_items.find(L"ViewURL") != response_items.end()) {
wsprintf(buf, strings[ST_CRASHDETAILSURL],
response_items[L"ViewURL"].c_str());
data += buf;
data += L"\n";
}
string utf8_data;
if (!ConvertWideToUTF8(data.c_str(), utf8_data))
return false;
file << utf8_data;
file.close();
return true;
}
int main(int argc, char **argv)
{
map<wstring,wstring> query_parameters;
DoInitCommonControls();
if (!ReadConfig()) {
MessageBox(NULL, L"Missing crashreporter.ini file", L"Crash Reporter Error", MB_OK | MB_ICONSTOP);
return 0;
}
return true;
}
wstring dumpfile;
bool enabled = false;
void UIShutdown()
{
}
if (argc > 1) {
if (!ConvertUTF8ToWide(argv[1], dumpfile))
return 0;
}
void UIShowDefaultUI()
{
bool enabled;
// no dump file specified, just ask about enabling
if (!CheckCrashReporterEnabled(&enabled))
enabled = true;
if (dumpfile.empty()) {
// no dump file specified, just ask about enabling
if (!CheckCrashReporterEnabled(&enabled))
enabled = true;
enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)enabled));
SetCrashReporterEnabled(enabled);
}
enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)enabled));
void UIShowCrashUI(const string& dumpfile,
const StringTable& query_parameters,
const string& send_url)
{
bool enabled;
if (!CheckCrashReporterEnabled(&enabled)) {
//ask user if crash reporter should be enabled
enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)true));
SetCrashReporterEnabled(enabled);
}
else {
wstring extrafile = GetExtraDataFilename(dumpfile);
if (!extrafile.empty()) {
ReadExtraData(extrafile, query_parameters);
}
else {
//XXX: print error message
return 0;
}
if (query_parameters.find(L"ProductName") == query_parameters.end()) {
//XXX: print error message
return 0;
}
wstring product = query_parameters[L"ProductName"];
wstring vendor;
if (query_parameters.find(L"Vendor") != query_parameters.end()) {
vendor = query_parameters[L"Vendor"];
}
wstring settings_path;
if(!GetSettingsPath(vendor, product, settings_path)) {
//XXX: print error message
return 0;
}
EnsurePathExists(settings_path, L"pending");
EnsurePathExists(settings_path, L"submitted");
// now we move the crash report and extra data to pending
wstring newfile = settings_path + L"\\pending\\" +
google_breakpad::WindowsStringUtils::GetBaseName(dumpfile);
MoveFile(dumpfile.c_str(), newfile.c_str());
dumpfile = newfile;
newfile = settings_path + L"\\pending\\" +
google_breakpad::WindowsStringUtils::GetBaseName(extrafile);
MoveFile(extrafile.c_str(), newfile.c_str());
extrafile = newfile;
if (!CheckCrashReporterEnabled(&enabled)) {
//ask user if crash reporter should be enabled
enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)true));
SetCrashReporterEnabled(enabled);
}
// if enabled, send crash report
if (enabled) {
wstring server_response;
if (SendCrashReport(dumpfile, &query_parameters, &server_response)) {
if (deleteDump) {
DeleteFile(dumpfile.c_str());
if (!extrafile.empty())
DeleteFile(extrafile.c_str());
}
AddSubmittedReport(settings_path, server_response);
}
//TODO: show details?
}
// if enabled, send crash report
if (enabled) {
wstring server_response;
bool success = SendCrashReport(UTF8ToWide(dumpfile),
&query_parameters,
UTF8ToWide(send_url),
&server_response);
CrashReporterSendCompleted(success, WideToUTF8(server_response));
}
}
#if defined(XP_WIN) && !defined(__GNUC__)
// We need WinMain in order to not be a console app. This function is unused
// if we are a console application.
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
void UIError(const string& message)
{
// Do the real work.
return main(__argc, __argv);
wstring title = Str(ST_CRASHREPORTERTITLE);
if (title.empty())
title = L"Crash Reporter Error";
MessageBox(NULL, UTF8ToWide(message).c_str(), title.c_str(),
MB_OK | MB_ICONSTOP);
}
bool UIGetIniPath(string& path)
{
wchar_t fileName[MAX_PATH];
if (GetModuleFileName(NULL, fileName, MAX_PATH)) {
// get crashreporter ini
wchar_t* s = wcsrchr(fileName, '.');
if (s) {
wcscpy(s, L".ini");
path = WideToUTF8(fileName);
return true;
}
}
return false;
}
bool UIGetSettingsPath(const string& vendor,
const string& product,
string& settings_path)
{
wchar_t path[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(NULL,
CSIDL_APPDATA,
NULL,
0,
path))) {
if (!vendor.empty()) {
PathAppend(path, UTF8ToWide(vendor).c_str());
}
PathAppend(path, UTF8ToWide(product).c_str());
PathAppend(path, L"Crash Reports");
// in case it doesn't exist
CreateDirectory(path, NULL);
settings_path = WideToUTF8(path);
return true;
}
return false;
}
bool UIEnsurePathExists(const string& path)
{
if (CreateDirectory(UTF8ToWide(path).c_str(), NULL) == 0) {
if (GetLastError() != ERROR_ALREADY_EXISTS)
return false;
}
return true;
}
bool UIMoveFile(const string& oldfile, const string& newfile)
{
return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str());
}
bool UIDeleteFile(const string& oldfile)
{
return DeleteFile(UTF8ToWide(oldfile).c_str());
}
#endif

Просмотреть файл

@ -10,10 +10,14 @@
disableReportingButton = NSButton;
dontSendButton = NSButton;
enableView = NSView;
errorCloseButton = NSButton;
errorLabel = NSTextField;
errorView = NSView;
progressBar = NSProgressIndicator;
progressLabel = NSTextField;
sendButton = NSButton;
uploadingView = NSView;
window = NSWindow;
};
SUPERCLASS = NSObject;
},

Просмотреть файл

@ -7,11 +7,13 @@
<key>IBEditorPositions</key>
<dict>
<key>282</key>
<string>445 520 550 163 0 0 1440 878 </string>
<string>24 681 550 163 0 0 1440 878 </string>
<key>29</key>
<string>447 315 137 44 0 0 1440 878 </string>
<key>356</key>
<string>643 213 551 213 0 0 1440 878 </string>
<key>386</key>
<string>23 537 456 145 0 0 1440 878 </string>
</dict>
<key>IBFramework Version</key>
<string>446.1</string>
@ -24,9 +26,10 @@
<integer>2</integer>
<key>IBOpenObjects</key>
<array>
<integer>21</integer>
<integer>29</integer>
<integer>386</integer>
<integer>282</integer>
<integer>29</integer>
<integer>21</integer>
</array>
<key>IBSystem Version</key>
<string>8P2137</string>

Двоичный файл не отображается.