]> git.sesse.net Git - vlc/blob - modules/gui/macosx/CrashReporter.m
macosx: fix issue which caused automatic video resizing even if disabled
[vlc] / modules / gui / macosx / CrashReporter.m
1 /*****************************************************************************
2  * CrashReporter.h: Mac OS X interface crash reporter
3  *****************************************************************************
4  * Copyright (C) 2009-2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
8  *          Pierre d'Herbemont <pdherbemont # videolan org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "CrashReporter.h"
26 #import "intf.h"
27 #import <AddressBook/AddressBook.h>
28
29 @implementation VLCCrashReporter
30
31 #pragma mark - init
32
33 static VLCCrashReporter *_sharedMainInstance = nil;
34
35 + (VLCCrashReporter *)sharedInstance
36 {
37     return _sharedMainInstance ? _sharedMainInstance : [[self alloc] init];
38 }
39
40 - (id)init
41 {
42     if (_sharedMainInstance)
43         [self dealloc];
44     else
45         _sharedMainInstance = [super init];
46
47     return _sharedMainInstance;
48 }
49
50
51 - (void)awakeFromNib
52 {
53     [_crashrep_send_btn setTitle: _NS("Send")];
54     [_crashrep_dontSend_btn setTitle: _NS("Don't Send")];
55     [_crashrep_title_txt setStringValue: _NS("VLC crashed previously")];
56     [_crashrep_win setTitle: _NS("VLC crashed previously")];
57     [_crashrep_desc_txt setStringValue: _NS("Do you want to send details on the crash to VLC's development team?\n\nIf you want, you can enter a few lines on what you did before VLC crashed along with other helpful information: a link to download a sample file, a URL of a network stream, ...")];
58     [_crashrep_includeEmail_ckb setTitle: _NS("I agree to be possibly contacted about this bugreport.")];
59     [_crashrep_includeEmail_txt setStringValue: _NS("Only your default E-Mail address will be submitted, including no further information.")];
60     [_crashrep_dontaskagain_ckb setTitle: _NS("Don't ask again")];
61 }
62
63 - (void)dealloc
64 {
65     [_crashLogURLConnection cancel];
66     [_crashLogURLConnection release];
67
68     [super dealloc];
69 }
70
71 #pragma mark - inter-object services
72
73 - (NSString *)_latestCrashLogPathPreviouslySeen:(BOOL)previouslySeen
74 {
75     NSString * crashReporter;
76     if (OSX_MOUNTAIN_LION || OSX_MAVERICKS)
77         crashReporter = [@"~/Library/Logs/DiagnosticReports" stringByExpandingTildeInPath];
78     else
79         crashReporter = [@"~/Library/Logs/CrashReporter" stringByExpandingTildeInPath];
80     NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:crashReporter];
81     NSString *fname;
82     NSString * latestLog = nil;
83     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
84     int year  = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportYear"] : 0;
85     int month = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportMonth"]: 0;
86     int day   = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportDay"]  : 0;
87     int hours = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportHours"]: 0;
88
89     while (fname = [direnum nextObject]) {
90         [direnum skipDescendents];
91         if ([fname hasPrefix:@"VLC"] && [fname hasSuffix:@"crash"]) {
92             NSArray * compo = [fname componentsSeparatedByString:@"_"];
93             if ([compo count] < 3)
94                 continue;
95             compo = [[compo objectAtIndex:1] componentsSeparatedByString:@"-"];
96             if ([compo count] < 4)
97                 continue;
98
99             // Dooh. ugly.
100             if (year < [[compo objectAtIndex:0] intValue] ||
101                 (year ==[[compo objectAtIndex:0] intValue] &&
102                  (month < [[compo objectAtIndex:1] intValue] ||
103                   (month ==[[compo objectAtIndex:1] intValue] &&
104                    (day   < [[compo objectAtIndex:2] intValue] ||
105                     (day   ==[[compo objectAtIndex:2] intValue] &&
106                      hours < [[compo objectAtIndex:3] intValue])))))) {
107                         year  = [[compo objectAtIndex:0] intValue];
108                         month = [[compo objectAtIndex:1] intValue];
109                         day   = [[compo objectAtIndex:2] intValue];
110                         hours = [[compo objectAtIndex:3] intValue];
111                         latestLog = [crashReporter stringByAppendingPathComponent:fname];
112                     }
113         }
114     }
115
116     if (!(latestLog && [[NSFileManager defaultManager] fileExistsAtPath:latestLog]))
117         return nil;
118
119     if (!previouslySeen) {
120         [defaults setInteger:year  forKey:@"LatestCrashReportYear"];
121         [defaults setInteger:month forKey:@"LatestCrashReportMonth"];
122         [defaults setInteger:day   forKey:@"LatestCrashReportDay"];
123         [defaults setInteger:hours forKey:@"LatestCrashReportHours"];
124     }
125     return latestLog;
126 }
127
128 - (NSString *)_latestCrashLogPath
129 {
130     return [self _latestCrashLogPathPreviouslySeen:YES];
131 }
132
133 - (void)showDialogAndSendLogIfDesired
134 {
135     // This pref key doesn't exists? this VLC is an upgrade, and this crash log come from previous version
136     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
137     BOOL areCrashLogsTooOld = ![defaults integerForKey:@"LatestCrashReportYear"];
138     NSString * latestLog = [self _latestCrashLogPathPreviouslySeen:NO];
139     if (latestLog && !areCrashLogsTooOld) {
140         if ([defaults integerForKey:@"AlwaysSendCrashReports"] > 0)
141             [self _sendCrashLog:[NSString stringWithContentsOfFile: [self _latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [_crashrep_fld string]];
142         else if ([defaults integerForKey:@"AlwaysSendCrashReports"] == 0) {
143             [NSBundle loadNibNamed:@"CrashReporter" owner:self];
144             [NSApp runModalForWindow:_crashrep_win];
145         } else {
146             if ([self.delegate respondsToSelector: @selector(reporterFinishedAction:)])
147                 [self.delegate reporterFinishedAction: self];
148         }
149     }
150 }
151
152 #pragma mark - UI interaction
153
154 - (IBAction)buttonAction:(id)sender
155 {
156     [NSApp stopModal];
157     [_crashrep_win orderOut: sender];
158     if (sender == _crashrep_send_btn) {
159         [self _sendCrashLog:[NSString stringWithContentsOfFile: [self _latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [_crashrep_fld string]];
160         if ([_crashrep_dontaskagain_ckb state])
161             [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"AlwaysSendCrashReports"];
162     } else {
163         if ([_crashrep_dontaskagain_ckb state])
164             [[NSUserDefaults standardUserDefaults] setInteger:-1 forKey:@"AlwaysSendCrashReports"];
165         if ([self.delegate respondsToSelector: @selector(reporterFinishedAction:)])
166             [self.delegate reporterFinishedAction: self];
167     }
168 }
169
170 #pragma mark - network handling
171
172 - (void)_sendCrashLog:(NSString *)crashLog withUserComment:(NSString *)userComment
173 {
174     NSString *urlStr = @"http://crash.videolan.org/crashlog/sendcrashreport.php";
175     NSURL *url = [NSURL URLWithString:urlStr];
176
177     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
178     [req setHTTPMethod:@"POST"];
179
180     NSString * email;
181     if ([_crashrep_includeEmail_ckb state] == NSOnState) {
182         ABPerson * contact = [[ABAddressBook sharedAddressBook] me];
183         ABMultiValue *emails = [contact valueForProperty:kABEmailProperty];
184         email = [emails valueAtIndex:[emails indexForIdentifier:
185                                       [emails primaryIdentifier]]];
186     }
187     else
188         email = [NSString string];
189
190     NSString *postBody;
191     postBody = [NSString stringWithFormat:@"CrashLog=%@&Comment=%@&Email=%@\r\n",
192                 [crashLog stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
193                 [userComment stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
194                 [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
195
196     [req setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
197
198     /* Released from delegate */
199     _crashLogURLConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
200 }
201
202 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
203 {
204     msg_Dbg(VLCIntf, "crash report successfully sent");
205     [_crashLogURLConnection release];
206     _crashLogURLConnection = nil;
207
208     if ([self.delegate respondsToSelector: @selector(reporterFinishedAction:)])
209         [self.delegate reporterFinishedAction: self];
210 }
211
212 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
213 {
214     msg_Warn(VLCIntf, "Error when sending the crash report: %s (%li)", [[error localizedDescription] UTF8String], [error code]);
215     [_crashLogURLConnection release];
216     _crashLogURLConnection = nil;
217
218     if ([self.delegate respondsToSelector: @selector(reporterFinishedAction:)])
219         [self.delegate reporterFinishedAction: self];
220 }
221
222 @end