Commit 4cec2c76 authored by Felix Paul Kühne's avatar Felix Paul Kühne

macosx: add and deploy QuincyKit for crashlog reporting

This is for evaluation only. We might want to revert to our previous system for the release.
parent f66dacb3
This diff is collapsed.
......@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
7D3F6525188051F2005776C4 /* BWQuincyMain.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D3F6523188051F2005776C4 /* BWQuincyMain.xib */; };
7D8BB0B71830311300FAE9B7 /* DebugMessageVisualizer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D8BB0B51830311300FAE9B7 /* DebugMessageVisualizer.xib */; };
7D8BB0BA1830367200FAE9B7 /* PlaylistMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D8BB0B81830367200FAE9B7 /* PlaylistMenu.xib */; };
7D8BB0C01830393300FAE9B7 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D8BB0BE1830393300FAE9B7 /* MainWindow.xib */; };
......@@ -469,6 +470,11 @@
5CCED71314C0D4A90057F8D1 /* ExtensionsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ExtensionsManager.m; path = ../../../modules/gui/macosx/ExtensionsManager.m; sourceTree = SOURCE_ROOT; };
5CCED71414C0D4A90057F8D1 /* VLCUIWidgets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCUIWidgets.h; path = ../../../modules/gui/macosx/VLCUIWidgets.h; sourceTree = SOURCE_ROOT; };
5CCED71514C0D4A90057F8D1 /* VLCUIWidgets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCUIWidgets.m; path = ../../../modules/gui/macosx/VLCUIWidgets.m; sourceTree = SOURCE_ROOT; };
7D3F6524188051F2005776C4 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Resources/English.lproj/BWQuincyMain.xib; sourceTree = "<group>"; };
7D3F652618805297005776C4 /* BWQuincyManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BWQuincyManager.h; path = ../../../modules/gui/macosx/BWQuincyManager.h; sourceTree = "<group>"; };
7D3F652718805297005776C4 /* BWQuincyManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BWQuincyManager.m; path = ../../../modules/gui/macosx/BWQuincyManager.m; sourceTree = "<group>"; };
7D3F652818805297005776C4 /* BWQuincyUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BWQuincyUI.h; path = ../../../modules/gui/macosx/BWQuincyUI.h; sourceTree = "<group>"; };
7D3F652918805297005776C4 /* BWQuincyUI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BWQuincyUI.m; path = ../../../modules/gui/macosx/BWQuincyUI.m; sourceTree = "<group>"; };
7D7DC2CA182E86D9008C9E3E /* darwinvlc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = darwinvlc.c; path = ../../../bin/darwinvlc.c; sourceTree = "<group>"; };
7D8BB0B318302AC000FAE9B7 /* DebugMessageVisualizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DebugMessageVisualizer.h; path = ../../../modules/gui/macosx/DebugMessageVisualizer.h; sourceTree = "<group>"; };
7D8BB0B418302AC000FAE9B7 /* DebugMessageVisualizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = DebugMessageVisualizer.m; path = ../../../modules/gui/macosx/DebugMessageVisualizer.m; sourceTree = "<group>"; };
......@@ -1172,6 +1178,17 @@
name = Products;
sourceTree = "<group>";
};
7D3F652A1880529C005776C4 /* QuincyKit */ = {
isa = PBXGroup;
children = (
7D3F652618805297005776C4 /* BWQuincyManager.h */,
7D3F652718805297005776C4 /* BWQuincyManager.m */,
7D3F652818805297005776C4 /* BWQuincyUI.h */,
7D3F652918805297005776C4 /* BWQuincyUI.m */,
);
name = QuincyKit;
sourceTree = "<group>";
};
8E445F86047289E50059A3A7 /* fileicons */ = {
isa = PBXGroup;
children = (
......@@ -1511,6 +1528,7 @@
CC0CD0E513DE0FC600B0D90D /* Imported Code */ = {
isa = PBXGroup;
children = (
7D3F652A1880529C005776C4 /* QuincyKit */,
CC6D8D9C0A878DED006F2BBE /* AppleRemote.h */,
CC6D8D9D0A878DED006F2BBE /* AppleRemote.m */,
CC0CD0E413DE0EB500B0D90D /* PXSourceList */,
......@@ -1792,6 +1810,7 @@
7D8BB0B81830367200FAE9B7 /* PlaylistMenu.xib */,
7D8BB0BE1830393300FAE9B7 /* MainWindow.xib */,
7DE88F281831396A00CD9DA2 /* PlaylistAccessoryView.xib */,
7D3F6523188051F2005776C4 /* BWQuincyMain.xib */,
);
name = xibs;
sourceTree = "<group>";
......@@ -1854,6 +1873,7 @@
CC402EB90E00ABBB006A4BA4 /* vlc.scriptSuite in Resources */,
CC402EBA0E00ABBB006A4BA4 /* vlc.scriptTerminology in Resources */,
CC402EBB0E00ABBB006A4BA4 /* README.MacOSX.rtf in Resources */,
7D3F6525188051F2005776C4 /* BWQuincyMain.xib in Resources */,
CC402F3C0E00ABBB006A4BA4 /* noart.png in Resources */,
CC04323D13B246DF00D7D52E /* effects-double-buttons_dark.png in Resources */,
CC04323E13B246DF00D7D52E /* effects-double-buttons-blue_dark.png in Resources */,
......@@ -2277,6 +2297,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXVariantGroup section */
7D3F6523188051F2005776C4 /* BWQuincyMain.xib */ = {
isa = PBXVariantGroup;
children = (
7D3F6524188051F2005776C4 /* English */,
);
name = BWQuincyMain.xib;
sourceTree = "<group>";
};
7D8BB0B51830311300FAE9B7 /* DebugMessageVisualizer.xib */ = {
isa = PBXVariantGroup;
children = (
......
/*
* Author: Andreas Linde <mail@andreaslinde.de>
* Kent Sutherland
*
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Cocoa/Cocoa.h>
typedef enum CrashAlertType {
CrashAlertTypeSend = 0,
CrashAlertTypeFeedback = 1,
} CrashAlertType;
typedef enum CrashReportStatus {
// This app version is set to discontinued, no new crash reports accepted by the server
CrashReportStatusFailureVersionDiscontinued = -30,
// XML: Sender ersion string contains not allowed characters, only alphanumberical including space and . are allowed
CrashReportStatusFailureXMLSenderVersionNotAllowed = -21,
// XML: Version string contains not allowed characters, only alphanumberical including space and . are allowed
CrashReportStatusFailureXMLVersionNotAllowed = -20,
// SQL for adding a symoblicate todo entry in the database failed
CrashReportStatusFailureSQLAddSymbolicateTodo = -18,
// SQL for adding crash log in the database failed
CrashReportStatusFailureSQLAddCrashlog = -17,
// SQL for adding a new version in the database failed
CrashReportStatusFailureSQLAddVersion = -16,
// SQL for checking if the version is already added in the database failed
CrashReportStatusFailureSQLCheckVersionExists = -15,
// SQL for creating a new pattern for this bug and set amount of occurrances to 1 in the database failed
CrashReportStatusFailureSQLAddPattern = -14,
// SQL for checking the status of the bugfix version in the database failed
CrashReportStatusFailureSQLCheckBugfixStatus = -13,
// SQL for updating the occurances of this pattern in the database failed
CrashReportStatusFailureSQLUpdatePatternOccurances = -12,
// SQL for getting all the known bug patterns for the current app version in the database failed
CrashReportStatusFailureSQLFindKnownPatterns = -11,
// SQL for finding the bundle identifier in the database failed
CrashReportStatusFailureSQLSearchAppName = -10,
// the post request didn't contain valid data
CrashReportStatusFailureInvalidPostData = -3,
// incoming data may not be added, because e.g. bundle identifier wasn't found
CrashReportStatusFailureInvalidIncomingData = -2,
// database cannot be accessed, check hostname, username, password and database name settings in config.php
CrashReportStatusFailureDatabaseNotAvailable = -1,
CrashReportStatusUnknown = 0,
CrashReportStatusAssigned = 1,
CrashReportStatusSubmitted = 2,
CrashReportStatusAvailable = 3,
} CrashReportStatus;
@class BWQuincyUI;
@protocol BWQuincyManagerDelegate <NSObject>
@required
// Invoked once the modal sheets are gone
- (void) showMainApplicationWindow;
@optional
// Return the description the crashreport should contain, empty by default. The string will automatically be wrapped into <[DATA[ ]]>, so make sure you don't do that in your string.
-(NSString *) crashReportDescription;
// Return the userid the crashreport should contain, empty by default
-(NSString *) crashReportUserID;
// Return the contact value (e.g. email) the crashreport should contain, empty by default
-(NSString *) crashReportContact;
@end
@interface BWQuincyManager : NSObject
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
<NSXMLParserDelegate>
#endif
{
CrashReportStatus _serverResult;
NSInteger _statusCode;
NSMutableString *_contentOfProperty;
id<BWQuincyManagerDelegate> _delegate;
NSString *_submissionURL;
NSString *_companyName;
NSString *_appIdentifier;
BOOL _autoSubmitCrashReport;
NSString *_crashFile;
BWQuincyUI *_quincyUI;
}
- (NSString*) modelVersion;
+ (BWQuincyManager *)sharedQuincyManager;
// submission URL defines where to send the crash reports to (required)
@property (nonatomic, retain) NSString *submissionURL;
// defines the company name to be shown in the crash reporting dialog
@property (nonatomic, retain) NSString *companyName;
// delegate is required
@property (nonatomic, assign) id <BWQuincyManagerDelegate> delegate;
// if YES, the crash report will be submitted without asking the user
// if NO, the user will be asked if the crash report can be submitted (default)
@property (nonatomic, assign, getter=isAutoSubmitCrashReport) BOOL autoSubmitCrashReport;
///////////////////////////////////////////////////////////////////////////////////////////////////
// settings
// If you want to use HockeyApp instead of your own server, this is required
@property (nonatomic, retain) NSString *appIdentifier;
- (void) cancelReport;
- (void) sendReportCrash:(NSString*)crashContent
description:(NSString*)description;
- (NSString *) applicationName;
- (NSString *) applicationVersionString;
- (NSString *) applicationVersion;
@end
/*
* Author: Andreas Linde <mail@andreaslinde.de>
* Kent Sutherland
*
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import "BWQuincyManager.h"
#import "BWQuincyUI.h"
#import <sys/sysctl.h>
#define SDK_NAME @"Quincy"
#define SDK_VERSION @"2.1.6"
@interface BWQuincyManager(private)
- (void) startManager;
- (void) _postXML:(NSString*)xml toURL:(NSURL*)url;
- (void) searchCrashLogFile:(NSString *)path;
- (BOOL) hasPendingCrashReport;
- (void) returnToMainApplication;
@end
@implementation BWQuincyManager
@synthesize delegate = _delegate;
@synthesize submissionURL = _submissionURL;
@synthesize companyName = _companyName;
@synthesize appIdentifier = _appIdentifier;
@synthesize autoSubmitCrashReport = _autoSubmitCrashReport;
+ (BWQuincyManager *)sharedQuincyManager {
static BWQuincyManager *quincyManager = nil;
if (quincyManager == nil) {
quincyManager = [[BWQuincyManager alloc] init];
}
return quincyManager;
}
- (id) init {
if ((self = [super init])) {
_serverResult = CrashReportStatusFailureDatabaseNotAvailable;
_quincyUI = nil;
_submissionURL = nil;
_appIdentifier = nil;
_crashFile = nil;
self.delegate = nil;
self.companyName = @"";
}
return self;
}
- (void)dealloc {
_companyName = nil;
_delegate = nil;
_submissionURL = nil;
_appIdentifier = nil;
[_crashFile release];
[_quincyUI release];
[super dealloc];
}
- (void) searchCrashLogFile:(NSString *)path {
NSFileManager* fman = [NSFileManager defaultManager];
NSError* error;
NSMutableArray* filesWithModificationDate = [NSMutableArray array];
NSArray* crashLogFiles = [fman contentsOfDirectoryAtPath:path error:&error];
NSEnumerator* filesEnumerator = [crashLogFiles objectEnumerator];
NSString* crashFile;
while((crashFile = [filesEnumerator nextObject])) {
NSString* crashLogPath = [path stringByAppendingPathComponent:crashFile];
NSDate* modDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:crashLogPath error:&error] fileModificationDate];
[filesWithModificationDate addObject:[NSDictionary dictionaryWithObjectsAndKeys:crashFile,@"name",crashLogPath,@"path",modDate,@"modDate",nil]];
}
NSSortDescriptor* dateSortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"modDate" ascending:YES] autorelease];
NSArray* sortedFiles = [filesWithModificationDate sortedArrayUsingDescriptors:[NSArray arrayWithObject:dateSortDescriptor]];
NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@", [self applicationName]];
NSArray* filteredFiles = [sortedFiles filteredArrayUsingPredicate:filterPredicate];
_crashFile = [[[filteredFiles valueForKeyPath:@"path"] lastObject] copy];
}
#pragma mark -
#pragma mark setter
- (void)setSubmissionURL:(NSString *)anSubmissionURL {
if (_submissionURL != anSubmissionURL) {
[_submissionURL release];
_submissionURL = [anSubmissionURL copy];
}
[self performSelector:@selector(startManager) withObject:nil afterDelay:0.1f];
}
- (void)setAppIdentifier:(NSString *)anAppIdentifier {
if (_appIdentifier != anAppIdentifier) {
[_appIdentifier release];
_appIdentifier = [anAppIdentifier copy];
}
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
}
- (void)storeLastCrashDate:(NSDate *) date {
[[NSUserDefaults standardUserDefaults] setValue:date forKey:@"CrashReportSender.lastCrashDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSDate *)loadLastCrashDate {
NSDate *date = [[NSUserDefaults standardUserDefaults] valueForKey:@"CrashReportSender.lastCrashDate"];
return date ?: [NSDate distantPast];
}
- (void)storeAppVersion:(NSString *) version {
[[NSUserDefaults standardUserDefaults] setValue:version forKey:@"CrashReportSender.appVersion"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)loadAppVersion {
NSString *appVersion = [[NSUserDefaults standardUserDefaults] valueForKey:@"CrashReportSender.appVersion"];
return appVersion ?: nil;
}
#pragma mark -
#pragma mark GetCrashData
- (BOOL) hasPendingCrashReport {
BOOL returnValue = NO;
NSString *appVersion = [self loadAppVersion];
NSDate *lastCrashDate = [self loadLastCrashDate];
if (!appVersion || ![appVersion isEqualToString:[self applicationVersion]] || [lastCrashDate isEqualToDate:[NSDate distantPast]]) {
[self storeAppVersion:[self applicationVersion]];
[self storeLastCrashDate:[NSDate date]];
return NO;
}
NSArray* libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, TRUE);
// Snow Leopard is having the log files in another location
[self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/DiagnosticReports"]];
if (_crashFile == nil) {
[self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/CrashReporter"]];
if (_crashFile == nil) {
NSString *sandboxFolder = [NSString stringWithFormat:@"/Containers/%@/Data/Library", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]];
if ([[libraryDirectories lastObject] rangeOfString:sandboxFolder].location != NSNotFound) {
NSString *libFolderName = [[libraryDirectories lastObject] stringByReplacingOccurrencesOfString:sandboxFolder withString:@""];
[self searchCrashLogFile:[libFolderName stringByAppendingPathComponent:@"Logs/DiagnosticReports"]];
}
}
// Search machine diagnostic reports directory
if (_crashFile == nil) {
NSArray* libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSLocalDomainMask, TRUE);
[self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/DiagnosticReports"]];
if (_crashFile == nil) {
[self searchCrashLogFile:[[libraryDirectories lastObject] stringByAppendingPathComponent:@"Logs/CrashReporter"]];
}
}
}
if (_crashFile) {
NSError* error;
NSDate *crashLogModificationDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:_crashFile error:&error] fileModificationDate];
unsigned long long crashLogFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:_crashFile error:&error] fileSize];
if ([crashLogModificationDate compare: lastCrashDate] == NSOrderedDescending && crashLogFileSize > 0) {
[self storeLastCrashDate:crashLogModificationDate];
returnValue = YES;
}
}
return returnValue;
}
- (void) returnToMainApplication {
if ( self.delegate != nil && [self.delegate respondsToSelector:@selector(showMainApplicationWindow)])
[self.delegate showMainApplicationWindow];
}
- (void) startManager {
if ([self hasPendingCrashReport]) {
if (!self.autoSubmitCrashReport) {
_quincyUI = [[BWQuincyUI alloc] initWithManager:self crashFile:_crashFile companyName:_companyName applicationName:[self applicationName]];
[_quincyUI askCrashReportDetails];
} else {
NSError* error = nil;
NSString *crashLogs = [NSString stringWithContentsOfFile:_crashFile encoding:NSUTF8StringEncoding error:&error];
if (!error) {
NSString *lastCrash = [[crashLogs componentsSeparatedByString: @"**********\n\n"] lastObject];
NSString* description = @"";
if (_delegate && [_delegate respondsToSelector:@selector(crashReportDescription)]) {
description = [_delegate crashReportDescription];
}
[self sendReportCrash:lastCrash description:description];
} else {
[self returnToMainApplication];
}
}
} else {
[self returnToMainApplication];
}
}
- (NSString*) modelVersion {
NSString * modelString = nil;
int modelInfo[2] = { CTL_HW, HW_MODEL };
size_t modelSize;
if (sysctl(modelInfo,
2,
NULL,
&modelSize,
NULL, 0) == 0) {
void * modelData = malloc(modelSize);
if (modelData) {
if (sysctl(modelInfo,
2,
modelData,
&modelSize,
NULL, 0) == 0) {
modelString = [NSString stringWithUTF8String:modelData];
}
free(modelData);
}
}
return modelString;
}
- (void) cancelReport {
[self returnToMainApplication];
}
- (void) sendReportCrash:(NSString*)crashContent
description:(NSString*)notes
{
NSString *userid = @"";
NSString *contact = @"";
SInt32 versionMajor, versionMinor, versionBugFix;
if (Gestalt(gestaltSystemVersionMajor, &versionMajor) != noErr) versionMajor = 0;
if (Gestalt(gestaltSystemVersionMinor, &versionMinor) != noErr) versionMinor= 0;
if (Gestalt(gestaltSystemVersionBugFix, &versionBugFix) != noErr) versionBugFix = 0;
NSString* xml = [NSString stringWithFormat:@"<crash><applicationname>%s</applicationname><bundleidentifier>%s</bundleidentifier><systemversion>%@</systemversion><senderversion>%@</senderversion><version>%@</version><platform>%@</platform><userid>%@</userid><contact>%@</contact><description><![CDATA[%@]]></description><log><![CDATA[%@]]></log></crash>",
[[self applicationName] UTF8String],
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"] UTF8String],
[NSString stringWithFormat:@"%i.%i.%i", versionMajor, versionMinor, versionBugFix],
[self applicationVersion],
[self applicationVersion],
[self modelVersion],
userid,
contact,
notes,
crashContent
];
[self returnToMainApplication];
[self _postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", xml] toURL:[NSURL URLWithString:self.submissionURL]];
}
- (void)_postXML:(NSString*)xml toURL:(NSURL*)url {
NSMutableURLRequest *request = nil;
NSString *boundary = @"----FOO";
if (self.appIdentifier) {
request = [NSMutableURLRequest requestWithURL:
[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes?sdk=%@&sdk_version=%@",
self.submissionURL,
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
SDK_NAME,
SDK_VERSION
]
]];
} else {
request = [NSMutableURLRequest requestWithURL:url];
}
[request setValue:@"Quincy/Mac" forHTTPHeaderField:@"User-Agent"];
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:@"POST"];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField:@"Content-type"];
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
if (self.appIdentifier) {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xmlstring\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
_serverResult = CrashReportStatusUnknown;
_statusCode = 200;
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = nil;
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
_statusCode = [response statusCode];
if (responseData != nil) {
if (_statusCode >= 200 && _statusCode < 400) {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:responseData];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser release];
}
}
}
#pragma mark NSXMLParser
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (qName) {
elementName = qName;
}
if ([elementName isEqualToString:@"result"]) {
_contentOfProperty = [NSMutableString string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (qName) {
elementName = qName;
}
if ([elementName isEqualToString:@"result"]) {
if ([_contentOfProperty intValue] > _serverResult) {
_serverResult = [_contentOfProperty intValue];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (_contentOfProperty) {
// If the current element is one whose content we care about, append 'string'
// to the property that holds the content of the current element.
if (string != nil) {
[_contentOfProperty appendString:string];
}
}
}
#pragma mark GetterSetter
- (NSString *) applicationName {
NSString *applicationName = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleExecutable"];
if (!applicationName)
applicationName = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleExecutable"];
return applicationName;
}
- (NSString*) applicationVersionString {
NSString* string = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleShortVersionString"];
if (!string)
string = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleShortVersionString"];
return string;
}
- (NSString *) applicationVersion {
NSString* string = [[[NSBundle mainBundle] localizedInfoDictionary] valueForKey: @"CFBundleVersion"];
if (!string)
string = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleVersion"];
return string;
}
@end
/*
* Author: Andreas Linde <mail@andreaslinde.de>
* Kent Sutherland
*
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation