xref: /aosp_15_r20/external/google-breakpad/src/client/mac/sender/uploader.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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