summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm636
1 files changed, 0 insertions, 636 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm
deleted file mode 100644
index 42a43bfc3..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm
+++ /dev/null
@@ -1,636 +0,0 @@
-// Copyright (c) 2011, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#import <fcntl.h>
-#import <sys/stat.h>
-#include <TargetConditionals.h>
-#import <unistd.h>
-
-#import <SystemConfiguration/SystemConfiguration.h>
-
-#import "common/mac/HTTPMultipartUpload.h"
-
-#import "client/apple/Framework/BreakpadDefines.h"
-#import "client/mac/sender/uploader.h"
-#import "common/mac/GTMLogger.h"
-
-const int kMinidumpFileLengthLimit = 2 * 1024 * 1024; // 2MB
-
-#define kApplePrefsSyncExcludeAllKey \
- @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
-
-NSString *const kGoogleServerType = @"google";
-NSString *const kSocorroServerType = @"socorro";
-NSString *const kDefaultServerType = @"google";
-
-#pragma mark -
-
-namespace {
-// Read one line from the configuration file.
-NSString *readString(int fileId) {
- NSMutableString *str = [NSMutableString stringWithCapacity:32];
- char ch[2] = { 0 };
-
- while (read(fileId, &ch[0], 1) == 1) {
- if (ch[0] == '\n') {
- // Break if this is the first newline after reading some other string
- // data.
- if ([str length])
- break;
- } else {
- [str appendString:[NSString stringWithUTF8String:ch]];
- }
- }
-
- return str;
-}
-
-//=============================================================================
-// Read |length| of binary data from the configuration file. This method will
-// returns |nil| in case of error.
-NSData *readData(int fileId, ssize_t length) {
- NSMutableData *data = [NSMutableData dataWithLength:length];
- char *bytes = (char *)[data bytes];
-
- if (read(fileId, bytes, length) != length)
- return nil;
-
- return data;
-}
-
-//=============================================================================
-// Read the configuration from the config file.
-NSDictionary *readConfigurationData(const char *configFile) {
- int fileId = open(configFile, O_RDONLY, 0600);
- if (fileId == -1) {
- GTMLoggerDebug(@"Couldn't open config file %s - %s",
- configFile,
- strerror(errno));
- }
-
- // we want to avoid a build-up of old config files even if they
- // have been incorrectly written by the framework
- if (unlink(configFile)) {
- GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
- configFile,
- strerror(errno));
- }
-
- if (fileId == -1) {
- return nil;
- }
-
- NSMutableDictionary *config = [NSMutableDictionary dictionary];
-
- while (1) {
- NSString *key = readString(fileId);
-
- if (![key length])
- break;
-
- // Read the data. Try to convert to a UTF-8 string, or just save
- // the data
- NSString *lenStr = readString(fileId);
- ssize_t len = [lenStr intValue];
- NSData *data = readData(fileId, len);
- id value = [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
-
- [config setObject:(value ? value : data) forKey:key];
- [value release];
- }
-
- close(fileId);
- return config;
-}
-} // namespace
-
-#pragma mark -
-
-@interface Uploader(PrivateMethods)
-
-// Update |parameters_| as well as the server parameters using |config|.
-- (void)translateConfigurationData:(NSDictionary *)config;
-
-// Read the minidump referenced in |parameters_| and update |minidumpContents_|
-// with its content.
-- (BOOL)readMinidumpData;
-
-// Read the log files referenced in |parameters_| and update |logFileData_|
-// with their content.
-- (BOOL)readLogFileData;
-
-// Returns a unique client id (user-specific), creating a persistent
-// one in the user defaults, if necessary.
-- (NSString*)clientID;
-
-// Returns a dictionary that can be used to map Breakpad parameter names to
-// URL parameter names.
-- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType;
-
-// Helper method to set HTTP parameters based on server type. This is
-// called right before the upload - crashParameters will contain, on exit,
-// URL parameters that should be sent with the minidump.
-- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters;
-
-// Initialization helper to create dictionaries mapping Breakpad
-// parameters to URL parameters
-- (void)createServerParameterDictionaries;
-
-// Accessor method for the URL parameter dictionary
-- (NSMutableDictionary *)urlParameterDictionary;
-
-// Records the uploaded crash ID to the log file.
-- (void)logUploadWithID:(const char *)uploadID;
-@end
-
-@implementation Uploader
-
-//=============================================================================
-- (id)initWithConfigFile:(const char *)configFile {
- NSDictionary *config = readConfigurationData(configFile);
- if (!config)
- return nil;
-
- return [self initWithConfig:config];
-}
-
-//=============================================================================
-- (id)initWithConfig:(NSDictionary *)config {
- if ((self = [super init])) {
- // Because the reporter is embedded in the framework (and many copies
- // of the framework may exist) its not completely certain that the OS
- // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
- // Info.plist. To make sure, also set the key directly if needed.
- NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
- if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
- [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
- }
-
- [self createServerParameterDictionaries];
-
- [self translateConfigurationData:config];
-
- // Read the minidump into memory.
- [self readMinidumpData];
- [self readLogFileData];
- }
- return self;
-}
-
-//=============================================================================
-+ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile {
- return readConfigurationData([configFile fileSystemRepresentation]);
-}
-
-//=============================================================================
-- (void)translateConfigurationData:(NSDictionary *)config {
- parameters_ = [[NSMutableDictionary alloc] init];
-
- NSEnumerator *it = [config keyEnumerator];
- while (NSString *key = [it nextObject]) {
- // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX
- // that indicates that it should be uploaded to the server along
- // with the minidump, so we treat it specially.
- if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) {
- NSString *urlParameterKey =
- [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]];
- if ([urlParameterKey length]) {
- id value = [config objectForKey:key];
- if ([value isKindOfClass:[NSString class]]) {
- [self addServerParameter:(NSString *)value
- forKey:urlParameterKey];
- } else {
- [self addServerParameter:(NSData *)value
- forKey:urlParameterKey];
- }
- }
- } else {
- [parameters_ setObject:[config objectForKey:key] forKey:key];
- }
- }
-
- // generate a unique client ID based on this host's MAC address
- // then add a key/value pair for it
- NSString *clientID = [self clientID];
- [parameters_ setObject:clientID forKey:@"guid"];
-}
-
-// Per user per machine
-- (NSString *)clientID {
- NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
- NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey];
- if (crashClientID) {
- return crashClientID;
- }
-
- // Otherwise, if we have no client id, generate one!
- srandom((int)[[NSDate date] timeIntervalSince1970]);
- long clientId1 = random();
- long clientId2 = random();
- long clientId3 = random();
- crashClientID = [NSString stringWithFormat:@"%lx%lx%lx",
- clientId1, clientId2, clientId3];
-
- [ud setObject:crashClientID forKey:kClientIdPreferenceKey];
- [ud synchronize];
- return crashClientID;
-}
-
-//=============================================================================
-- (BOOL)readLogFileData {
-#if TARGET_OS_IPHONE
- return NO;
-#else
- unsigned int logFileCounter = 0;
-
- NSString *logPath;
- size_t logFileTailSize =
- [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue];
-
- NSMutableArray *logFilenames; // An array of NSString, one per log file
- logFilenames = [[NSMutableArray alloc] init];
-
- char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX";
- char *tmpDir = mkdtemp(tmpDirTemplate);
-
- // Construct key names for the keys we expect to contain log file paths
- for(logFileCounter = 0;; logFileCounter++) {
- NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
- @BREAKPAD_LOGFILE_KEY_PREFIX,
- logFileCounter];
-
- logPath = [parameters_ objectForKey:logFileKey];
-
- // They should all be consecutive, so if we don't find one, assume
- // we're done
-
- if (!logPath) {
- break;
- }
-
- NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath];
-
- if (entireLogFile == nil) {
- continue;
- }
-
- NSRange fileRange;
-
- // Truncate the log file, only if necessary
-
- if ([entireLogFile length] <= logFileTailSize) {
- fileRange = NSMakeRange(0, [entireLogFile length]);
- } else {
- fileRange = NSMakeRange([entireLogFile length] - logFileTailSize,
- logFileTailSize);
- }
-
- char tmpFilenameTemplate[100];
-
- // Generate a template based on the log filename
- sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir,
- [[logPath lastPathComponent] fileSystemRepresentation]);
-
- char *tmpFile = mktemp(tmpFilenameTemplate);
-
- NSData *logSubdata = [entireLogFile subdataWithRange:fileRange];
- NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile];
- [logSubdata writeToFile:tmpFileString atomically:NO];
-
- [logFilenames addObject:[tmpFileString lastPathComponent]];
- [entireLogFile release];
- }
-
- if ([logFilenames count] == 0) {
- [logFilenames release];
- logFileData_ = nil;
- return NO;
- }
-
- // now, bzip all files into one
- NSTask *tarTask = [[NSTask alloc] init];
-
- [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]];
- [tarTask setLaunchPath:@"/usr/bin/tar"];
-
- NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf",
- @"log.tar.bz2",nil];
- [bzipArgs addObjectsFromArray:logFilenames];
-
- [logFilenames release];
-
- [tarTask setArguments:bzipArgs];
- [tarTask launch];
- [tarTask waitUntilExit];
- [tarTask release];
-
- NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir];
- logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile];
- if (logFileData_ == nil) {
- GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile);
- return NO;
- }
- return YES;
-#endif // TARGET_OS_IPHONE
-}
-
-//=============================================================================
-- (BOOL)readMinidumpData {
- NSString *minidumpDir =
- [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
- NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
-
- if (![minidumpID length])
- return NO;
-
- NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID];
- path = [path stringByAppendingPathExtension:@"dmp"];
-
- // check the size of the minidump and limit it to a reasonable size
- // before attempting to load into memory and upload
- const char *fileName = [path fileSystemRepresentation];
- struct stat fileStatus;
-
- BOOL success = YES;
-
- if (!stat(fileName, &fileStatus)) {
- if (fileStatus.st_size > kMinidumpFileLengthLimit) {
- fprintf(stderr, "Breakpad Uploader: minidump file too large " \
- "to upload : %d\n", (int)fileStatus.st_size);
- success = NO;
- }
- } else {
- fprintf(stderr, "Breakpad Uploader: unable to determine minidump " \
- "file length\n");
- success = NO;
- }
-
- if (success) {
- minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path];
- success = ([minidumpContents_ length] ? YES : NO);
- }
-
- if (!success) {
- // something wrong with the minidump file -- delete it
- unlink(fileName);
- }
-
- return success;
-}
-
-#pragma mark -
-//=============================================================================
-
-- (void)createServerParameterDictionaries {
- serverDictionary_ = [[NSMutableDictionary alloc] init];
- socorroDictionary_ = [[NSMutableDictionary alloc] init];
- googleDictionary_ = [[NSMutableDictionary alloc] init];
- extraServerVars_ = [[NSMutableDictionary alloc] init];
-
- [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType];
- [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType];
-
- [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME];
- [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL];
- [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS];
- [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT];
- [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION];
- [googleDictionary_ setObject:@"guid" forKey:@"guid"];
-
- [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS];
- [socorroDictionary_ setObject:@"CrashTime"
- forKey:@BREAKPAD_PROCESS_CRASH_TIME];
- [socorroDictionary_ setObject:@"StartupTime"
- forKey:@BREAKPAD_PROCESS_START_TIME];
- [socorroDictionary_ setObject:@"Version"
- forKey:@BREAKPAD_VERSION];
- [socorroDictionary_ setObject:@"ProductName"
- forKey:@BREAKPAD_PRODUCT];
- [socorroDictionary_ setObject:@"Email"
- forKey:@BREAKPAD_EMAIL];
-}
-
-- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {
- if (serverType == nil || [serverType length] == 0) {
- return [serverDictionary_ objectForKey:kDefaultServerType];
- }
- return [serverDictionary_ objectForKey:serverType];
-}
-
-- (NSMutableDictionary *)urlParameterDictionary {
- NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE];
- return [self dictionaryForServerType:serverType];
-
-}
-
-- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters {
- NSDictionary *urlParameterNames = [self urlParameterDictionary];
-
- id key;
- NSEnumerator *enumerator = [parameters_ keyEnumerator];
-
- while ((key = [enumerator nextObject])) {
- // The key from parameters_ corresponds to a key in
- // urlParameterNames. The value in parameters_ gets stored in
- // crashParameters with a key that is the value in
- // urlParameterNames.
-
- // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and
- // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP
- // URL parameter becomes [pname => "FOOBAR"].
- NSString *breakpadParameterName = (NSString *)key;
- NSString *urlParameter = [urlParameterNames
- objectForKey:breakpadParameterName];
- if (urlParameter) {
- [crashParameters setObject:[parameters_ objectForKey:key]
- forKey:urlParameter];
- }
- }
-
- // Now, add the parameters that were added by the application.
- enumerator = [extraServerVars_ keyEnumerator];
-
- while ((key = [enumerator nextObject])) {
- NSString *urlParameterName = (NSString *)key;
- NSString *urlParameterValue =
- [extraServerVars_ objectForKey:urlParameterName];
- [crashParameters setObject:urlParameterValue
- forKey:urlParameterName];
- }
- return YES;
-}
-
-- (void)addServerParameter:(id)value forKey:(NSString *)key {
- [extraServerVars_ setObject:value forKey:key];
-}
-
-//=============================================================================
-- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error {
- NSString *result = [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
- const char *reportID = "ERR";
- if (error) {
- fprintf(stderr, "Breakpad Uploader: Send Error: %s\n",
- [[error description] UTF8String]);
- } else {
- NSCharacterSet *trimSet =
- [NSCharacterSet whitespaceAndNewlineCharacterSet];
- reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
- [self logUploadWithID:reportID];
- }
-
- // rename the minidump file according to the id returned from the server
- NSString *minidumpDir =
- [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
- NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
-
- NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
- minidumpDir, minidumpID];
- NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
- minidumpDir, reportID];
-
- const char *src = [srcString fileSystemRepresentation];
- const char *dest = [destString fileSystemRepresentation];
-
- if (rename(src, dest) == 0) {
- GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \
- "upload",src, dest);
- }
- else {
- // can't rename - don't worry - it's not important for users
- GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n",
- reportID );
- }
- [result release];
-}
-
-//=============================================================================
-- (void)report {
- NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
- HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
- NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
-
- if (![self populateServerDictionary:uploadParameters]) {
- [upload release];
- return;
- }
-
- [upload setParameters:uploadParameters];
-
- // Add minidump file
- if (minidumpContents_) {
- [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
-
- // If there is a log file, upload it together with the minidump.
- if (logFileData_) {
- [upload addFileContents:logFileData_ name:@"log"];
- }
-
- // Send it
- NSError *error = nil;
- NSData *data = [upload send:&error];
-
- if (![url isFileURL]) {
- [self handleNetworkResponse:data withError:error];
- } else {
- if (error) {
- fprintf(stderr, "Breakpad Uploader: Error writing request file: %s\n",
- [[error description] UTF8String]);
- }
- }
-
- } else {
- // Minidump is missing -- upload just the log file.
- if (logFileData_) {
- [self uploadData:logFileData_ name:@"log"];
- }
- }
- [upload release];
-}
-
-- (void)uploadData:(NSData *)data name:(NSString *)name {
- NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
- NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
-
- if (![self populateServerDictionary:uploadParameters])
- return;
-
- HTTPMultipartUpload *upload =
- [[HTTPMultipartUpload alloc] initWithURL:url];
-
- [uploadParameters setObject:name forKey:@"type"];
- [upload setParameters:uploadParameters];
- [upload addFileContents:data name:name];
-
- [upload send:nil];
- [upload release];
-}
-
-- (void)logUploadWithID:(const char *)uploadID {
- NSString *minidumpDir =
- [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
- NSString *logFilePath = [NSString stringWithFormat:@"%@/%s",
- minidumpDir, kReporterLogFilename];
- NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n",
- [[NSDate date] timeIntervalSince1970], uploadID];
- NSData *logData = [logLine dataUsingEncoding:NSUTF8StringEncoding];
-
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:logFilePath]) {
- NSFileHandle *logFileHandle =
- [NSFileHandle fileHandleForWritingAtPath:logFilePath];
- [logFileHandle seekToEndOfFile];
- [logFileHandle writeData:logData];
- [logFileHandle closeFile];
- } else {
- [fileManager createFileAtPath:logFilePath
- contents:logData
- attributes:nil];
- }
-}
-
-//=============================================================================
-- (NSMutableDictionary *)parameters {
- return parameters_;
-}
-
-//=============================================================================
-- (void)dealloc {
- [parameters_ release];
- [minidumpContents_ release];
- [logFileData_ release];
- [googleDictionary_ release];
- [socorroDictionary_ release];
- [serverDictionary_ release];
- [extraServerVars_ release];
- [super dealloc];
-}
-
-@end