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