зеркало из https://github.com/mozilla/pjs.git
bug 378581 - merge platform implementations of crash reporter client. Patch by Dave Camp <dcamp@mozilla.com>, r=me
This commit is contained in:
Родитель
4284fd49bb
Коммит
7c3fed2a85
|
@ -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 §ions = *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>
|
||||
|
|
Двоичные данные
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
сгенерированный
Двоичные данные
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
сгенерированный
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче