Use Keychain for some data

- Added SFHFKeyChainUtils with BIT prefix
- Save username, email or userid (if provided by the app or the user) in the keychain instead of user defaults or property files
This commit is contained in:
Andreas Linde 2013-05-09 16:45:58 +02:00
parent 89fa399787
commit 93c2f05fac
12 changed files with 450 additions and 18 deletions

View File

@ -208,6 +208,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
[_fileManager removeItemAtPath:[_crashFiles objectAtIndex:i] error:&error];
[_fileManager removeItemAtPath:[[_crashFiles objectAtIndex:i] stringByAppendingString:@".meta"] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", [_crashFiles objectAtIndex:i], kBITCrashMetaUserID]];
}
[_crashFiles removeAllObjects];
[_approvedCrashReports removeAllObjects];
@ -321,9 +324,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
NSString *applicationLog = @"";
NSString *errorString = nil;
[metaDict setObject:[self userNameForCrashReport] forKey:kBITCrashMetaUserName];
[metaDict setObject:[self userEmailForCrashReport] forKey:kBITCrashMetaUserEmail];
[metaDict setObject:[self userIDForCrashReport] forKey:kBITCrashMetaUserID];
[self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]];
[self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]];
[self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]];
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) {
applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @"";
@ -548,6 +551,10 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
// we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]];
continue;
}
@ -583,9 +590,9 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
format:&format
errorDescription:&errorString];
username = [metaDict objectForKey:kBITCrashMetaUserName] ?: @"";
useremail = [metaDict objectForKey:kBITCrashMetaUserEmail] ?: @"";
userid = [metaDict objectForKey:kBITCrashMetaUserID] ?: @"";
username = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]] ?: @"";
useremail = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]] ?: @"";
userid = [self stringValueFromKeychainForKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]] ?: @"";
applicationLog = [metaDict objectForKey:kBITCrashMetaApplicationLog] ?: @"";
} else {
BITHockeyLog(@"ERROR: Reading crash meta data. %@", error);
@ -618,6 +625,10 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
// we cannot do anything with this report, so delete it
[_fileManager removeItemAtPath:filename error:&error];
[_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]];
[self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]];
}
}

View File

@ -295,25 +295,37 @@
}
if (!userIDViaDelegate) {
if ([unarchiver containsValueForKey:kBITFeedbackUserID])
if ([unarchiver containsValueForKey:kBITFeedbackUserID]) {
self.userID = [unarchiver decodeObjectForKey:kBITFeedbackUserID];
[self addStringValueToKeychain:self.userID forKey:kBITFeedbackUserID];
}
self.userID = [self stringValueFromKeychainForKey:kBITFeedbackUserID];
}
if (!userNameViaDelegate) {
if ([unarchiver containsValueForKey:kBITFeedbackName])
if ([unarchiver containsValueForKey:kBITFeedbackName]) {
self.userName = [unarchiver decodeObjectForKey:kBITFeedbackName];
[self addStringValueToKeychain:self.userName forKey:kBITFeedbackName];
}
self.userName = [self stringValueFromKeychainForKey:kBITFeedbackName];
}
if (!userEmailViaDelegate) {
if ([unarchiver containsValueForKey:kBITFeedbackEmail])
if ([unarchiver containsValueForKey:kBITFeedbackEmail]) {
self.userEmail = [unarchiver decodeObjectForKey:kBITFeedbackEmail];
[self addStringValueToKeychain:self.userEmail forKey:kBITFeedbackEmail];
}
self.userEmail = [self stringValueFromKeychainForKey:kBITFeedbackEmail];
}
if ([unarchiver containsValueForKey:kBITFeedbackUserDataAsked])
_didAskUserData = YES;
if ([unarchiver containsValueForKey:kBITFeedbackToken])
if ([unarchiver containsValueForKey:kBITFeedbackToken]) {
self.token = [unarchiver decodeObjectForKey:kBITFeedbackToken];
[self addStringValueToKeychain:self.token forKey:kBITFeedbackToken];
}
self.token = [self stringValueFromKeychainForKey:kBITFeedbackToken];
if ([unarchiver containsValueForKey:kBITFeedbackAppID]) {
NSString *appID = [unarchiver decodeObjectForKey:kBITFeedbackAppID];
@ -359,19 +371,19 @@
[archiver encodeObject:[NSNumber numberWithBool:YES] forKey:kBITFeedbackUserDataAsked];
if (self.token)
[archiver encodeObject:self.token forKey:kBITFeedbackToken];
[self addStringValueToKeychain:self.token forKey:kBITFeedbackToken];
if (self.appIdentifier)
[archiver encodeObject:self.appIdentifier forKey:kBITFeedbackAppID];
if (self.userID)
[archiver encodeObject:self.userID forKey:kBITFeedbackUserID];
[self addStringValueToKeychain:self.userID forKey:kBITFeedbackUserID];
if (self.userName)
[archiver encodeObject:self.userName forKey:kBITFeedbackName];
[self addStringValueToKeychain:self.userName forKey:kBITFeedbackName];
if (self.userEmail)
[archiver encodeObject:self.userEmail forKey:kBITFeedbackEmail];
[self addStringValueToKeychain:self.userEmail forKey:kBITFeedbackEmail];
if (self.lastCheck)
[archiver encodeObject:self.lastCheck forKey:kBITFeedbackDateOfLastCheck];

View File

@ -16,6 +16,7 @@
#import "BITHockeyBaseViewController.h"
#import "BITHockeyManagerPrivate.h"
#import "BITKeychainUtils.h"
#import <sys/sysctl.h>
#if !TARGET_IPHONE_SIMULATOR
@ -192,6 +193,33 @@
}
}
- (BOOL)addStringValueToKeychain:(NSString *)stringValue forKey:(NSString *)key {
if (!key || !stringValue)
return NO;
NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]];
NSError *error = nil;
return [BITKeychainUtils storeUsername:key andPassword:stringValue forServiceName:serviceName updateExisting:YES error:&error];
}
- (NSString *)stringValueFromKeychainForKey:(NSString *)key {
if (!key)
return nil;
NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]];
NSError *error = nil;
return [BITKeychainUtils getPasswordForUsername:key andServiceName:serviceName error:&error];
}
- (BOOL)removeKeyFromKeychain:(NSString *)key {
NSString *serviceName = [NSString stringWithFormat:@"%@.HockeySDK", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]];
NSError *error = nil;
return [BITKeychainUtils deleteItemForUsername:key andServiceName:serviceName error:&error];
}
#pragma mark - Manager Control

View File

@ -34,4 +34,8 @@
- (NSDate *)parseRFC3339Date:(NSString *)dateString;
- (BOOL)addStringValueToKeychain:(NSString *)stringValue forKey:(NSString *)key;
- (NSString *)stringValueFromKeychainForKey:(NSString *)key;
- (BOOL)removeKeyFromKeychain:(NSString *)key;
@end

View File

@ -0,0 +1,42 @@
//
// SFHFKeychainUtils.h
//
// Created by Buzz Andersen on 10/20/08.
// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import <UIKit/UIKit.h>
@interface BITKeychainUtils : NSObject {
}
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (BOOL) purgeItemsForServiceName:(NSString *) serviceName error: (NSError **) error;
@end

297
Classes/BITKeychainUtils.m Normal file
View File

@ -0,0 +1,297 @@
//
// SFHFKeychainUtils.m
//
// Created by Buzz Andersen on 10/20/08.
// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import "BITKeychainUtils.h"
#import <Security/Security.h>
static NSString *BITKeychainUtilsErrorDomain = @"BITKeychainUtilsErrorDomain";
@implementation BITKeychainUtils
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
if (error != nil) {
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return nil;
}
if (error != nil) {
*error = nil;
}
// Set up a query dictionary with the base query attributes: item type (generic), username, and service
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, nil];
NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys];
// First do a query for attributes, in case we already have a Keychain item with no password data set.
// One likely way such an incorrect item could have come about is due to the previous (incorrect)
// version of this code (which set the password as a generic attribute instead of password data).
NSMutableDictionary *attributeQuery = [query mutableCopy];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnAttributes];
CFTypeRef attrResult = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) attributeQuery, &attrResult);
//NSDictionary *attributeResult = (__bridge_transfer NSDictionary *)attrResult;
if (status != noErr) {
// No existing item found--simply return nil for the password
if (error != nil && status != errSecItemNotFound) {
//Only return an error if a real exception happened--not simply for "not found."
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
// We have an existing item, now query for the password data associated with it.
NSMutableDictionary *passwordQuery = [query mutableCopy];
[passwordQuery setObject: (id) kCFBooleanTrue forKey: (__bridge_transfer id) kSecReturnData];
CFTypeRef resData = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef) passwordQuery, (CFTypeRef *) &resData);
NSData *resultData = (__bridge_transfer NSData *)resData;
if (status != noErr) {
if (status == errSecItemNotFound) {
// We found attributes for the item previously, but no password now, so return a special error.
// Users of this API will probably want to detect this error and prompt the user to
// re-enter their credentials. When you attempt to store the re-entered credentials
// using storeUsername:andPassword:forServiceName:updateExisting:error
// the old, incorrect entry will be deleted and a new one with a properly encrypted
// password will be added.
if (error != nil) {
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
else {
// Something else went wrong. Simply return the normal Keychain API error code.
if (error != nil) {
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
return nil;
}
NSString *password = nil;
if (resultData) {
password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
}
else {
// There is an existing item, but we weren't able to get password data for it for some reason,
// Possibly as a result of an item being incorrectly entered by the previous code.
// Set the -1999 error so the code above us can prompt the user again.
if (error != nil) {
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
return password;
}
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error
{
if (!username || !password || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
// See if we already have a password entered for these credentials.
NSError *getError = nil;
NSString *existingPassword = [BITKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
if ([getError code] == -1999)
{
// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
// Delete the existing item before moving on entering a correct one.
getError = nil;
[self deleteItemForUsername: username andServiceName: serviceName error: &getError];
if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
}
else if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
if (error != nil)
{
*error = nil;
}
OSStatus status = noErr;
if (existingPassword)
{
// We have an existing, properly entered item with a password.
// Update the existing item.
if (![existingPassword isEqualToString:password] && updateExisting)
{
//Only update if we're allowed to update existing. If not, simply do nothing.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemUpdate((__bridge_retained CFDictionaryRef) query, (__bridge_retained CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (__bridge_transfer NSString *) kSecValueData]);
}
}
else
{
// No existing entry (or an existing, improperly entered, and therefore now
// deleted, entry). Create a new entry.
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
kSecValueData,
nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
[password dataUsingEncoding: NSUTF8StringEncoding],
nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
status = SecItemAdd((__bridge_retained CFDictionaryRef) query, NULL);
}
if (error != nil && status != noErr)
{
// Something went wrong with adding the new item. Return the Keychain error code.
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error
{
if (!username || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
if (error != nil)
{
*error = nil;
}
NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil];
NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);
if (error != nil && status != noErr)
{
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
+ (BOOL) purgeItemsForServiceName:(NSString *) serviceName error: (NSError **) error {
if (!serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
if (error != nil)
{
*error = nil;
}
NSMutableDictionary *searchData = [NSMutableDictionary new];
[searchData setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[searchData setObject:serviceName forKey:(__bridge id)kSecAttrService];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)searchData);
if (error != nil && status != noErr)
{
*error = [NSError errorWithDomain: BITKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
@end

View File

@ -225,7 +225,7 @@
- (BITUpdateAuthorizationState)authorizationState {
NSString *version = [[NSUserDefaults standardUserDefaults] objectForKey:kBITUpdateAuthorizedVersion];
NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:kBITUpdateAuthorizedToken];
NSString *token = [self stringValueFromKeychainForKey:kBITUpdateAuthorizedToken];
if (version != nil && token != nil) {
if ([version compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) {
@ -602,7 +602,7 @@
// store the new data
[[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion];
[[NSUserDefaults standardUserDefaults] setObject:token forKey:kBITUpdateAuthorizedToken];
[self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken];
[[NSUserDefaults standardUserDefaults] synchronize];
self.requireAuthorization = NO;
@ -618,7 +618,7 @@
// store the new data
[[NSUserDefaults standardUserDefaults] setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:kBITUpdateAuthorizedVersion];
[[NSUserDefaults standardUserDefaults] setObject:token forKey:kBITUpdateAuthorizedToken];
[self addStringValueToKeychain:token forKey:kBITUpdateAuthorizedToken];
[[NSUserDefaults standardUserDefaults] synchronize];
[self showBlockingScreen:BITHockeyLocalizedString(@"UpdateAuthorizationDenied") image:@"authorize_denied.png"];

27
LICENSE
View File

@ -95,3 +95,30 @@ TTTAttributedLabel is licensed as follows:
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
SFHFKeychainUtils is licensed as follows:
Created by Buzz Andersen on 10/20/08.
Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,3 +1,3 @@
OTHER_LDFLAGS=$(inherited) -framework CoreText -framework CoreGraphics -framework Foundation -framework QuartzCore -framework SystemConfiguration -framework UIKit
OTHER_LDFLAGS=$(inherited) -framework CoreText -framework CoreGraphics -framework Foundation -framework QuartzCore -framework SystemConfiguration -framework UIKit -framework Security
HOCKEYSDK_DOCSET_NAME=HockeySDK-iOS
GCC_PREPROCESSOR_DEFINITIONS=$(inherited) CONFIGURATION_$(CONFIGURATION)

View File

@ -33,6 +33,8 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E0FEE26173BDB260061331F /* BITKeychainUtils.h */; };
1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E0FEE27173BDB260061331F /* BITKeychainUtils.m */; };
1E1127C416580C87007067A2 /* buttonRoundedDelete.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */; };
1E1127C516580C87007067A2 /* buttonRoundedDelete@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */; };
1E1127C616580C87007067A2 /* buttonRoundedDeleteHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */; };
@ -130,6 +132,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1E0FEE22173BD9BB0061331F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
1E0FEE26173BDB260061331F /* BITKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITKeychainUtils.h; sourceTree = "<group>"; };
1E0FEE27173BDB260061331F /* BITKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITKeychainUtils.m; sourceTree = "<group>"; };
1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDelete.png; sourceTree = "<group>"; };
1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "buttonRoundedDelete@2x.png"; sourceTree = "<group>"; };
1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDeleteHighlighted.png; sourceTree = "<group>"; };
@ -295,6 +300,8 @@
1E49A4A4161222B900463151 /* BITHockeyBaseViewController.m */,
1E49A4A5161222B900463151 /* BITHockeyHelper.h */,
1E49A4A6161222B900463151 /* BITHockeyHelper.m */,
1E0FEE26173BDB260061331F /* BITKeychainUtils.h */,
1E0FEE27173BDB260061331F /* BITKeychainUtils.m */,
1EACC979162F041E007578C5 /* BITAttributedLabel.h */,
1EACC97A162F041E007578C5 /* BITAttributedLabel.m */,
1E49A4A7161222B900463151 /* BITAppStoreHeader.h */,
@ -463,6 +470,7 @@
1E754E5F1621FBB70070AB92 /* BITCrashManagerPrivate.h in Headers */,
1E754E601621FBB70070AB92 /* BITCrashReportTextFormatter.h in Headers */,
1EACC97B162F041E007578C5 /* BITAttributedLabel.h in Headers */,
1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -636,6 +644,7 @@
1E754E611621FBB70070AB92 /* BITCrashReportTextFormatter.m in Sources */,
1EF95CA7162CB037000AE3AD /* BITFeedbackActivity.m in Sources */,
1EACC97C162F041E007578C5 /* BITAttributedLabel.m in Sources */,
1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -102,6 +102,7 @@ If you need support for iOS 3.x, please check out [HockeyKit](http://support.hoc
- `CoreGraphics`
- `Foundation`
- `QuartzCore`
- `Security`
- `SystemConfiguration`
- `UIKit`

View File

@ -72,6 +72,7 @@ If you need support for iOS 3.x, please check out [HockeyKit](http://support.hoc
- `CoreGraphics`
- `Foundation`
- `QuartzCore`
- `Security`
- `SystemConfiguration`
- `UIKit`