toolkit/crashreporter/google-breakpad/src/client/mac/sender/uploader.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2011, Google Inc.
michael@0 2 // All rights reserved.
michael@0 3 //
michael@0 4 // Redistribution and use in source and binary forms, with or without
michael@0 5 // modification, are permitted provided that the following conditions are
michael@0 6 // met:
michael@0 7 //
michael@0 8 // * Redistributions of source code must retain the above copyright
michael@0 9 // notice, this list of conditions and the following disclaimer.
michael@0 10 // * Redistributions in binary form must reproduce the above
michael@0 11 // copyright notice, this list of conditions and the following disclaimer
michael@0 12 // in the documentation and/or other materials provided with the
michael@0 13 // distribution.
michael@0 14 // * Neither the name of Google Inc. nor the names of its
michael@0 15 // contributors may be used to endorse or promote products derived from
michael@0 16 // this software without specific prior written permission.
michael@0 17 //
michael@0 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30 #import <fcntl.h>
michael@0 31 #import <sys/stat.h>
michael@0 32 #include <TargetConditionals.h>
michael@0 33 #import <unistd.h>
michael@0 34
michael@0 35 #import <SystemConfiguration/SystemConfiguration.h>
michael@0 36
michael@0 37 #import "common/mac/HTTPMultipartUpload.h"
michael@0 38
michael@0 39 #import "client/apple/Framework/BreakpadDefines.h"
michael@0 40 #import "client/mac/sender/uploader.h"
michael@0 41 #import "common/mac/GTMLogger.h"
michael@0 42
michael@0 43 const int kMinidumpFileLengthLimit = 2 * 1024 * 1024; // 2MB
michael@0 44
michael@0 45 #define kApplePrefsSyncExcludeAllKey \
michael@0 46 @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
michael@0 47
michael@0 48 NSString *const kGoogleServerType = @"google";
michael@0 49 NSString *const kSocorroServerType = @"socorro";
michael@0 50 NSString *const kDefaultServerType = @"google";
michael@0 51
michael@0 52 #pragma mark -
michael@0 53
michael@0 54 namespace {
michael@0 55 // Read one line from the configuration file.
michael@0 56 NSString *readString(int fileId) {
michael@0 57 NSMutableString *str = [NSMutableString stringWithCapacity:32];
michael@0 58 char ch[2] = { 0 };
michael@0 59
michael@0 60 while (read(fileId, &ch[0], 1) == 1) {
michael@0 61 if (ch[0] == '\n') {
michael@0 62 // Break if this is the first newline after reading some other string
michael@0 63 // data.
michael@0 64 if ([str length])
michael@0 65 break;
michael@0 66 } else {
michael@0 67 [str appendString:[NSString stringWithUTF8String:ch]];
michael@0 68 }
michael@0 69 }
michael@0 70
michael@0 71 return str;
michael@0 72 }
michael@0 73
michael@0 74 //=============================================================================
michael@0 75 // Read |length| of binary data from the configuration file. This method will
michael@0 76 // returns |nil| in case of error.
michael@0 77 NSData *readData(int fileId, ssize_t length) {
michael@0 78 NSMutableData *data = [NSMutableData dataWithLength:length];
michael@0 79 char *bytes = (char *)[data bytes];
michael@0 80
michael@0 81 if (read(fileId, bytes, length) != length)
michael@0 82 return nil;
michael@0 83
michael@0 84 return data;
michael@0 85 }
michael@0 86
michael@0 87 //=============================================================================
michael@0 88 // Read the configuration from the config file.
michael@0 89 NSDictionary *readConfigurationData(const char *configFile) {
michael@0 90 int fileId = open(configFile, O_RDONLY, 0600);
michael@0 91 if (fileId == -1) {
michael@0 92 GTMLoggerDebug(@"Couldn't open config file %s - %s",
michael@0 93 configFile,
michael@0 94 strerror(errno));
michael@0 95 }
michael@0 96
michael@0 97 // we want to avoid a build-up of old config files even if they
michael@0 98 // have been incorrectly written by the framework
michael@0 99 if (unlink(configFile)) {
michael@0 100 GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
michael@0 101 configFile,
michael@0 102 strerror(errno));
michael@0 103 }
michael@0 104
michael@0 105 if (fileId == -1) {
michael@0 106 return nil;
michael@0 107 }
michael@0 108
michael@0 109 NSMutableDictionary *config = [NSMutableDictionary dictionary];
michael@0 110
michael@0 111 while (1) {
michael@0 112 NSString *key = readString(fileId);
michael@0 113
michael@0 114 if (![key length])
michael@0 115 break;
michael@0 116
michael@0 117 // Read the data. Try to convert to a UTF-8 string, or just save
michael@0 118 // the data
michael@0 119 NSString *lenStr = readString(fileId);
michael@0 120 ssize_t len = [lenStr intValue];
michael@0 121 NSData *data = readData(fileId, len);
michael@0 122 id value = [[NSString alloc] initWithData:data
michael@0 123 encoding:NSUTF8StringEncoding];
michael@0 124
michael@0 125 [config setObject:(value ? value : data) forKey:key];
michael@0 126 [value release];
michael@0 127 }
michael@0 128
michael@0 129 close(fileId);
michael@0 130 return config;
michael@0 131 }
michael@0 132 } // namespace
michael@0 133
michael@0 134 #pragma mark -
michael@0 135
michael@0 136 @interface Uploader(PrivateMethods)
michael@0 137
michael@0 138 // Update |parameters_| as well as the server parameters using |config|.
michael@0 139 - (void)translateConfigurationData:(NSDictionary *)config;
michael@0 140
michael@0 141 // Read the minidump referenced in |parameters_| and update |minidumpContents_|
michael@0 142 // with its content.
michael@0 143 - (BOOL)readMinidumpData;
michael@0 144
michael@0 145 // Read the log files referenced in |parameters_| and update |logFileData_|
michael@0 146 // with their content.
michael@0 147 - (BOOL)readLogFileData;
michael@0 148
michael@0 149 // Returns a unique client id (user-specific), creating a persistent
michael@0 150 // one in the user defaults, if necessary.
michael@0 151 - (NSString*)clientID;
michael@0 152
michael@0 153 // Returns a dictionary that can be used to map Breakpad parameter names to
michael@0 154 // URL parameter names.
michael@0 155 - (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType;
michael@0 156
michael@0 157 // Helper method to set HTTP parameters based on server type. This is
michael@0 158 // called right before the upload - crashParameters will contain, on exit,
michael@0 159 // URL parameters that should be sent with the minidump.
michael@0 160 - (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters;
michael@0 161
michael@0 162 // Initialization helper to create dictionaries mapping Breakpad
michael@0 163 // parameters to URL parameters
michael@0 164 - (void)createServerParameterDictionaries;
michael@0 165
michael@0 166 // Accessor method for the URL parameter dictionary
michael@0 167 - (NSMutableDictionary *)urlParameterDictionary;
michael@0 168
michael@0 169 // Records the uploaded crash ID to the log file.
michael@0 170 - (void)logUploadWithID:(const char *)uploadID;
michael@0 171 @end
michael@0 172
michael@0 173 @implementation Uploader
michael@0 174
michael@0 175 //=============================================================================
michael@0 176 - (id)initWithConfigFile:(const char *)configFile {
michael@0 177 NSDictionary *config = readConfigurationData(configFile);
michael@0 178 if (!config)
michael@0 179 return nil;
michael@0 180
michael@0 181 return [self initWithConfig:config];
michael@0 182 }
michael@0 183
michael@0 184 //=============================================================================
michael@0 185 - (id)initWithConfig:(NSDictionary *)config {
michael@0 186 if ((self = [super init])) {
michael@0 187 // Because the reporter is embedded in the framework (and many copies
michael@0 188 // of the framework may exist) its not completely certain that the OS
michael@0 189 // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
michael@0 190 // Info.plist. To make sure, also set the key directly if needed.
michael@0 191 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
michael@0 192 if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
michael@0 193 [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
michael@0 194 }
michael@0 195
michael@0 196 [self createServerParameterDictionaries];
michael@0 197
michael@0 198 [self translateConfigurationData:config];
michael@0 199
michael@0 200 // Read the minidump into memory.
michael@0 201 [self readMinidumpData];
michael@0 202 [self readLogFileData];
michael@0 203 }
michael@0 204 return self;
michael@0 205 }
michael@0 206
michael@0 207 //=============================================================================
michael@0 208 - (void)translateConfigurationData:(NSDictionary *)config {
michael@0 209 parameters_ = [[NSMutableDictionary alloc] init];
michael@0 210
michael@0 211 NSEnumerator *it = [config keyEnumerator];
michael@0 212 while (NSString *key = [it nextObject]) {
michael@0 213 // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX
michael@0 214 // that indicates that it should be uploaded to the server along
michael@0 215 // with the minidump, so we treat it specially.
michael@0 216 if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) {
michael@0 217 NSString *urlParameterKey =
michael@0 218 [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]];
michael@0 219 if ([urlParameterKey length]) {
michael@0 220 id value = [config objectForKey:key];
michael@0 221 if ([value isKindOfClass:[NSString class]]) {
michael@0 222 [self addServerParameter:(NSString *)value
michael@0 223 forKey:urlParameterKey];
michael@0 224 } else {
michael@0 225 [self addServerParameter:(NSData *)value
michael@0 226 forKey:urlParameterKey];
michael@0 227 }
michael@0 228 }
michael@0 229 } else {
michael@0 230 [parameters_ setObject:[config objectForKey:key] forKey:key];
michael@0 231 }
michael@0 232 }
michael@0 233
michael@0 234 // generate a unique client ID based on this host's MAC address
michael@0 235 // then add a key/value pair for it
michael@0 236 NSString *clientID = [self clientID];
michael@0 237 [parameters_ setObject:clientID forKey:@"guid"];
michael@0 238 }
michael@0 239
michael@0 240 // Per user per machine
michael@0 241 - (NSString *)clientID {
michael@0 242 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
michael@0 243 NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey];
michael@0 244 if (crashClientID) {
michael@0 245 return crashClientID;
michael@0 246 }
michael@0 247
michael@0 248 // Otherwise, if we have no client id, generate one!
michael@0 249 srandom((int)[[NSDate date] timeIntervalSince1970]);
michael@0 250 long clientId1 = random();
michael@0 251 long clientId2 = random();
michael@0 252 long clientId3 = random();
michael@0 253 crashClientID = [NSString stringWithFormat:@"%lx%lx%lx",
michael@0 254 clientId1, clientId2, clientId3];
michael@0 255
michael@0 256 [ud setObject:crashClientID forKey:kClientIdPreferenceKey];
michael@0 257 [ud synchronize];
michael@0 258 return crashClientID;
michael@0 259 }
michael@0 260
michael@0 261 //=============================================================================
michael@0 262 - (BOOL)readLogFileData {
michael@0 263 #if TARGET_OS_IPHONE
michael@0 264 return NO;
michael@0 265 #else
michael@0 266 unsigned int logFileCounter = 0;
michael@0 267
michael@0 268 NSString *logPath;
michael@0 269 size_t logFileTailSize =
michael@0 270 [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue];
michael@0 271
michael@0 272 NSMutableArray *logFilenames; // An array of NSString, one per log file
michael@0 273 logFilenames = [[NSMutableArray alloc] init];
michael@0 274
michael@0 275 char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX";
michael@0 276 char *tmpDir = mkdtemp(tmpDirTemplate);
michael@0 277
michael@0 278 // Construct key names for the keys we expect to contain log file paths
michael@0 279 for(logFileCounter = 0;; logFileCounter++) {
michael@0 280 NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
michael@0 281 @BREAKPAD_LOGFILE_KEY_PREFIX,
michael@0 282 logFileCounter];
michael@0 283
michael@0 284 logPath = [parameters_ objectForKey:logFileKey];
michael@0 285
michael@0 286 // They should all be consecutive, so if we don't find one, assume
michael@0 287 // we're done
michael@0 288
michael@0 289 if (!logPath) {
michael@0 290 break;
michael@0 291 }
michael@0 292
michael@0 293 NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath];
michael@0 294
michael@0 295 if (entireLogFile == nil) {
michael@0 296 continue;
michael@0 297 }
michael@0 298
michael@0 299 NSRange fileRange;
michael@0 300
michael@0 301 // Truncate the log file, only if necessary
michael@0 302
michael@0 303 if ([entireLogFile length] <= logFileTailSize) {
michael@0 304 fileRange = NSMakeRange(0, [entireLogFile length]);
michael@0 305 } else {
michael@0 306 fileRange = NSMakeRange([entireLogFile length] - logFileTailSize,
michael@0 307 logFileTailSize);
michael@0 308 }
michael@0 309
michael@0 310 char tmpFilenameTemplate[100];
michael@0 311
michael@0 312 // Generate a template based on the log filename
michael@0 313 sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir,
michael@0 314 [[logPath lastPathComponent] fileSystemRepresentation]);
michael@0 315
michael@0 316 char *tmpFile = mktemp(tmpFilenameTemplate);
michael@0 317
michael@0 318 NSData *logSubdata = [entireLogFile subdataWithRange:fileRange];
michael@0 319 NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile];
michael@0 320 [logSubdata writeToFile:tmpFileString atomically:NO];
michael@0 321
michael@0 322 [logFilenames addObject:[tmpFileString lastPathComponent]];
michael@0 323 [entireLogFile release];
michael@0 324 }
michael@0 325
michael@0 326 if ([logFilenames count] == 0) {
michael@0 327 [logFilenames release];
michael@0 328 logFileData_ = nil;
michael@0 329 return NO;
michael@0 330 }
michael@0 331
michael@0 332 // now, bzip all files into one
michael@0 333 NSTask *tarTask = [[NSTask alloc] init];
michael@0 334
michael@0 335 [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]];
michael@0 336 [tarTask setLaunchPath:@"/usr/bin/tar"];
michael@0 337
michael@0 338 NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf",
michael@0 339 @"log.tar.bz2",nil];
michael@0 340 [bzipArgs addObjectsFromArray:logFilenames];
michael@0 341
michael@0 342 [logFilenames release];
michael@0 343
michael@0 344 [tarTask setArguments:bzipArgs];
michael@0 345 [tarTask launch];
michael@0 346 [tarTask waitUntilExit];
michael@0 347 [tarTask release];
michael@0 348
michael@0 349 NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir];
michael@0 350 logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile];
michael@0 351 if (logFileData_ == nil) {
michael@0 352 GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile);
michael@0 353 return NO;
michael@0 354 }
michael@0 355 return YES;
michael@0 356 #endif // TARGET_OS_IPHONE
michael@0 357 }
michael@0 358
michael@0 359 //=============================================================================
michael@0 360 - (BOOL)readMinidumpData {
michael@0 361 NSString *minidumpDir =
michael@0 362 [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
michael@0 363 NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
michael@0 364
michael@0 365 if (![minidumpID length])
michael@0 366 return NO;
michael@0 367
michael@0 368 NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID];
michael@0 369 path = [path stringByAppendingPathExtension:@"dmp"];
michael@0 370
michael@0 371 // check the size of the minidump and limit it to a reasonable size
michael@0 372 // before attempting to load into memory and upload
michael@0 373 const char *fileName = [path fileSystemRepresentation];
michael@0 374 struct stat fileStatus;
michael@0 375
michael@0 376 BOOL success = YES;
michael@0 377
michael@0 378 if (!stat(fileName, &fileStatus)) {
michael@0 379 if (fileStatus.st_size > kMinidumpFileLengthLimit) {
michael@0 380 fprintf(stderr, "Breakpad Uploader: minidump file too large " \
michael@0 381 "to upload : %d\n", (int)fileStatus.st_size);
michael@0 382 success = NO;
michael@0 383 }
michael@0 384 } else {
michael@0 385 fprintf(stderr, "Breakpad Uploader: unable to determine minidump " \
michael@0 386 "file length\n");
michael@0 387 success = NO;
michael@0 388 }
michael@0 389
michael@0 390 if (success) {
michael@0 391 minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path];
michael@0 392 success = ([minidumpContents_ length] ? YES : NO);
michael@0 393 }
michael@0 394
michael@0 395 if (!success) {
michael@0 396 // something wrong with the minidump file -- delete it
michael@0 397 unlink(fileName);
michael@0 398 }
michael@0 399
michael@0 400 return success;
michael@0 401 }
michael@0 402
michael@0 403 #pragma mark -
michael@0 404 //=============================================================================
michael@0 405
michael@0 406 - (void)createServerParameterDictionaries {
michael@0 407 serverDictionary_ = [[NSMutableDictionary alloc] init];
michael@0 408 socorroDictionary_ = [[NSMutableDictionary alloc] init];
michael@0 409 googleDictionary_ = [[NSMutableDictionary alloc] init];
michael@0 410 extraServerVars_ = [[NSMutableDictionary alloc] init];
michael@0 411
michael@0 412 [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType];
michael@0 413 [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType];
michael@0 414
michael@0 415 [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME];
michael@0 416 [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL];
michael@0 417 [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS];
michael@0 418 [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT];
michael@0 419 [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION];
michael@0 420 [googleDictionary_ setObject:@"guid" forKey:@"guid"];
michael@0 421
michael@0 422 [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS];
michael@0 423 [socorroDictionary_ setObject:@"CrashTime"
michael@0 424 forKey:@BREAKPAD_PROCESS_CRASH_TIME];
michael@0 425 [socorroDictionary_ setObject:@"StartupTime"
michael@0 426 forKey:@BREAKPAD_PROCESS_START_TIME];
michael@0 427 [socorroDictionary_ setObject:@"Version"
michael@0 428 forKey:@BREAKPAD_VERSION];
michael@0 429 [socorroDictionary_ setObject:@"ProductName"
michael@0 430 forKey:@BREAKPAD_PRODUCT];
michael@0 431 [socorroDictionary_ setObject:@"Email"
michael@0 432 forKey:@BREAKPAD_EMAIL];
michael@0 433 }
michael@0 434
michael@0 435 - (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {
michael@0 436 if (serverType == nil || [serverType length] == 0) {
michael@0 437 return [serverDictionary_ objectForKey:kDefaultServerType];
michael@0 438 }
michael@0 439 return [serverDictionary_ objectForKey:serverType];
michael@0 440 }
michael@0 441
michael@0 442 - (NSMutableDictionary *)urlParameterDictionary {
michael@0 443 NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE];
michael@0 444 return [self dictionaryForServerType:serverType];
michael@0 445
michael@0 446 }
michael@0 447
michael@0 448 - (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters {
michael@0 449 NSDictionary *urlParameterNames = [self urlParameterDictionary];
michael@0 450
michael@0 451 id key;
michael@0 452 NSEnumerator *enumerator = [parameters_ keyEnumerator];
michael@0 453
michael@0 454 while ((key = [enumerator nextObject])) {
michael@0 455 // The key from parameters_ corresponds to a key in
michael@0 456 // urlParameterNames. The value in parameters_ gets stored in
michael@0 457 // crashParameters with a key that is the value in
michael@0 458 // urlParameterNames.
michael@0 459
michael@0 460 // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and
michael@0 461 // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP
michael@0 462 // URL parameter becomes [pname => "FOOBAR"].
michael@0 463 NSString *breakpadParameterName = (NSString *)key;
michael@0 464 NSString *urlParameter = [urlParameterNames
michael@0 465 objectForKey:breakpadParameterName];
michael@0 466 if (urlParameter) {
michael@0 467 [crashParameters setObject:[parameters_ objectForKey:key]
michael@0 468 forKey:urlParameter];
michael@0 469 }
michael@0 470 }
michael@0 471
michael@0 472 // Now, add the parameters that were added by the application.
michael@0 473 enumerator = [extraServerVars_ keyEnumerator];
michael@0 474
michael@0 475 while ((key = [enumerator nextObject])) {
michael@0 476 NSString *urlParameterName = (NSString *)key;
michael@0 477 NSString *urlParameterValue =
michael@0 478 [extraServerVars_ objectForKey:urlParameterName];
michael@0 479 [crashParameters setObject:urlParameterValue
michael@0 480 forKey:urlParameterName];
michael@0 481 }
michael@0 482 return YES;
michael@0 483 }
michael@0 484
michael@0 485 - (void)addServerParameter:(id)value forKey:(NSString *)key {
michael@0 486 [extraServerVars_ setObject:value forKey:key];
michael@0 487 }
michael@0 488
michael@0 489 //=============================================================================
michael@0 490 - (void)report {
michael@0 491 NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
michael@0 492 HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
michael@0 493 NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
michael@0 494
michael@0 495 if (![self populateServerDictionary:uploadParameters]) {
michael@0 496 [upload release];
michael@0 497 return;
michael@0 498 }
michael@0 499
michael@0 500 [upload setParameters:uploadParameters];
michael@0 501
michael@0 502 // Add minidump file
michael@0 503 if (minidumpContents_) {
michael@0 504 [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
michael@0 505
michael@0 506 // If there is a log file, upload it together with the minidump.
michael@0 507 if (logFileData_) {
michael@0 508 [upload addFileContents:logFileData_ name:@"log"];
michael@0 509 }
michael@0 510
michael@0 511 // Send it
michael@0 512 NSError *error = nil;
michael@0 513 NSData *data = [upload send:&error];
michael@0 514 NSString *result = [[NSString alloc] initWithData:data
michael@0 515 encoding:NSUTF8StringEncoding];
michael@0 516 const char *reportID = "ERR";
michael@0 517
michael@0 518 if (error) {
michael@0 519 fprintf(stderr, "Breakpad Uploader: Send Error: %s\n",
michael@0 520 [[error description] UTF8String]);
michael@0 521 } else {
michael@0 522 NSCharacterSet *trimSet =
michael@0 523 [NSCharacterSet whitespaceAndNewlineCharacterSet];
michael@0 524 reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
michael@0 525 [self logUploadWithID:reportID];
michael@0 526 }
michael@0 527
michael@0 528 // rename the minidump file according to the id returned from the server
michael@0 529 NSString *minidumpDir =
michael@0 530 [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
michael@0 531 NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
michael@0 532
michael@0 533 NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
michael@0 534 minidumpDir, minidumpID];
michael@0 535 NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
michael@0 536 minidumpDir, reportID];
michael@0 537
michael@0 538 const char *src = [srcString fileSystemRepresentation];
michael@0 539 const char *dest = [destString fileSystemRepresentation];
michael@0 540
michael@0 541 if (rename(src, dest) == 0) {
michael@0 542 GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \
michael@0 543 "upload",src, dest);
michael@0 544 }
michael@0 545 else {
michael@0 546 // can't rename - don't worry - it's not important for users
michael@0 547 GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n",
michael@0 548 reportID );
michael@0 549 }
michael@0 550 [result release];
michael@0 551 } else {
michael@0 552 // Minidump is missing -- upload just the log file.
michael@0 553 if (logFileData_) {
michael@0 554 [self uploadData:logFileData_ name:@"log"];
michael@0 555 }
michael@0 556 }
michael@0 557 [upload release];
michael@0 558 }
michael@0 559
michael@0 560 - (void)uploadData:(NSData *)data name:(NSString *)name {
michael@0 561 NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
michael@0 562 NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
michael@0 563
michael@0 564 if (![self populateServerDictionary:uploadParameters])
michael@0 565 return;
michael@0 566
michael@0 567 HTTPMultipartUpload *upload =
michael@0 568 [[HTTPMultipartUpload alloc] initWithURL:url];
michael@0 569
michael@0 570 [uploadParameters setObject:name forKey:@"type"];
michael@0 571 [upload setParameters:uploadParameters];
michael@0 572 [upload addFileContents:data name:name];
michael@0 573
michael@0 574 [upload send:nil];
michael@0 575 [upload release];
michael@0 576 }
michael@0 577
michael@0 578 - (void)logUploadWithID:(const char *)uploadID {
michael@0 579 NSString *minidumpDir =
michael@0 580 [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
michael@0 581 NSString *logFilePath = [NSString stringWithFormat:@"%@/%s",
michael@0 582 minidumpDir, kReporterLogFilename];
michael@0 583 NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n",
michael@0 584 [[NSDate date] timeIntervalSince1970], uploadID];
michael@0 585 NSData *logData = [logLine dataUsingEncoding:NSUTF8StringEncoding];
michael@0 586
michael@0 587 NSFileManager *fileManager = [NSFileManager defaultManager];
michael@0 588 if ([fileManager fileExistsAtPath:logFilePath]) {
michael@0 589 NSFileHandle *logFileHandle =
michael@0 590 [NSFileHandle fileHandleForWritingAtPath:logFilePath];
michael@0 591 [logFileHandle seekToEndOfFile];
michael@0 592 [logFileHandle writeData:logData];
michael@0 593 [logFileHandle closeFile];
michael@0 594 } else {
michael@0 595 [fileManager createFileAtPath:logFilePath
michael@0 596 contents:logData
michael@0 597 attributes:nil];
michael@0 598 }
michael@0 599 }
michael@0 600
michael@0 601 //=============================================================================
michael@0 602 - (NSMutableDictionary *)parameters {
michael@0 603 return parameters_;
michael@0 604 }
michael@0 605
michael@0 606 //=============================================================================
michael@0 607 - (void)dealloc {
michael@0 608 [parameters_ release];
michael@0 609 [minidumpContents_ release];
michael@0 610 [logFileData_ release];
michael@0 611 [googleDictionary_ release];
michael@0 612 [socorroDictionary_ release];
michael@0 613 [serverDictionary_ release];
michael@0 614 [extraServerVars_ release];
michael@0 615 [super dealloc];
michael@0 616 }
michael@0 617
michael@0 618 @end

mercurial