public
Description:
Homepage:
Clone URL: git://github.com/jessegrosjean/quickcursor.git
quickcursor / CrashReporter.m
100644 202 lines (159 sloc) 6.492 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//
// CrashReporter.m
// Documents
//
// Created by Jesse Grosjean on 9/6/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
 
#import "CrashReporter.h"
 
 
@implementation CrashReporter
 
#pragma mark class methods
 
+ (id)sharedInstance {
    static id sharedInstance = nil;
    if (sharedInstance == nil) {
        sharedInstance = [[self alloc] init];
    }
    return sharedInstance;
}
 
#pragma dealloc
 
- (id)init {
    if (self = [super initWithWindowNibName:@"CrashReporterWindow"]) {
crashReport = [[NSMutableDictionary dictionary] retain];
    }
    return self;
}
 
- (void)dealloc {
[crashReport release];
    [super dealloc];
}
 
#pragma awake from nib like methods
 
- (void)awakeFromNib {
[statusMessageTextField setStringValue:@""];
[[self window] setLevel:NSFloatingWindowLevel];
}
 
#pragma mark accessors
 
- (NSString *)latestCrashPath {
NSString *name = [[NSProcessInfo processInfo] processName];
NSString *crashLogPrefix = [NSString stringWithFormat: @"%@_", name];
NSString *crashLogSuffix = @".crash";
NSString *crashReporterFolder = [@"~/Library/Logs/CrashReporter/" stringByExpandingTildeInPath];
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:crashReporterFolder];
NSDate *currentDate = nil;
NSString *foundName = nil;
NSDate *foundDate = nil;
 
for (NSString *currentName in enumerator) {
if ([currentName hasPrefix:crashLogPrefix] && [currentName hasSuffix:crashLogSuffix]) {
currentDate = [[enumerator fileAttributes] fileModificationDate];
if (foundName) {
if ([currentDate isGreaterThan:foundDate]) {
foundName = currentName;
foundDate = currentDate;
}
} else {
foundName = currentName;
foundDate = currentDate;
}
}
}
 
if (!foundName) {
return nil;
} else {
return [crashReporterFolder stringByAppendingPathComponent:foundName];
}
}
 
- (void)setStatusMessage:(NSString *)message {
    if ([message length]) {
[statusProgressIndicator startAnimation:nil];
    } else {
[statusProgressIndicator stopAnimation:nil];
    }
    
    [statusMessageTextField setStringValue:message];
    [statusMessageTextField display];
}
 
#pragma mark actions
 
- (IBAction)check:(id)sender {
@try {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *latestCrashPath = [self latestCrashPath];
NSDictionary *lastCrashFileAttributes = [fileManager attributesOfItemAtPath:latestCrashPath error:nil];
 
if (lastCrashFileAttributes) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *lastCheckDate = [defaults objectForKey:CrashReporterLastCheckDateDefaultsKey];
NSDate *lastCrashDate = [lastCrashFileAttributes fileModificationDate];
 
if (lastCheckDate == nil || [lastCheckDate isLessThan:lastCrashDate]) {
[defaults setObject:lastCrashDate forKey:CrashReporterLastCheckDateDefaultsKey];
[defaults synchronize];
 
NSWindow *window = [self window];
NSString *processName = [[NSProcessInfo processInfo] processName];
NSMutableString *crashLogs = [NSMutableString string];
 
[statusProgressIndicator setUsesThreadedAnimation:YES];
 
[window setTitle:[NSString stringWithFormat:[window title], processName]];
[titleTextField setStringValue:[NSString stringWithFormat:[titleTextField stringValue], processName]];
 
[window center];
[window orderFront:self];
 
NSString *crashLog = [NSString stringWithContentsOfFile:latestCrashPath encoding:NSUTF8StringEncoding error:NULL];
if ([crashLog length] > 0) {
[crashLogs appendString:crashLog];
}
 
[[problemDetailsTextView textStorage] replaceCharactersInRange:NSMakeRange(0, 0) withString:crashLogs];
 
[crashReport setObject:crashLogs forKey:@"log"];
[crashReport setObject:@"jesse@hogbaysoftware.com" forKey:@"email"];
}
}
} @catch (NSException * e) {
NSLog(@"Exception while checking for crash reports %@", [e description]);
}
}
 
- (IBAction)sendReport:(id)sender {
NSString *crashReportURLString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CrashReporterPostToURL"];
 
if (!crashReportURLString) {
NSRunAlertPanel(NSLocalizedString(@"Unable to send crash report", nil),
NSLocalizedString(@"No value has been set for the CrashReporterPostToURL key in the applications Info.plist. Please contact the applictions developer.", nil),
NSLocalizedString(@"OK", nil),
nil,
nil);
return;
}
 
    [crashReport setObject:[[problemCommentsTextView textStorage] string] forKey:@"description"];
    
    NSMutableString *reportString = [[[NSMutableString alloc] init] autorelease];
    NSEnumerator *enumerator = [[crashReport allKeys] objectEnumerator];
    NSString *key;
 
    while(key = [enumerator nextObject]) {
if ([reportString length] != 0) [reportString appendString:@"&"];
[reportString appendFormat:@"%@=%@", key, [[crashReport objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }
    
    NSData *data = nil;
 
    while(!data) {
NSError *error;
NSURLResponse *reply;
NSURL *crashReportURL = [NSURL URLWithString:crashReportURLString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:crashReportURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:120];
[request addValue:[[NSProcessInfo processInfo] processName] forHTTPHeaderField:[NSString stringWithFormat:@"%@-Bug-Report", [[NSProcessInfo processInfo] processName]]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[reportString dataUsingEncoding:NSUTF8StringEncoding]];
 
[self setStatusMessage:NSLocalizedString(@"Sending Report...", nil)];
 
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&reply error:&error];
 
[self setStatusMessage:@""];
 
if (!data) {
if (NSRunAlertPanel(NSLocalizedString(@"Unable to send crash report", nil),
error != nil ? [error localizedDescription] : @"",
NSLocalizedString(@"Try Again", nil),
NSLocalizedString(@"Cancel", nil),
nil) == NSAlertAlternateReturn) {
break;
}
} else {
NSRunAlertPanel(NSLocalizedString(@"Thank You", nil),
NSLocalizedString(@"The crash report has been sent.", nil),
NSLocalizedString(@"OK", nil),
nil,
nil);
}
    }
 
[self close];
}
 
- (IBAction)ignore:(id)sender {
[self close];
}
 
@end
 
NSString *CrashReporterLastCheckDateDefaultsKey = @"CrashReporterLastCheckDateDefaultsKey";