From a0153e4205f6c1befcbe22402b6647b44794f552 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Thu, 6 Feb 2014 13:43:06 +0100 Subject: [PATCH 01/13] Improve BITFeedbackComposeViewControllerDelegate handling Goal: make BITFeedbackComposeViewControllerDelegate usable for the view controller used by BITFeedbackListViewController - The delegate is automatically set to the global BITHockeyManager delegate - When invoking your BITFeedbackComposeViewController, it is possible to overwrite the delegate --- Classes/BITFeedbackListViewController.m | 14 ++++++++++++-- Classes/BITFeedbackManager.m | 5 ++++- Classes/BITFeedbackManagerDelegate.h | 3 ++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Classes/BITFeedbackListViewController.m b/Classes/BITFeedbackListViewController.m index 633fca09..812634ca 100644 --- a/Classes/BITFeedbackListViewController.m +++ b/Classes/BITFeedbackListViewController.m @@ -255,8 +255,8 @@ - (void)setUserDataAction:(id)sender { } - (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 @@ - (void)feedbackComposeViewController:(BITFeedbackComposeViewController *)compos } 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 + } } diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 685cdc74..8d77606c 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -218,7 +218,10 @@ - (void)showFeedbackListView { - (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 { diff --git a/Classes/BITFeedbackManagerDelegate.h b/Classes/BITFeedbackManagerDelegate.h index dce3fb70..61b4c396 100644 --- a/Classes/BITFeedbackManagerDelegate.h +++ b/Classes/BITFeedbackManagerDelegate.h @@ -29,13 +29,14 @@ #import @class BITFeedbackManager; +@protocol BITFeedbackComposeViewControllerDelegate; /** * Delegate protocol which is notified about changes in the feedbackManager * @TODO * * move shouldShowUpdateAlert from feedbackManager here */ -@protocol BITFeedbackManagerDelegate +@protocol BITFeedbackManagerDelegate @optional From a9fda179da53591029a4f962e4b937aac3a8c4bb Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 16:25:40 +0100 Subject: [PATCH 02/13] Added more logging for BITAuthenticator --- Classes/BITAuthenticator.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index 0b908f5d..7634ac7a 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -578,6 +578,7 @@ - (BOOL) handleOpenURL:(NSURL *) url 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 @@ - (BOOL) handleOpenURL:(NSURL *) url } if(installationIdentifier){ + BITHockeyLog(@"Authentication succeeded."); if(NO == self.restrictApplicationUsage) { [self dismissAuthenticationControllerAnimated:YES completion:nil]; } @@ -622,6 +624,7 @@ - (BOOL) handleOpenURL:(NSURL *) url } } else { //reset token + BITHockeyLog(@"Resetting authentication token"); [self storeInstallationIdentifier:nil withType:self.identificationType]; self.identified = NO; if(self.identificationCompletion) { @@ -693,6 +696,8 @@ - (void)processFullSizeImage { 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) { @@ -754,7 +759,10 @@ - (void)processFullSizeImage { 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"); } } From 9123392d0fd99cea60b23720a4022349fa70605e Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 10 Feb 2014 17:57:12 +0100 Subject: [PATCH 03/13] Remove the word 'anywhere' from the header doc of `BITFeedbackManager` --- Classes/BITFeedbackManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/BITFeedbackManager.h b/Classes/BITFeedbackManager.h index 46b47494..01f58572 100644 --- a/Classes/BITFeedbackManager.h +++ b/Classes/BITFeedbackManager.h @@ -87,7 +87,7 @@ 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. From fad5e586c511af594f943fec434badfd52a7006d Mon Sep 17 00:00:00 2001 From: Thomas Dohmke Date: Mon, 10 Feb 2014 20:35:30 +0100 Subject: [PATCH 04/13] Make sure that the chunk name is null-terminated. --- Classes/BITAuthenticator.m | 3 +- .../xcshareddata/HockeySDK.xccheckout | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index 7634ac7a..4c53eab6 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -725,7 +725,8 @@ - (void)processFullSizeImage { length = ntohl(length); buffer += 4; - name = (unsigned char *)malloc(4); + name = (unsigned char *)malloc(5); + name[4] = 0; memcpy(name, buffer, 4); buffer += 4; diff --git a/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout b/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout new file mode 100644 index 00000000..3ecae9ae --- /dev/null +++ b/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + B576A34E-A0B3-4F0B-A0A2-4AE78B08404E + IDESourceControlProjectName + HockeySDK + IDESourceControlProjectOriginsDictionary + + 2E94B80A-CA5E-4B76-8E56-B671D00049A7 + ssh://github.com/bitstadium/HockeySDK-iOS.git + + IDESourceControlProjectPath + Support/HockeySDK.xcodeproj/project.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + 2E94B80A-CA5E-4B76-8E56-B671D00049A7 + ../../.. + + IDESourceControlProjectURL + ssh://github.com/bitstadium/HockeySDK-iOS.git + IDESourceControlProjectVersion + 110 + IDESourceControlProjectWCCIdentifier + 2E94B80A-CA5E-4B76-8E56-B671D00049A7 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 2E94B80A-CA5E-4B76-8E56-B671D00049A7 + IDESourceControlWCCName + HockeySDK-iOS + + + + From 506c5c259842f0f6890811bbb431980b8429da24 Mon Sep 17 00:00:00 2001 From: Thomas Dohmke Date: Mon, 10 Feb 2014 20:37:29 +0100 Subject: [PATCH 05/13] Remove .xccheckout file from repo. --- .gitignore | 1 + .../xcshareddata/HockeySDK.xccheckout | 41 ------------------- 2 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout diff --git a/.gitignore b/.gitignore index f962d0da..11a9929c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ build !default.xcworkspace !project.xcworkspace xcuserdata +xccheckout profile *.moved-aside diff --git a/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout b/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout deleted file mode 100644 index 3ecae9ae..00000000 --- a/Support/HockeySDK.xcodeproj/project.xcworkspace/xcshareddata/HockeySDK.xccheckout +++ /dev/null @@ -1,41 +0,0 @@ - - - - - IDESourceControlProjectFavoriteDictionaryKey - - IDESourceControlProjectIdentifier - B576A34E-A0B3-4F0B-A0A2-4AE78B08404E - IDESourceControlProjectName - HockeySDK - IDESourceControlProjectOriginsDictionary - - 2E94B80A-CA5E-4B76-8E56-B671D00049A7 - ssh://github.com/bitstadium/HockeySDK-iOS.git - - IDESourceControlProjectPath - Support/HockeySDK.xcodeproj/project.xcworkspace - IDESourceControlProjectRelativeInstallPathDictionary - - 2E94B80A-CA5E-4B76-8E56-B671D00049A7 - ../../.. - - IDESourceControlProjectURL - ssh://github.com/bitstadium/HockeySDK-iOS.git - IDESourceControlProjectVersion - 110 - IDESourceControlProjectWCCIdentifier - 2E94B80A-CA5E-4B76-8E56-B671D00049A7 - IDESourceControlProjectWCConfigurations - - - IDESourceControlRepositoryExtensionIdentifierKey - public.vcs.git - IDESourceControlWCCIdentifierKey - 2E94B80A-CA5E-4B76-8E56-B671D00049A7 - IDESourceControlWCCName - HockeySDK-iOS - - - - From 88bf3fae623b7ca8da50990f02b12078d8fff584 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 11 Feb 2014 15:38:47 +0100 Subject: [PATCH 06/13] Move creation of settings directory into helper function --- Classes/BITCrashManager.m | 12 +----------- Classes/BITFeedbackManager.m | 14 +------------- Classes/BITHockeyHelper.h | 2 ++ Classes/BITHockeyHelper.m | 22 ++++++++++++++++++++++ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index fa1c8bca..18b713e1 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -125,17 +125,7 @@ - (id)init { [[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]; diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 8d77606c..ddd58bf3 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -53,7 +53,6 @@ @implementation BITFeedbackManager { NSFileManager *_fileManager; - NSString *_feedbackDir; NSString *_settingsFile; id _appDidBecomeActiveObserver; @@ -89,18 +88,7 @@ - (id)init { _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; diff --git a/Classes/BITHockeyHelper.h b/Classes/BITHockeyHelper.h index 22d6dbda..a508dc64 100644 --- a/Classes/BITHockeyHelper.h +++ b/Classes/BITHockeyHelper.h @@ -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); diff --git a/Classes/BITHockeyHelper.m b/Classes/BITHockeyHelper.m index 843283a1..dc01d98e 100644 --- a/Classes/BITHockeyHelper.m +++ b/Classes/BITHockeyHelper.m @@ -76,6 +76,28 @@ - (NSString *)base64Encoding; #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!#$%\\&'*+/=?\\^_`{|}" From 6d6aa54beaee4bb73ee1610b7dc72c202c897e57 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 11 Feb 2014 16:22:31 +0100 Subject: [PATCH 07/13] Add new setter for global userID, userName, and userEmail properties The values are used by the `BITCrashManager` to attach to a crash report and`BITFeedbackManager 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`. These properties are optional and alternatives to the delegates. If you want to define specific values for each component, use the delegate instead which do overwrite the values set by these properties. Also fixed a typo in the delegates documentation. --- Classes/BITCrashManager.m | 9 ++-- Classes/BITFeedbackManager.m | 75 +++++++++++++++++------------- Classes/BITHockeyHelper.m | 8 +++- Classes/BITHockeyManager.h | 63 ++++++++++++++++++++++++- Classes/BITHockeyManager.m | 51 ++++++++++++++++++++ Classes/BITHockeyManagerDelegate.h | 6 +-- Classes/HockeySDKPrivate.h | 4 ++ 7 files changed, 175 insertions(+), 41 deletions(-) diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 18b713e1..17e079f6 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -298,7 +298,8 @@ - (void) unregisterObservers { * @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. @@ -327,7 +328,8 @@ - (NSString *)userIDForCrashReport { * @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] ?: @""; @@ -348,7 +350,8 @@ - (NSString *)userNameForCrashReport { * @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. diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index ddd58bf3..90318326 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -262,63 +262,72 @@ - (void)updateMessagesListIfRequired { } } -- (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 - userIDForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; - if (userID) { - availableViaDelegate = YES; - self.userID = userID; - } + 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; - } } - + + 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 - userEmailForHockeyManager:[BITHockeyManager sharedHockeyManager] - componentManager:self]; - if (userEmail) { - availableViaDelegate = YES; - self.userEmail = userEmail; - self.requireUserEmail = BITFeedbackUserDataElementDontShow; - } + 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 && @@ -330,9 +339,9 @@ - (void)updateAppDefinedUserData { #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; diff --git a/Classes/BITHockeyHelper.m b/Classes/BITHockeyHelper.m index dc01d98e..11b29ad8 100644 --- a/Classes/BITHockeyHelper.m +++ b/Classes/BITHockeyHelper.m @@ -113,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; } diff --git a/Classes/BITHockeyManager.h b/Classes/BITHockeyManager.h index 0102cc9d..93f1a6e0 100644 --- a/Classes/BITHockeyManager.h +++ b/Classes/BITHockeyManager.h @@ -387,7 +387,68 @@ ///----------------------------------------------------------------------------- -/// @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. + + @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. + + @see userID + @see userName + @see `[BITHockeyManagerDelegate userEmailForHockeyManager:componentManager:]` + */ +@property (nonatomic, retain) NSString *userEmail; + + +///----------------------------------------------------------------------------- +/// @name SDK meta data ///----------------------------------------------------------------------------- /** diff --git a/Classes/BITHockeyManager.m b/Classes/BITHockeyManager.m index 19c5c630..f72a907d 100644 --- a/Classes/BITHockeyManager.m +++ b/Classes/BITHockeyManager.m @@ -34,6 +34,7 @@ #import "BITHockeyHelper.h" #import "BITHockeyAppClient.h" +#import "BITKeychainUtils.h" #if HOCKEYSDK_FEATURE_CRASH_REPORTER @@ -350,6 +351,56 @@ - (void)setDelegate:(id)delegate { } } +- (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; diff --git a/Classes/BITHockeyManagerDelegate.h b/Classes/BITHockeyManagerDelegate.h index 76ade83a..dfe8728f 100644 --- a/Classes/BITHockeyManagerDelegate.h +++ b/Classes/BITHockeyManagerDelegate.h @@ -121,7 +121,7 @@ /** 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 @@ -152,7 +152,7 @@ /** 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 @@ -182,7 +182,7 @@ /** 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 diff --git a/Classes/HockeySDKPrivate.h b/Classes/HockeySDKPrivate.h index aad75baa..49d48646 100644 --- a/Classes/HockeySDKPrivate.h +++ b/Classes/HockeySDKPrivate.h @@ -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" From 53ae4c2dff5cba40cb7acd8548ead251821ef567 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 11 Feb 2014 16:37:06 +0100 Subject: [PATCH 08/13] Disable on device symbolication by default and add a property to enable it --- Classes/BITCrashManager.h | 14 ++++++++++++++ Classes/BITCrashManager.m | 8 +++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 64f695d6..bf651da5 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -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 * diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 17e079f6..fca13fc4 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -660,8 +660,14 @@ - (void)startManager { 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 From 5b782b90bee048ec99ce7eab46a0a40c1874af7c Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 11 Feb 2014 16:40:21 +0100 Subject: [PATCH 09/13] Documentation fixes --- Classes/BITFeedbackManager.h | 2 +- Classes/BITHockeyManager.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Classes/BITFeedbackManager.h b/Classes/BITFeedbackManager.h index 01f58572..1f1543df 100644 --- a/Classes/BITFeedbackManager.h +++ b/Classes/BITFeedbackManager.h @@ -89,7 +89,7 @@ typedef NS_ENUM(NSInteger, BITFeedbackUserDataElement) { 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 diff --git a/Classes/BITHockeyManager.h b/Classes/BITHockeyManager.h index 93f1a6e0..599179cb 100644 --- a/Classes/BITHockeyManager.h +++ b/Classes/BITHockeyManager.h @@ -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: From 0f44f6f4ada324df46400a90f340961fcc88c533 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Tue, 11 Feb 2014 18:20:53 +0100 Subject: [PATCH 10/13] More documentation improvements --- Classes/BITHockeyManager.h | 8 +++- Classes/BITHockeyManagerDelegate.h | 60 +++++++++++++++++++----------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/Classes/BITHockeyManager.h b/Classes/BITHockeyManager.h index 599179cb..585a8f2a 100644 --- a/Classes/BITHockeyManager.h +++ b/Classes/BITHockeyManager.h @@ -417,7 +417,10 @@ 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:]` @@ -437,6 +440,9 @@ 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:]` diff --git a/Classes/BITHockeyManagerDelegate.h b/Classes/BITHockeyManagerDelegate.h index dfe8728f..c8eeaced 100644 --- a/Classes/BITHockeyManagerDelegate.h +++ b/Classes/BITHockeyManagerDelegate.h @@ -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; @@ -128,6 +129,7 @@ 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,14 +140,18 @@ } } + 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; @@ -159,6 +165,7 @@ 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,13 +176,18 @@ } } + 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; @@ -189,6 +201,7 @@ 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; From f2b7f3d575289e41546cec318610f0bef8fab2c2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 12 Feb 2014 02:11:02 +0100 Subject: [PATCH 11/13] Update `Auhenticating Users on iOS` documentation with automatic authentication hint --- docs/HowTo-Authenticating-Users-on-iOS-template.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/HowTo-Authenticating-Users-on-iOS-template.md b/docs/HowTo-Authenticating-Users-on-iOS-template.md index d3eece6d..7c66e8df 100644 --- a/docs/HowTo-Authenticating-Users-on-iOS-template.md +++ b/docs/HowTo-Authenticating-Users-on-iOS-template.md @@ -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]; From ab241aaa1f5abd894439b7825a74b41d8c579d03 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 12 Feb 2014 12:26:02 +0100 Subject: [PATCH 12/13] Show the selector name found in the current argument registers in crash reports, helpful e.g. for crashes in objc_msgSend This is a safe implementation with only hard wiring the necessary bits for where objc_msgSend stores the receiver objects based on the CPU architecture. From: http://sealiesoftware.com/blog/archive/2008/09/22/objc_explain_So_you_crashed_in_objc_msgSend.html In the mid/long term, PLCrashReporter will include this directly. --- Classes/BITCrashReportTextFormatter.m | 193 +++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/Classes/BITCrashReportTextFormatter.m b/Classes/BITCrashReportTextFormatter.m index 6094affc..1acc07c9 100644 --- a/Classes/BITCrashReportTextFormatter.m +++ b/Classes/BITCrashReportTextFormatter.m @@ -33,6 +33,18 @@ #import +#import +#import +#import +#import +#import + +#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 ` - `, 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 @@ + (NSString *)stringValueForCrashReport:(BITPLCrashReport *)report crashReporter [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 @@ + (NSString *)stringValueForCrashReport:(BITPLCrashReport *)report crashReporter 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 @@ + (NSString *)stringValueForCrashReport:(BITPLCrashReport *)report crashReporter } /* 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 @@ + (NSString *)stringValueForCrashReport:(BITPLCrashReport *)report crashReporter 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 From 4c9a268a0cf93ccc74d165dd17460e952282112d Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 12 Feb 2014 15:09:15 +0100 Subject: [PATCH 13/13] 3.5.3 release and documentation updates --- HockeySDK.podspec | 6 +++--- README.md | 14 +++++++------- Support/buildnumber.xcconfig | 4 ++-- docs/Changelog-template.md | 10 ++++++++++ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/HockeySDK.podspec b/HockeySDK.podspec index f6ce8a53..8080cb46 100644 --- a/HockeySDK.podspec +++ b/HockeySDK.podspec @@ -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' diff --git a/README.md b/README.md index a38637e8..b1d764e6 100644 --- a/README.md +++ b/README.md @@ -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/) diff --git a/Support/buildnumber.xcconfig b/Support/buildnumber.xcconfig index 506af1bb..8c2c2802 100644 --- a/Support/buildnumber.xcconfig +++ b/Support/buildnumber.xcconfig @@ -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 diff --git a/docs/Changelog-template.md b/docs/Changelog-template.md index e9c6431e..8c8c76b4 100644 --- a/docs/Changelog-template.md +++ b/docs/Changelog-template.md @@ -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 +

+ ## Version 3.5.2 - [UPDATE] Make sure a log message appears in the console if the SDK is not setup on the main thread