diff options
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.mm | 636 |
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 |