Merge branch 'release/3.5.3'

This commit is contained in:
Andreas Linde 2014-02-12 15:09:18 +01:00
commit 15d57cce01
20 changed files with 513 additions and 111 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ build
!default.xcworkspace
!project.xcworkspace
xcuserdata
xccheckout
profile
*.moved-aside

View File

@ -578,6 +578,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
NSString *const kAuthorizationHost = @"authorize";
NSString *urlScheme = _urlScheme ? : [NSString stringWithFormat:@"ha%@", self.appIdentifier];
if(!([[url scheme] isEqualToString:urlScheme] && [[url host] isEqualToString:kAuthorizationHost])) {
BITHockeyLog(@"URL scheme for authentication doesn't match!");
return NO;
}
@ -611,6 +612,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
}
if(installationIdentifier){
BITHockeyLog(@"Authentication succeeded.");
if(NO == self.restrictApplicationUsage) {
[self dismissAuthenticationControllerAnimated:YES completion:nil];
}
@ -622,6 +624,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
}
} else {
//reset token
BITHockeyLog(@"Resetting authentication token");
[self storeInstallationIdentifier:nil withType:self.identificationType];
self.identified = NO;
if(self.identificationCompletion) {
@ -693,6 +696,8 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
return;
}
BITHockeyLog(@"Processing full size image for possible authentication");
unsigned char *buffer, *source;
source = (unsigned char *)malloc((unsigned long)fs.st_size);
if (read(fd, source, (unsigned long)fs.st_size) != fs.st_size) {
@ -720,7 +725,8 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
length = ntohl(length);
buffer += 4;
name = (unsigned char *)malloc(4);
name = (unsigned char *)malloc(5);
name[4] = 0;
memcpy(name, buffer, 4);
buffer += 4;
@ -754,7 +760,10 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44};
free(source);
if (result) {
BITHockeyLog(@"Authenticating using full size image information: %@", result);
[self handleOpenURL:[NSURL URLWithString:result] sourceApplication:nil annotation:nil];
} else {
BITHockeyLog(@"No authentication information found");
}
}

View File

@ -166,6 +166,20 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) {
@property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler;
/**
* Enable on device symbolication for system symbols
*
* By default, the SDK does not symbolicate on the device, since this can
* take a few seconds at each crash. Also note that symbolication on the
* device might not be able to retrieve all symbols.
*
* Enable if you want to analyze crashes on unreleased OS versions.
*
* Default: _NO_
*/
@property (nonatomic, assign, getter=isOnDeviceSymbolicationEnabled) BOOL enableOnDeviceSymbolication;
/**
* Set the callbacks that will be executed prior to program termination after a crash has occurred
*

View File

@ -125,17 +125,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
[[NSUserDefaults standardUserDefaults] setInteger:_crashManagerStatus forKey:kBITCrashManagerStatus];
}
// temporary directory for crashes grabbed from PLCrashReporter
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
_crashesDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:BITHOCKEY_IDENTIFIER];
if (![self.fileManager fileExistsAtPath:_crashesDir]) {
NSDictionary *attributes = [NSDictionary dictionaryWithObject: [NSNumber numberWithUnsignedLong: 0755] forKey: NSFilePosixPermissions];
NSError *theError = NULL;
[self.fileManager createDirectoryAtPath:_crashesDir withIntermediateDirectories: YES attributes: attributes error: &theError];
}
_crashesDir = bit_settingsDir();
_settingsFile = [_crashesDir stringByAppendingPathComponent:BITHOCKEY_CRASH_SETTINGS];
_analyzerInProgressFile = [_crashesDir stringByAppendingPathComponent:BITHOCKEY_CRASH_ANALYZER];
@ -308,7 +298,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
* @return The userID value
*/
- (NSString *)userIDForCrashReport {
NSString *userID = @"";
// first check the global keychain storage
NSString *userID = [self stringValueFromKeychainForKey:kBITHockeyMetaUserID] ?: @"";
#if HOCKEYSDK_FEATURE_AUTHENTICATOR
// if we have an identification from BITAuthenticator, use this as a default.
@ -337,7 +328,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
* @return The userName value
*/
- (NSString *)userNameForCrashReport {
NSString *username = @"";
// first check the global keychain storage
NSString *username = [self stringValueFromKeychainForKey:kBITHockeyMetaUserName] ?: @"";
if (self.delegate && [self.delegate respondsToSelector:@selector(userNameForCrashManager:)]) {
username = [self.delegate userNameForCrashManager:self] ?: @"";
@ -358,7 +350,8 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
* @return The userEmail value
*/
- (NSString *)userEmailForCrashReport {
NSString *useremail = @"";
// first check the global keychain storage
NSString *useremail = [self stringValueFromKeychainForKey:kBITHockeyMetaUserEmail] ?: @"";
#if HOCKEYSDK_FEATURE_AUTHENTICATOR
// if we have an identification from BITAuthenticator, use this as a default.
@ -667,8 +660,14 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
if (self.isMachExceptionHandlerEnabled) {
signalHandlerType = PLCrashReporterSignalHandlerTypeMach;
}
PLCrashReporterSymbolicationStrategy symbolicationStrategy = PLCrashReporterSymbolicationStrategyNone;
if (self.isOnDeviceSymbolicationEnabled) {
symbolicationStrategy = PLCrashReporterSymbolicationStrategyAll;
}
BITPLCrashReporterConfig *config = [[BITPLCrashReporterConfig alloc] initWithSignalHandlerType: signalHandlerType
symbolicationStrategy: PLCrashReporterSymbolicationStrategyAll];
symbolicationStrategy: symbolicationStrategy];
self.plCrashReporter = [[BITPLCrashReporter alloc] initWithConfiguration: config];
// Check if we previously crashed

View File

@ -33,6 +33,18 @@
#import <CrashReporter/CrashReporter.h>
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>
#import <mach-o/ldsyms.h>
#import <dlfcn.h>
#import <Availability.h>
#if defined(__OBJC2__)
#define SEL_NAME_SECT "__objc_methname"
#else
#define SEL_NAME_SECT "__cstring"
#endif
#import "BITCrashReportTextFormatter.h"
/*
@ -67,6 +79,112 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
return NSOrderedSame;
}
/**
* Validates that the given @a string terminates prior to @a limit.
*/
static const char *safer_string_read (const char *string, const char *limit) {
const char *p = string;
do {
if (p >= limit || p+1 >= limit) {
return NULL;
}
p++;
} while (*p != '\0');
return string;
}
/*
* The relativeAddress should be `<ecx/rsi/r1/x1 ...> - <image base>`, extracted from the crash report's thread
* and binary image list.
*
* For the (architecture-specific) registers to attempt, see:
* http://sealiesoftware.com/blog/archive/2008/09/22/objc_explain_So_you_crashed_in_objc_msgSend.html
*/
static const char *findSEL (const char *imageName, NSString *imageUUID, uint64_t relativeAddress) {
unsigned int images_count = _dyld_image_count();
for (unsigned int i = 0; i < images_count; ++i) {
intptr_t slide = _dyld_get_image_vmaddr_slide(i);
const struct mach_header *header = _dyld_get_image_header(i);
const struct mach_header_64 *header64 = (const struct mach_header_64 *) header;
const char *name = _dyld_get_image_name(i);
/* Image disappeared? */
if (name == NULL || header == NULL)
continue;
/* Check if this is the correct image. If we were being even more careful, we'd check the LC_UUID */
if (strcmp(name, imageName) != 0)
continue;
/* Determine whether this is a 64-bit or 32-bit Mach-O file */
BOOL m64 = NO;
if (header->magic == MH_MAGIC_64)
m64 = YES;
NSString *uuidString = nil;
const uint8_t *command;
uint32_t ncmds;
if (m64) {
command = (const uint8_t *)(header64 + 1);
ncmds = header64->ncmds;
} else {
command = (const uint8_t *)(header + 1);
ncmds = header->ncmds;
}
for (uint32_t idx = 0; idx < ncmds; ++idx) {
const struct load_command *load_command = (const struct load_command *)command;
if (load_command->cmd == LC_UUID) {
const struct uuid_command *uuid_command = (const struct uuid_command *)command;
const uint8_t *uuid = uuid_command->uuid;
uuidString = [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]]
lowercaseString];
break;
} else {
command += load_command->cmdsize;
}
}
// Check if this is the correct image by comparing the UUIDs
if (!uuidString || ![uuidString isEqualToString:imageUUID])
continue;
/* Fetch the __objc_methname section */
const char *methname_sect;
uint64_t methname_sect_size;
if (m64) {
methname_sect = getsectdatafromheader_64(header64, SEG_TEXT, SEL_NAME_SECT, &methname_sect_size);
} else {
uint32_t meth_size_32;
methname_sect = getsectdatafromheader(header, SEG_TEXT, SEL_NAME_SECT, &meth_size_32);
methname_sect_size = meth_size_32;
}
/* Apply the slide, as per getsectdatafromheader(3) */
methname_sect += slide;
if (methname_sect == NULL) {
return NULL;
}
/* Calculate the target address within this image, and verify that it is within __objc_methname */
const char *target = ((const char *)header) + relativeAddress;
const char *limit = methname_sect + methname_sect_size;
if (target < methname_sect || target >= limit) {
return NULL;
}
/* Read the actual method name */
return safer_string_read(target, limit);
}
return NULL;
}
/**
* Formats PLCrashReport data as human-readable text.
@ -282,6 +400,14 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
[text appendString: @"\n"];
BITPLCrashReportThreadInfo *crashed_thread = nil;
for (BITPLCrashReportThreadInfo *thread in report.threads) {
if (thread.crashed) {
crashed_thread = thread;
break;
}
}
/* Uncaught Exception */
if (report.hasExceptionInfo) {
[text appendFormat: @"Application Specific Information:\n"];
@ -289,6 +415,36 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
report.exceptionInfo.exceptionName, report.exceptionInfo.exceptionReason];
[text appendString: @"\n"];
} else if (crashed_thread != nil) {
// try to find the selector in case this was a crash in obj_msgSend
// we search this wether the crash happend in obj_msgSend or not since we don't have the symbol!
NSString *foundSelector = nil;
// search the registers value for the current arch
#if TARGET_IPHONE_SIMULATOR
if (lp64) {
foundSelector = [[self class] selectorForRegisterWithName:@"rsi" ofThread:crashed_thread report:report];
if (foundSelector == NULL)
foundSelector = [[self class] selectorForRegisterWithName:@"rdx" ofThread:crashed_thread report:report];
} else {
foundSelector = [[self class] selectorForRegisterWithName:@"ecx" ofThread:crashed_thread report:report];
}
#else
if (lp64) {
foundSelector = [[self class] selectorForRegisterWithName:@"x1" ofThread:crashed_thread report:report];
} else {
foundSelector = [[self class] selectorForRegisterWithName:@"r1" ofThread:crashed_thread report:report];
if (foundSelector == NULL)
foundSelector = [[self class] selectorForRegisterWithName:@"r2" ofThread:crashed_thread report:report];
}
#endif
if (foundSelector) {
[text appendFormat: @"Application Specific Information:\n"];
[text appendFormat: @"Selector name found in current argument registers: %@\n", foundSelector];
[text appendString: @"\n"];
}
}
/* If an exception stack trace is available, output an Apple-compatible backtrace. */
@ -308,12 +464,10 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
}
/* Threads */
BITPLCrashReportThreadInfo *crashed_thread = nil;
NSInteger maxThreadNum = 0;
for (BITPLCrashReportThreadInfo *thread in report.threads) {
if (thread.crashed) {
[text appendFormat: @"Thread %ld Crashed:\n", (long) thread.threadNumber];
crashed_thread = thread;
} else {
[text appendFormat: @"Thread %ld:\n", (long) thread.threadNumber];
}
@ -417,6 +571,41 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
return text;
}
/**
* Return the selector string of a given register name
*
* @param regName The name of the register to use for getting the address
* @param thread The crashed thread
* @param images NSArray of binary images
*
* @return The selector as a C string or NULL if no selector was found
*/
+ (NSString *)selectorForRegisterWithName:(NSString *)regName ofThread:(BITPLCrashReportThreadInfo *)thread report:(BITPLCrashReport *)report {
// get the address for the register
uint64_t regAddress = 0;
for (BITPLCrashReportRegisterInfo *reg in thread.registers) {
if ([reg.registerName isEqualToString:regName]) {
regAddress = reg.registerValue;
break;
}
}
if (regAddress == 0)
return nil;
BITPLCrashReportBinaryImageInfo *imageForRegAddress = [report imageForAddress:regAddress];
if (imageForRegAddress) {
// get the SEL
const char *foundSelector = findSEL([imageForRegAddress.imageName UTF8String], imageForRegAddress.imageUUID, regAddress - (uint64_t)imageForRegAddress.imageBaseAddress);
return [NSString stringWithUTF8String:foundSelector];
}
return nil;
}
/**
* Returns an array of app UUIDs and their architecture
* As a dictionary for each element

View File

@ -255,7 +255,7 @@
}
- (void)newFeedbackAction:(id)sender {
BITFeedbackComposeViewController *composeController = [[BITFeedbackComposeViewController alloc] init];
BITFeedbackComposeViewController *composeController = [self.manager feedbackComposeViewController];
UINavigationController *navController = [self.manager customNavigationControllerWithRootViewController:composeController
presentationStyle:UIModalPresentationFormSheet];
@ -366,6 +366,16 @@
} else {
[self dismissViewControllerAnimated:YES completion:^(void){}];
}
if (self.manager.delegate &&
[self.manager.delegate respondsToSelector:@selector(feedbackComposeViewController:didFinishWithResult:)]) {
[self.manager.delegate feedbackComposeViewController:composeViewController didFinishWithResult:composeResult];
} else if (self.manager.delegate && [self.manager.delegate respondsToSelector:@selector(feedbackComposeViewControllerDidFinish:)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
[self.manager.delegate feedbackComposeViewControllerDidFinish:composeViewController];
#pragma clang diagnostic pop
}
}

View File

@ -87,9 +87,9 @@ typedef NS_ENUM(NSInteger, BITFeedbackUserDataElement) {
reload the list content from the server and changing the users name or email if these
are allowed to be set.
It is also possible to invoke the user interface to compose a new message anywhere in your
It is also possible to invoke the user interface to compose a new message in your
own code, by calling `[BITFeedbackManager showFeedbackComposeView]` modally or adding
`[BITFeedackManager feedbackComposeViewController]` to push onto a navigation stack.
`[BITFeedbackManager feedbackComposeViewController]` to push onto a navigation stack.
If new messages are written while the device is offline, the SDK automatically retries to
send them once the app starts again or gets active again, or if the notification

View File

@ -53,7 +53,6 @@
@implementation BITFeedbackManager {
NSFileManager *_fileManager;
NSString *_feedbackDir;
NSString *_settingsFile;
id _appDidBecomeActiveObserver;
@ -89,18 +88,7 @@
_fileManager = [[NSFileManager alloc] init];
// temporary directory for crashes grabbed from PLCrashReporter
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
_feedbackDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:BITHOCKEY_IDENTIFIER];
if (![_fileManager fileExistsAtPath:_feedbackDir]) {
NSDictionary *attributes = [NSDictionary dictionaryWithObject: [NSNumber numberWithUnsignedLong: 0755] forKey: NSFilePosixPermissions];
NSError *theError = NULL;
[_fileManager createDirectoryAtPath:_feedbackDir withIntermediateDirectories: YES attributes: attributes error: &theError];
}
_settingsFile = [_feedbackDir stringByAppendingPathComponent:BITHOCKEY_FEEDBACK_SETTINGS];
_settingsFile = [bit_settingsDir() stringByAppendingPathComponent:BITHOCKEY_FEEDBACK_SETTINGS];
_userID = nil;
_userName = nil;
@ -218,7 +206,10 @@
- (BITFeedbackComposeViewController *)feedbackComposeViewController {
return [[BITFeedbackComposeViewController alloc] init];
BITFeedbackComposeViewController *composeViewController = [[BITFeedbackComposeViewController alloc] init];
// by default set the delegate to be identical to the one of BITFeedbackManager
[composeViewController setDelegate:self.delegate];
return composeViewController;
}
- (void)showFeedbackComposeView {
@ -271,63 +262,72 @@
}
}
- (BOOL)updateUserIDUsingDelegate {
- (BOOL)updateUserIDUsingKeychainAndDelegate {
BOOL availableViaDelegate = NO;
NSString *userID = [self stringValueFromKeychainForKey:kBITHockeyMetaUserID];
if ([BITHockeyManager sharedHockeyManager].delegate &&
[[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userIDForHockeyManager:componentManager:)]) {
NSString *userID = [[BITHockeyManager sharedHockeyManager].delegate
userID = [[BITHockeyManager sharedHockeyManager].delegate
userIDForHockeyManager:[BITHockeyManager sharedHockeyManager]
componentManager:self];
}
if (userID) {
availableViaDelegate = YES;
self.userID = userID;
}
}
return availableViaDelegate;
}
- (BOOL)updateUserNameUsingDelegate {
- (BOOL)updateUserNameUsingKeychainAndDelegate {
BOOL availableViaDelegate = NO;
NSString *userName = [self stringValueFromKeychainForKey:kBITHockeyMetaUserName];
if ([BITHockeyManager sharedHockeyManager].delegate &&
[[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userNameForHockeyManager:componentManager:)]) {
NSString *userName = [[BITHockeyManager sharedHockeyManager].delegate
userName = [[BITHockeyManager sharedHockeyManager].delegate
userNameForHockeyManager:[BITHockeyManager sharedHockeyManager]
componentManager:self];
}
if (userName) {
availableViaDelegate = YES;
self.userName = userName;
self.requireUserName = BITFeedbackUserDataElementDontShow;
}
}
return availableViaDelegate;
}
- (BOOL)updateUserEmailUsingDelegate {
- (BOOL)updateUserEmailUsingKeychainAndDelegate {
BOOL availableViaDelegate = NO;
NSString *userEmail = [self stringValueFromKeychainForKey:kBITHockeyMetaUserEmail];
if ([BITHockeyManager sharedHockeyManager].delegate &&
[[BITHockeyManager sharedHockeyManager].delegate respondsToSelector:@selector(userEmailForHockeyManager:componentManager:)]) {
NSString *userEmail = [[BITHockeyManager sharedHockeyManager].delegate
userEmail = [[BITHockeyManager sharedHockeyManager].delegate
userEmailForHockeyManager:[BITHockeyManager sharedHockeyManager]
componentManager:self];
}
if (userEmail) {
availableViaDelegate = YES;
self.userEmail = userEmail;
self.requireUserEmail = BITFeedbackUserDataElementDontShow;
}
}
return availableViaDelegate;
}
- (void)updateAppDefinedUserData {
[self updateUserIDUsingDelegate];
[self updateUserNameUsingDelegate];
[self updateUserEmailUsingDelegate];
[self updateUserIDUsingKeychainAndDelegate];
[self updateUserNameUsingKeychainAndDelegate];
[self updateUserEmailUsingKeychainAndDelegate];
// if both values are shown via the delegates, we never ever did ask and will never ever ask for user data
if (self.requireUserName == BITFeedbackUserDataElementDontShow &&
@ -339,9 +339,9 @@
#pragma mark - Local Storage
- (void)loadMessages {
BOOL userIDViaDelegate = [self updateUserIDUsingDelegate];
BOOL userNameViaDelegate = [self updateUserNameUsingDelegate];
BOOL userEmailViaDelegate = [self updateUserEmailUsingDelegate];
BOOL userIDViaDelegate = [self updateUserIDUsingKeychainAndDelegate];
BOOL userNameViaDelegate = [self updateUserNameUsingKeychainAndDelegate];
BOOL userEmailViaDelegate = [self updateUserEmailUsingKeychainAndDelegate];
if (![_fileManager fileExistsAtPath:_settingsFile])
return;

View File

@ -29,13 +29,14 @@
#import <Foundation/Foundation.h>
@class BITFeedbackManager;
@protocol BITFeedbackComposeViewControllerDelegate;
/**
* Delegate protocol which is notified about changes in the feedbackManager
* @TODO
* * move shouldShowUpdateAlert from feedbackManager here
*/
@protocol BITFeedbackManagerDelegate <NSObject>
@protocol BITFeedbackManagerDelegate <NSObject, BITFeedbackComposeViewControllerDelegate>
@optional

View File

@ -34,6 +34,8 @@ NSString *bit_URLEncodedString(NSString *inputString);
NSString *bit_URLDecodedString(NSString *inputString);
NSString *bit_base64String(NSData * data, unsigned long length);
NSString *bit_settingsDir(void);
BOOL bit_validateEmail(NSString *email);
NSString *bit_keychainHockeySDKServiceName(void);

View File

@ -76,6 +76,28 @@ NSString *bit_base64String(NSData * data, unsigned long length) {
#endif
}
NSString *bit_settingsDir(void) {
static NSString *settingsDir = nil;
static dispatch_once_t predSettingsDir;
dispatch_once(&predSettingsDir, ^{
NSFileManager *fileManager = [[NSFileManager alloc] init];
// temporary directory for crashes grabbed from PLCrashReporter
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
settingsDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:BITHOCKEY_IDENTIFIER];
if (![fileManager fileExistsAtPath:settingsDir]) {
NSDictionary *attributes = [NSDictionary dictionaryWithObject: [NSNumber numberWithUnsignedLong: 0755] forKey: NSFilePosixPermissions];
NSError *theError = NULL;
[fileManager createDirectoryAtPath:settingsDir withIntermediateDirectories: YES attributes: attributes error: &theError];
}
});
return settingsDir;
}
BOOL bit_validateEmail(NSString *email) {
NSString *emailRegex =
@"(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
@ -91,7 +113,13 @@ BOOL bit_validateEmail(NSString *email) {
}
NSString *bit_keychainHockeySDKServiceName(void) {
NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", bit_mainBundleIdentifier()];
static NSString *serviceName = nil;
static dispatch_once_t predServiceName;
dispatch_once(&predServiceName, ^{
serviceName = [NSString stringWithFormat:@"%@.HockeySDK", bit_mainBundleIdentifier()];
});
return serviceName;
}

View File

@ -312,9 +312,6 @@
/**
Reference to the initialized BITAuthenticator module
The authenticator is disabled by default. To enable it you need to set
`[BITAuthenticator authenticationType]` and `[BITAuthenticator validationType]`
Returns the BITAuthenticator instance initialized by BITHockeyManager
@see configureWithIdentifier:delegate:
@ -387,7 +384,74 @@
///-----------------------------------------------------------------------------
/// @name Meta
/// @name Additional meta data
///-----------------------------------------------------------------------------
/** Set the userid that should used in the SDK components
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
The value can be set at any time and will be stored in the keychain on the current
device only! To delete the value from the keychain set the value to `nil`.
This property is optional and can be used as an alternative to the delegate. If you
want to define specific data for each component, use the delegate instead which does
overwrite the values set by this property.
@see userName
@see userEmail
@see `[BITHockeyManagerDelegate userIDForHockeyManager:componentManager:]`
*/
@property (nonatomic, retain) NSString *userID;
/** Set the user name that should used in the SDK components
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
The value can be set at any time and will be stored in the keychain on the current
device only! To delete the value from the keychain set the value to `nil`.
This property is optional and can be used as an alternative to the delegate. If you
want to define specific data for each component, use the delegate instead which does
overwrite the values set by this property.
@warning When returning a non nil value, crash reports are not anonymous any more
and the crash alerts will not show the word "anonymous"!
@see userID
@see userEmail
@see `[BITHockeyManagerDelegate userNameForHockeyManager:componentManager:]`
*/
@property (nonatomic, retain) NSString *userName;
/** Set the users email address that should used in the SDK components
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
The value can be set at any time and will be stored in the keychain on the current
device only! To delete the value from the keychain set the value to `nil`.
This property is optional and can be used as an alternative to the delegate. If you
want to define specific data for each component, use the delegate instead which does
overwrite the values set by this property.
@warning When returning a non nil value, crash reports are not anonymous any more
and the crash alerts will not show the word "anonymous"!
@see userID
@see userName
@see `[BITHockeyManagerDelegate userEmailForHockeyManager:componentManager:]`
*/
@property (nonatomic, retain) NSString *userEmail;
///-----------------------------------------------------------------------------
/// @name SDK meta data
///-----------------------------------------------------------------------------
/**

View File

@ -34,6 +34,7 @@
#import "BITHockeyHelper.h"
#import "BITHockeyAppClient.h"
#import "BITKeychainUtils.h"
#if HOCKEYSDK_FEATURE_CRASH_REPORTER
@ -350,6 +351,56 @@
}
}
- (void)modifyKeychainUserValue:(NSString *)value forKey:(NSString *)key {
NSError *error = nil;
BOOL success = YES;
NSString *updateType = @"update";
if (value) {
success = [BITKeychainUtils storeUsername:key
andPassword:value
forServiceName:bit_keychainHockeySDKServiceName()
updateExisting:YES
accessibility:kSecAttrAccessibleWhenUnlockedThisDeviceOnly
error:&error];
} else {
updateType = @"delete";
if ([BITKeychainUtils getPasswordForUsername:key
andServiceName:bit_keychainHockeySDKServiceName()
error:&error]) {
success = [BITKeychainUtils deleteItemForUsername:key
andServiceName:bit_keychainHockeySDKServiceName()
error:&error];
}
}
if (!success) {
NSString *errorDescription = [error description] ?: @"";
BITHockeyLog(@"ERROR: Couldn't %@ key %@ in the keychain. %@", updateType, key, errorDescription);
}
}
- (void)setUserID:(NSString *)userID {
// always set it, since nil value will trigger removal of the keychain entry
_userID = userID;
[self modifyKeychainUserValue:userID forKey:kBITHockeyMetaUserID];
}
- (void)setUserName:(NSString *)userName {
// always set it, since nil value will trigger removal of the keychain entry
_userName = userName;
[self modifyKeychainUserValue:userName forKey:kBITHockeyMetaUserName];
}
- (void)setUserEmail:(NSString *)userEmail {
// always set it, since nil value will trigger removal of the keychain entry
_userEmail = userEmail;
[self modifyKeychainUserValue:userEmail forKey:kBITHockeyMetaUserEmail];
}
- (void)testIdentifier {
if (!_appIdentifier || [self isAppStoreEnvironment]) {
return;

View File

@ -76,21 +76,22 @@
///-----------------------------------------------------------------------------
/**
* Implement to force the usage of the live identifier
*
* This is useful if you are e.g. distributing an enterprise app inside your company
* and want to use the `liveIdentifier` for that even though it is not running from
* the App Store.
*
* Example:
* - (BOOL)shouldUseLiveIdentifierForHockeyManager:(BITHockeyManager *)hockeyManager {
* #ifdef (CONFIGURATION_AppStore)
* return YES;
* #endif
* return NO;
* }
*
* @param hockeyManager BITHockeyManager instance
Implement to force the usage of the live identifier
This is useful if you are e.g. distributing an enterprise app inside your company
and want to use the `liveIdentifier` for that even though it is not running from
the App Store.
Example:
- (BOOL)shouldUseLiveIdentifierForHockeyManager:(BITHockeyManager *)hockeyManager {
#ifdef (CONFIGURATION_AppStore)
return YES;
#endif
return NO;
}
@param hockeyManager BITHockeyManager instance
*/
- (BOOL)shouldUseLiveIdentifierForHockeyManager:(BITHockeyManager *)hockeyManager;
@ -121,13 +122,14 @@
/** Return the userid that should used in the SDK components
Right now this is used by the `BITCrashMananger` to attach to a crash report.
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
In addition, if this returns not nil for `BITFeedbackManager` the user will
not be asked for any user details by the component, including useerName or userEmail.
You can find out the component requesting the userID like this:
- (NSString *)userIDForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager {
if (componentManager == hockeyManager.feedbackManager) {
return UserIDForFeedback;
@ -138,27 +140,32 @@
}
}
For crash reports, this delegate is invoked on the startup after the crash!
Alternatively you can also use `[BITHockeyManager userID]` which will cache the value in the keychain.
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@param hockeyManager The `BITHockeyManager` HockeyManager instance invoking this delegate
@param componentManager The `BITHockeyBaseManager` component instance invoking this delegate, can be `BITCrashManager` or `BITFeedbackManager`
@see userNameForHockeyManager:componentManager:
@see userEmailForHockeyManager:componentManager:
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@see [BITHockeyManager userID]
*/
- (NSString *)userIDForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager;
/** Return the user name that should used in the SDK components
Right now this is used by the `BITCrashMananger` to attach to a crash report.
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
In addition, if this returns not nil for `BITFeedbackManager` the user will
not be asked for any user details by the component, including useerName or userEmail.
You can find out the component requesting the user name like this:
- (NSString *)userNameForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager {
if (componentManager == hockeyManager.feedbackManager) {
return UserNameForFeedback;
@ -169,26 +176,32 @@
}
}
For crash reports, this delegate is invoked on the startup after the crash!
Alternatively you can also use `[BITHockeyManager userName]` which will cache the value in the keychain.
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@param hockeyManager The `BITHockeyManager` HockeyManager instance invoking this delegate
@param componentManager The `BITHockeyBaseManager` component instance invoking this delegate, can be `BITCrashManager` or `BITFeedbackManager`
@see userIDForHockeyManager:componentManager:
@see userEmailForHockeyManager:componentManager:
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@see [BITHockeyManager userName]
*/
- (NSString *)userNameForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager;
/** Return the users email address that should used in the SDK components
Right now this is used by the `BITCrashMananger` to attach to a crash report.
Right now this is used by the `BITCrashManager` to attach to a crash report.
`BITFeedbackManager` uses it too for assigning the user to a discussion thread.
In addition, if this returns not nil for `BITFeedbackManager` the user will
not be asked for any user details by the component, including useerName or userEmail.
You can find out the component requesting the user email like this:
- (NSString *)userEmailForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager {
if (componentManager == hockeyManager.feedbackManager) {
return UserEmailForFeedback;
@ -199,13 +212,18 @@
}
}
For crash reports, this delegate is invoked on the startup after the crash!
Alternatively you can also use `[BITHockeyManager userEmail]` which will cache the value in the keychain.
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@param hockeyManager The `BITHockeyManager` HockeyManager instance invoking this delegate
@param componentManager The `BITHockeyBaseManager` component instance invoking this delegate, can be `BITCrashManager` or `BITFeedbackManager`
@see userIDForHockeyManager:componentManager:
@see userNameForHockeyManager:componentManager:
@warning When returning a non nil value for the `BITCrashManager` component, crash reports
are not anonymous any more and the crash alerts will not show the word "anonymous"!
@see [BITHockeyManager userEmail]
*/
- (NSString *)userEmailForHockeyManager:(BITHockeyManager *)hockeyManager componentManager:(BITHockeyBaseManager *)componentManager;

View File

@ -43,6 +43,10 @@
#define BITHOCKEY_USAGE_DATA @"BITUpdateManager.plist"
#define kBITHockeyMetaUserName @"BITHockeyMetaUserName"
#define kBITHockeyMetaUserEmail @"BITHockeyMetaUserEmail"
#define kBITHockeyMetaUserID @"BITHockeyMetaUserID"
#define kBITUpdateInstalledUUID @"BITUpdateInstalledUUID"
#define kBITUpdateInstalledVersionID @"BITUpdateInstalledVersionID"
#define kBITUpdateCurrentCompanyName @"BITUpdateCurrentCompanyName"

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'HockeySDK'
s.version = '3.5.2'
s.version = '3.5.3'
s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and analyze your test coverage with HockeyApp.'
s.description = <<-DESC
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
DESC
s.homepage = 'http://hockeyapp.net/'
s.documentation_url = 'http://hockeyapp.net/help/sdk/ios/3.5.2/'
s.documentation_url = 'http://hockeyapp.net/help/sdk/ios/3.5.3/'
s.license = 'MIT'
s.author = { 'Andreas Linde' => 'mail@andreaslinde.de', 'Thomas Dohmke' => "thomas@dohmke.de" }
@ -24,7 +24,7 @@ Pod::Spec.new do |s|
s.frameworks = 'CoreText', 'QuartzCore', 'SystemConfiguration', 'CoreGraphics', 'UIKit', 'Security'
s.ios.vendored_frameworks = 'Vendor/CrashReporter.framework'
s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\"" BITHOCKEY_BUILD="@\\"25\\""} }
s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => %{$(inherited) BITHOCKEY_VERSION="@\\"#{s.version}\\"" BITHOCKEY_BUILD="@\\"26\\""} }
s.resource_bundle = { 'HockeySDKResources' => ['Resources/*.png', 'Resources/*.lproj'] }
s.preserve_paths = 'Resources', 'Support'

View File

@ -1,6 +1,6 @@
## Version 3.5.2
## Version 3.5.3
- [Changelog](http://www.hockeyapp.net/help/sdk/ios/3.5.2/docs/docs/Changelog.html)
- [Changelog](http://www.hockeyapp.net/help/sdk/ios/3.5.3/docs/docs/Changelog.html)
## Introduction
@ -31,10 +31,10 @@ The main SDK class is `BITHockeyManager`. It initializes all modules and provide
## Installation & Setup
- [Installation & Setup](http://www.hockeyapp.net/help/sdk/ios/3.5.2/docs/docs/Guide-Installation-Setup.html) (Recommended)
- [Installation & Setup Advanced](http://www.hockeyapp.net/help/sdk/ios/3.5.2/docs/docs/Guide-Installation-Setup-Advanced.html) (Using Git submodule and Xcode sub-project)
- [Identify and authenticate users of Ad-Hoc or Enterprise builds](http://www.hockeyapp.net/help/sdk/ios/3.5.2/docs/docs/HowTo-Authenticating-Users-on-iOS.html)
- [Migration from previous SDK Versions](http://www.hockeyapp.net/help/sdk/ios/3.5.2/docs/docs/Guide-Migration-Kits.html)
- [Installation & Setup](http://www.hockeyapp.net/help/sdk/ios/3.5.3/docs/docs/Guide-Installation-Setup.html) (Recommended)
- [Installation & Setup Advanced](http://www.hockeyapp.net/help/sdk/ios/3.5.3/docs/docs/Guide-Installation-Setup-Advanced.html) (Using Git submodule and Xcode sub-project)
- [Identify and authenticate users of Ad-Hoc or Enterprise builds](http://www.hockeyapp.net/help/sdk/ios/3.5.3/docs/docs/HowTo-Authenticating-Users-on-iOS.html)
- [Migration from previous SDK Versions](http://www.hockeyapp.net/help/sdk/ios/3.5.3/docs/docs/Guide-Migration-Kits.html)
- [Mac Desktop Uploader](http://support.hockeyapp.net/kb/how-tos/how-to-upload-to-hockeyapp-on-a-mac)
@ -48,4 +48,4 @@ This documentation provides integrated help in Xcode for all public APIs and a s
3. Copy the content into ~`/Library/Developer/Shared/Documentation/DocSets`
The documentation is also available via the following URL: [http://hockeyapp.net/help/sdk/ios/3.5.2/](http://hockeyapp.net/help/sdk/ios/3.5.2/)
The documentation is also available via the following URL: [http://hockeyapp.net/help/sdk/ios/3.5.3/](http://hockeyapp.net/help/sdk/ios/3.5.3/)

View File

@ -1,7 +1,7 @@
#include "HockeySDK.xcconfig"
BUILD_NUMBER = 25
VERSION_STRING = 3.5.2
BUILD_NUMBER = 26
VERSION_STRING = 3.5.3
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) BITHOCKEY_VERSION="@\""$(VERSION_STRING)"\"" BITHOCKEY_BUILD="@\""$(BUILD_NUMBER)"\""
BIT_ARM_ARCHS = armv7 armv7s arm64
BIT_SIM_ARCHS = x86_64 i386

View File

@ -1,3 +1,13 @@
## Version 3.5.3
- [NEW] Crash Reports now provide the selector name e.g. for crashes in `objc_MsgSend`
- [NEW] Add setter for global `userID`, `userName`, `userEmail`. Can be used instead of the delegates.
- [UPDATE] On device symbolication is now optional, disabled by default
- [BUGFIX] Fix for automatic authentication not always working correctly
- [BUGFIX] `BITFeedbackComposeViewControllerDelegate` now also works for compose view controller used by the feedback list view
- [BUGFIX] Fix typos in documentation
<br /><br/>
## Version 3.5.2
- [UPDATE] Make sure a log message appears in the console if the SDK is not setup on the main thread

View File

@ -30,6 +30,8 @@ Previous versions of HockeySDK for iOS used the response of the method `UIDevice
The app opens Safari and asks the user to log in to his HockeyApp account.
The strategies **BITAuthenticatorIdentificationTypeDevice** and **BITAuthenticatorIdentificationTypeWebAuth** also allow for automatic authentication as explained [here](http://hockeyapp.net/blog/2014/01/31/automatic-authentication-ios.html).
After setting up one of those strategies, you need to trigger the authentication process by calling
[[BITHockeyManager sharedHockeyManager].authenticator authenticateInstallation];