1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,618 @@ 1.4 +// Copyright (c) 2011, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 +#import <fcntl.h> 1.34 +#import <sys/stat.h> 1.35 +#include <TargetConditionals.h> 1.36 +#import <unistd.h> 1.37 + 1.38 +#import <SystemConfiguration/SystemConfiguration.h> 1.39 + 1.40 +#import "common/mac/HTTPMultipartUpload.h" 1.41 + 1.42 +#import "client/apple/Framework/BreakpadDefines.h" 1.43 +#import "client/mac/sender/uploader.h" 1.44 +#import "common/mac/GTMLogger.h" 1.45 + 1.46 +const int kMinidumpFileLengthLimit = 2 * 1024 * 1024; // 2MB 1.47 + 1.48 +#define kApplePrefsSyncExcludeAllKey \ 1.49 + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" 1.50 + 1.51 +NSString *const kGoogleServerType = @"google"; 1.52 +NSString *const kSocorroServerType = @"socorro"; 1.53 +NSString *const kDefaultServerType = @"google"; 1.54 + 1.55 +#pragma mark - 1.56 + 1.57 +namespace { 1.58 +// Read one line from the configuration file. 1.59 +NSString *readString(int fileId) { 1.60 + NSMutableString *str = [NSMutableString stringWithCapacity:32]; 1.61 + char ch[2] = { 0 }; 1.62 + 1.63 + while (read(fileId, &ch[0], 1) == 1) { 1.64 + if (ch[0] == '\n') { 1.65 + // Break if this is the first newline after reading some other string 1.66 + // data. 1.67 + if ([str length]) 1.68 + break; 1.69 + } else { 1.70 + [str appendString:[NSString stringWithUTF8String:ch]]; 1.71 + } 1.72 + } 1.73 + 1.74 + return str; 1.75 +} 1.76 + 1.77 +//============================================================================= 1.78 +// Read |length| of binary data from the configuration file. This method will 1.79 +// returns |nil| in case of error. 1.80 +NSData *readData(int fileId, ssize_t length) { 1.81 + NSMutableData *data = [NSMutableData dataWithLength:length]; 1.82 + char *bytes = (char *)[data bytes]; 1.83 + 1.84 + if (read(fileId, bytes, length) != length) 1.85 + return nil; 1.86 + 1.87 + return data; 1.88 +} 1.89 + 1.90 +//============================================================================= 1.91 +// Read the configuration from the config file. 1.92 +NSDictionary *readConfigurationData(const char *configFile) { 1.93 + int fileId = open(configFile, O_RDONLY, 0600); 1.94 + if (fileId == -1) { 1.95 + GTMLoggerDebug(@"Couldn't open config file %s - %s", 1.96 + configFile, 1.97 + strerror(errno)); 1.98 + } 1.99 + 1.100 + // we want to avoid a build-up of old config files even if they 1.101 + // have been incorrectly written by the framework 1.102 + if (unlink(configFile)) { 1.103 + GTMLoggerDebug(@"Couldn't unlink config file %s - %s", 1.104 + configFile, 1.105 + strerror(errno)); 1.106 + } 1.107 + 1.108 + if (fileId == -1) { 1.109 + return nil; 1.110 + } 1.111 + 1.112 + NSMutableDictionary *config = [NSMutableDictionary dictionary]; 1.113 + 1.114 + while (1) { 1.115 + NSString *key = readString(fileId); 1.116 + 1.117 + if (![key length]) 1.118 + break; 1.119 + 1.120 + // Read the data. Try to convert to a UTF-8 string, or just save 1.121 + // the data 1.122 + NSString *lenStr = readString(fileId); 1.123 + ssize_t len = [lenStr intValue]; 1.124 + NSData *data = readData(fileId, len); 1.125 + id value = [[NSString alloc] initWithData:data 1.126 + encoding:NSUTF8StringEncoding]; 1.127 + 1.128 + [config setObject:(value ? value : data) forKey:key]; 1.129 + [value release]; 1.130 + } 1.131 + 1.132 + close(fileId); 1.133 + return config; 1.134 +} 1.135 +} // namespace 1.136 + 1.137 +#pragma mark - 1.138 + 1.139 +@interface Uploader(PrivateMethods) 1.140 + 1.141 +// Update |parameters_| as well as the server parameters using |config|. 1.142 +- (void)translateConfigurationData:(NSDictionary *)config; 1.143 + 1.144 +// Read the minidump referenced in |parameters_| and update |minidumpContents_| 1.145 +// with its content. 1.146 +- (BOOL)readMinidumpData; 1.147 + 1.148 +// Read the log files referenced in |parameters_| and update |logFileData_| 1.149 +// with their content. 1.150 +- (BOOL)readLogFileData; 1.151 + 1.152 +// Returns a unique client id (user-specific), creating a persistent 1.153 +// one in the user defaults, if necessary. 1.154 +- (NSString*)clientID; 1.155 + 1.156 +// Returns a dictionary that can be used to map Breakpad parameter names to 1.157 +// URL parameter names. 1.158 +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType; 1.159 + 1.160 +// Helper method to set HTTP parameters based on server type. This is 1.161 +// called right before the upload - crashParameters will contain, on exit, 1.162 +// URL parameters that should be sent with the minidump. 1.163 +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters; 1.164 + 1.165 +// Initialization helper to create dictionaries mapping Breakpad 1.166 +// parameters to URL parameters 1.167 +- (void)createServerParameterDictionaries; 1.168 + 1.169 +// Accessor method for the URL parameter dictionary 1.170 +- (NSMutableDictionary *)urlParameterDictionary; 1.171 + 1.172 +// Records the uploaded crash ID to the log file. 1.173 +- (void)logUploadWithID:(const char *)uploadID; 1.174 +@end 1.175 + 1.176 +@implementation Uploader 1.177 + 1.178 +//============================================================================= 1.179 +- (id)initWithConfigFile:(const char *)configFile { 1.180 + NSDictionary *config = readConfigurationData(configFile); 1.181 + if (!config) 1.182 + return nil; 1.183 + 1.184 + return [self initWithConfig:config]; 1.185 +} 1.186 + 1.187 +//============================================================================= 1.188 +- (id)initWithConfig:(NSDictionary *)config { 1.189 + if ((self = [super init])) { 1.190 + // Because the reporter is embedded in the framework (and many copies 1.191 + // of the framework may exist) its not completely certain that the OS 1.192 + // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our 1.193 + // Info.plist. To make sure, also set the key directly if needed. 1.194 + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 1.195 + if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { 1.196 + [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; 1.197 + } 1.198 + 1.199 + [self createServerParameterDictionaries]; 1.200 + 1.201 + [self translateConfigurationData:config]; 1.202 + 1.203 + // Read the minidump into memory. 1.204 + [self readMinidumpData]; 1.205 + [self readLogFileData]; 1.206 + } 1.207 + return self; 1.208 +} 1.209 + 1.210 +//============================================================================= 1.211 +- (void)translateConfigurationData:(NSDictionary *)config { 1.212 + parameters_ = [[NSMutableDictionary alloc] init]; 1.213 + 1.214 + NSEnumerator *it = [config keyEnumerator]; 1.215 + while (NSString *key = [it nextObject]) { 1.216 + // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX 1.217 + // that indicates that it should be uploaded to the server along 1.218 + // with the minidump, so we treat it specially. 1.219 + if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) { 1.220 + NSString *urlParameterKey = 1.221 + [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]]; 1.222 + if ([urlParameterKey length]) { 1.223 + id value = [config objectForKey:key]; 1.224 + if ([value isKindOfClass:[NSString class]]) { 1.225 + [self addServerParameter:(NSString *)value 1.226 + forKey:urlParameterKey]; 1.227 + } else { 1.228 + [self addServerParameter:(NSData *)value 1.229 + forKey:urlParameterKey]; 1.230 + } 1.231 + } 1.232 + } else { 1.233 + [parameters_ setObject:[config objectForKey:key] forKey:key]; 1.234 + } 1.235 + } 1.236 + 1.237 + // generate a unique client ID based on this host's MAC address 1.238 + // then add a key/value pair for it 1.239 + NSString *clientID = [self clientID]; 1.240 + [parameters_ setObject:clientID forKey:@"guid"]; 1.241 +} 1.242 + 1.243 +// Per user per machine 1.244 +- (NSString *)clientID { 1.245 + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 1.246 + NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; 1.247 + if (crashClientID) { 1.248 + return crashClientID; 1.249 + } 1.250 + 1.251 + // Otherwise, if we have no client id, generate one! 1.252 + srandom((int)[[NSDate date] timeIntervalSince1970]); 1.253 + long clientId1 = random(); 1.254 + long clientId2 = random(); 1.255 + long clientId3 = random(); 1.256 + crashClientID = [NSString stringWithFormat:@"%lx%lx%lx", 1.257 + clientId1, clientId2, clientId3]; 1.258 + 1.259 + [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; 1.260 + [ud synchronize]; 1.261 + return crashClientID; 1.262 +} 1.263 + 1.264 +//============================================================================= 1.265 +- (BOOL)readLogFileData { 1.266 +#if TARGET_OS_IPHONE 1.267 + return NO; 1.268 +#else 1.269 + unsigned int logFileCounter = 0; 1.270 + 1.271 + NSString *logPath; 1.272 + size_t logFileTailSize = 1.273 + [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue]; 1.274 + 1.275 + NSMutableArray *logFilenames; // An array of NSString, one per log file 1.276 + logFilenames = [[NSMutableArray alloc] init]; 1.277 + 1.278 + char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; 1.279 + char *tmpDir = mkdtemp(tmpDirTemplate); 1.280 + 1.281 + // Construct key names for the keys we expect to contain log file paths 1.282 + for(logFileCounter = 0;; logFileCounter++) { 1.283 + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", 1.284 + @BREAKPAD_LOGFILE_KEY_PREFIX, 1.285 + logFileCounter]; 1.286 + 1.287 + logPath = [parameters_ objectForKey:logFileKey]; 1.288 + 1.289 + // They should all be consecutive, so if we don't find one, assume 1.290 + // we're done 1.291 + 1.292 + if (!logPath) { 1.293 + break; 1.294 + } 1.295 + 1.296 + NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; 1.297 + 1.298 + if (entireLogFile == nil) { 1.299 + continue; 1.300 + } 1.301 + 1.302 + NSRange fileRange; 1.303 + 1.304 + // Truncate the log file, only if necessary 1.305 + 1.306 + if ([entireLogFile length] <= logFileTailSize) { 1.307 + fileRange = NSMakeRange(0, [entireLogFile length]); 1.308 + } else { 1.309 + fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, 1.310 + logFileTailSize); 1.311 + } 1.312 + 1.313 + char tmpFilenameTemplate[100]; 1.314 + 1.315 + // Generate a template based on the log filename 1.316 + sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, 1.317 + [[logPath lastPathComponent] fileSystemRepresentation]); 1.318 + 1.319 + char *tmpFile = mktemp(tmpFilenameTemplate); 1.320 + 1.321 + NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; 1.322 + NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; 1.323 + [logSubdata writeToFile:tmpFileString atomically:NO]; 1.324 + 1.325 + [logFilenames addObject:[tmpFileString lastPathComponent]]; 1.326 + [entireLogFile release]; 1.327 + } 1.328 + 1.329 + if ([logFilenames count] == 0) { 1.330 + [logFilenames release]; 1.331 + logFileData_ = nil; 1.332 + return NO; 1.333 + } 1.334 + 1.335 + // now, bzip all files into one 1.336 + NSTask *tarTask = [[NSTask alloc] init]; 1.337 + 1.338 + [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; 1.339 + [tarTask setLaunchPath:@"/usr/bin/tar"]; 1.340 + 1.341 + NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", 1.342 + @"log.tar.bz2",nil]; 1.343 + [bzipArgs addObjectsFromArray:logFilenames]; 1.344 + 1.345 + [logFilenames release]; 1.346 + 1.347 + [tarTask setArguments:bzipArgs]; 1.348 + [tarTask launch]; 1.349 + [tarTask waitUntilExit]; 1.350 + [tarTask release]; 1.351 + 1.352 + NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; 1.353 + logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; 1.354 + if (logFileData_ == nil) { 1.355 + GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile); 1.356 + return NO; 1.357 + } 1.358 + return YES; 1.359 +#endif // TARGET_OS_IPHONE 1.360 +} 1.361 + 1.362 +//============================================================================= 1.363 +- (BOOL)readMinidumpData { 1.364 + NSString *minidumpDir = 1.365 + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; 1.366 + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; 1.367 + 1.368 + if (![minidumpID length]) 1.369 + return NO; 1.370 + 1.371 + NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; 1.372 + path = [path stringByAppendingPathExtension:@"dmp"]; 1.373 + 1.374 + // check the size of the minidump and limit it to a reasonable size 1.375 + // before attempting to load into memory and upload 1.376 + const char *fileName = [path fileSystemRepresentation]; 1.377 + struct stat fileStatus; 1.378 + 1.379 + BOOL success = YES; 1.380 + 1.381 + if (!stat(fileName, &fileStatus)) { 1.382 + if (fileStatus.st_size > kMinidumpFileLengthLimit) { 1.383 + fprintf(stderr, "Breakpad Uploader: minidump file too large " \ 1.384 + "to upload : %d\n", (int)fileStatus.st_size); 1.385 + success = NO; 1.386 + } 1.387 + } else { 1.388 + fprintf(stderr, "Breakpad Uploader: unable to determine minidump " \ 1.389 + "file length\n"); 1.390 + success = NO; 1.391 + } 1.392 + 1.393 + if (success) { 1.394 + minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; 1.395 + success = ([minidumpContents_ length] ? YES : NO); 1.396 + } 1.397 + 1.398 + if (!success) { 1.399 + // something wrong with the minidump file -- delete it 1.400 + unlink(fileName); 1.401 + } 1.402 + 1.403 + return success; 1.404 +} 1.405 + 1.406 +#pragma mark - 1.407 +//============================================================================= 1.408 + 1.409 +- (void)createServerParameterDictionaries { 1.410 + serverDictionary_ = [[NSMutableDictionary alloc] init]; 1.411 + socorroDictionary_ = [[NSMutableDictionary alloc] init]; 1.412 + googleDictionary_ = [[NSMutableDictionary alloc] init]; 1.413 + extraServerVars_ = [[NSMutableDictionary alloc] init]; 1.414 + 1.415 + [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType]; 1.416 + [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType]; 1.417 + 1.418 + [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME]; 1.419 + [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL]; 1.420 + [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS]; 1.421 + [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT]; 1.422 + [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION]; 1.423 + [googleDictionary_ setObject:@"guid" forKey:@"guid"]; 1.424 + 1.425 + [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS]; 1.426 + [socorroDictionary_ setObject:@"CrashTime" 1.427 + forKey:@BREAKPAD_PROCESS_CRASH_TIME]; 1.428 + [socorroDictionary_ setObject:@"StartupTime" 1.429 + forKey:@BREAKPAD_PROCESS_START_TIME]; 1.430 + [socorroDictionary_ setObject:@"Version" 1.431 + forKey:@BREAKPAD_VERSION]; 1.432 + [socorroDictionary_ setObject:@"ProductName" 1.433 + forKey:@BREAKPAD_PRODUCT]; 1.434 + [socorroDictionary_ setObject:@"Email" 1.435 + forKey:@BREAKPAD_EMAIL]; 1.436 +} 1.437 + 1.438 +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType { 1.439 + if (serverType == nil || [serverType length] == 0) { 1.440 + return [serverDictionary_ objectForKey:kDefaultServerType]; 1.441 + } 1.442 + return [serverDictionary_ objectForKey:serverType]; 1.443 +} 1.444 + 1.445 +- (NSMutableDictionary *)urlParameterDictionary { 1.446 + NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; 1.447 + return [self dictionaryForServerType:serverType]; 1.448 + 1.449 +} 1.450 + 1.451 +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters { 1.452 + NSDictionary *urlParameterNames = [self urlParameterDictionary]; 1.453 + 1.454 + id key; 1.455 + NSEnumerator *enumerator = [parameters_ keyEnumerator]; 1.456 + 1.457 + while ((key = [enumerator nextObject])) { 1.458 + // The key from parameters_ corresponds to a key in 1.459 + // urlParameterNames. The value in parameters_ gets stored in 1.460 + // crashParameters with a key that is the value in 1.461 + // urlParameterNames. 1.462 + 1.463 + // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and 1.464 + // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP 1.465 + // URL parameter becomes [pname => "FOOBAR"]. 1.466 + NSString *breakpadParameterName = (NSString *)key; 1.467 + NSString *urlParameter = [urlParameterNames 1.468 + objectForKey:breakpadParameterName]; 1.469 + if (urlParameter) { 1.470 + [crashParameters setObject:[parameters_ objectForKey:key] 1.471 + forKey:urlParameter]; 1.472 + } 1.473 + } 1.474 + 1.475 + // Now, add the parameters that were added by the application. 1.476 + enumerator = [extraServerVars_ keyEnumerator]; 1.477 + 1.478 + while ((key = [enumerator nextObject])) { 1.479 + NSString *urlParameterName = (NSString *)key; 1.480 + NSString *urlParameterValue = 1.481 + [extraServerVars_ objectForKey:urlParameterName]; 1.482 + [crashParameters setObject:urlParameterValue 1.483 + forKey:urlParameterName]; 1.484 + } 1.485 + return YES; 1.486 +} 1.487 + 1.488 +- (void)addServerParameter:(id)value forKey:(NSString *)key { 1.489 + [extraServerVars_ setObject:value forKey:key]; 1.490 +} 1.491 + 1.492 +//============================================================================= 1.493 +- (void)report { 1.494 + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; 1.495 + HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; 1.496 + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; 1.497 + 1.498 + if (![self populateServerDictionary:uploadParameters]) { 1.499 + [upload release]; 1.500 + return; 1.501 + } 1.502 + 1.503 + [upload setParameters:uploadParameters]; 1.504 + 1.505 + // Add minidump file 1.506 + if (minidumpContents_) { 1.507 + [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; 1.508 + 1.509 + // If there is a log file, upload it together with the minidump. 1.510 + if (logFileData_) { 1.511 + [upload addFileContents:logFileData_ name:@"log"]; 1.512 + } 1.513 + 1.514 + // Send it 1.515 + NSError *error = nil; 1.516 + NSData *data = [upload send:&error]; 1.517 + NSString *result = [[NSString alloc] initWithData:data 1.518 + encoding:NSUTF8StringEncoding]; 1.519 + const char *reportID = "ERR"; 1.520 + 1.521 + if (error) { 1.522 + fprintf(stderr, "Breakpad Uploader: Send Error: %s\n", 1.523 + [[error description] UTF8String]); 1.524 + } else { 1.525 + NSCharacterSet *trimSet = 1.526 + [NSCharacterSet whitespaceAndNewlineCharacterSet]; 1.527 + reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; 1.528 + [self logUploadWithID:reportID]; 1.529 + } 1.530 + 1.531 + // rename the minidump file according to the id returned from the server 1.532 + NSString *minidumpDir = 1.533 + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; 1.534 + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; 1.535 + 1.536 + NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", 1.537 + minidumpDir, minidumpID]; 1.538 + NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", 1.539 + minidumpDir, reportID]; 1.540 + 1.541 + const char *src = [srcString fileSystemRepresentation]; 1.542 + const char *dest = [destString fileSystemRepresentation]; 1.543 + 1.544 + if (rename(src, dest) == 0) { 1.545 + GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \ 1.546 + "upload",src, dest); 1.547 + } 1.548 + else { 1.549 + // can't rename - don't worry - it's not important for users 1.550 + GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n", 1.551 + reportID ); 1.552 + } 1.553 + [result release]; 1.554 + } else { 1.555 + // Minidump is missing -- upload just the log file. 1.556 + if (logFileData_) { 1.557 + [self uploadData:logFileData_ name:@"log"]; 1.558 + } 1.559 + } 1.560 + [upload release]; 1.561 +} 1.562 + 1.563 +- (void)uploadData:(NSData *)data name:(NSString *)name { 1.564 + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; 1.565 + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; 1.566 + 1.567 + if (![self populateServerDictionary:uploadParameters]) 1.568 + return; 1.569 + 1.570 + HTTPMultipartUpload *upload = 1.571 + [[HTTPMultipartUpload alloc] initWithURL:url]; 1.572 + 1.573 + [uploadParameters setObject:name forKey:@"type"]; 1.574 + [upload setParameters:uploadParameters]; 1.575 + [upload addFileContents:data name:name]; 1.576 + 1.577 + [upload send:nil]; 1.578 + [upload release]; 1.579 +} 1.580 + 1.581 +- (void)logUploadWithID:(const char *)uploadID { 1.582 + NSString *minidumpDir = 1.583 + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; 1.584 + NSString *logFilePath = [NSString stringWithFormat:@"%@/%s", 1.585 + minidumpDir, kReporterLogFilename]; 1.586 + NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n", 1.587 + [[NSDate date] timeIntervalSince1970], uploadID]; 1.588 + NSData *logData = [logLine dataUsingEncoding:NSUTF8StringEncoding]; 1.589 + 1.590 + NSFileManager *fileManager = [NSFileManager defaultManager]; 1.591 + if ([fileManager fileExistsAtPath:logFilePath]) { 1.592 + NSFileHandle *logFileHandle = 1.593 + [NSFileHandle fileHandleForWritingAtPath:logFilePath]; 1.594 + [logFileHandle seekToEndOfFile]; 1.595 + [logFileHandle writeData:logData]; 1.596 + [logFileHandle closeFile]; 1.597 + } else { 1.598 + [fileManager createFileAtPath:logFilePath 1.599 + contents:logData 1.600 + attributes:nil]; 1.601 + } 1.602 +} 1.603 + 1.604 +//============================================================================= 1.605 +- (NSMutableDictionary *)parameters { 1.606 + return parameters_; 1.607 +} 1.608 + 1.609 +//============================================================================= 1.610 +- (void)dealloc { 1.611 + [parameters_ release]; 1.612 + [minidumpContents_ release]; 1.613 + [logFileData_ release]; 1.614 + [googleDictionary_ release]; 1.615 + [socorroDictionary_ release]; 1.616 + [serverDictionary_ release]; 1.617 + [extraServerVars_ release]; 1.618 + [super dealloc]; 1.619 +} 1.620 + 1.621 +@end