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

changeset 0
6474c204b198
     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

mercurial