diff options
Diffstat (limited to 'QSThingsPlugin/Helpers')
| -rw-r--r-- | QSThingsPlugin/Helpers/KeychainHelper.m | 117 | ||||
| -rw-r--r-- | QSThingsPlugin/Helpers/SQLHelper.m | 1 | ||||
| -rw-r--r-- | QSThingsPlugin/Helpers/TextParsingHelper.h | 22 | ||||
| -rw-r--r-- | QSThingsPlugin/Helpers/TextParsingHelper.m | 111 |
4 files changed, 191 insertions, 60 deletions
diff --git a/QSThingsPlugin/Helpers/KeychainHelper.m b/QSThingsPlugin/Helpers/KeychainHelper.m index fcf8291..50a8eac 100644 --- a/QSThingsPlugin/Helpers/KeychainHelper.m +++ b/QSThingsPlugin/Helpers/KeychainHelper.m @@ -5,70 +5,69 @@ NS_ASSUME_NONNULL_BEGIN @implementation KeychainHelper + (NSString *)authenticationKey { - const char *service = [kKeychainService UTF8String]; - const char *account = [kAuthenticationKeyKeychainKey UTF8String]; - - UInt32 authenticationKeyLength = 0; - void *authenticationKeyData = NULL; - - OSStatus status = SecKeychainFindGenericPassword( - NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account, - &authenticationKeyLength, &authenticationKeyData, NULL); - - if (status == errSecSuccess && authenticationKeyData != NULL) { - NSString *authenticationKey = [[NSString alloc] initWithBytes:authenticationKeyData - length:authenticationKeyLength - encoding:NSUTF8StringEncoding]; - SecKeychainItemFreeContent(NULL, authenticationKeyData); - return authenticationKey; - } - - return nil; + CFDictionaryRef query = (__bridge CFDictionaryRef)@{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: kKeychainService, + (__bridge NSString *)kSecAttrAccount: kAuthenticationKeyKeychainKey, + (__bridge NSString *)kSecReturnData: @YES, + (__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne + }; + + CFTypeRef result = NULL; + OSStatus status = SecItemCopyMatching(query, &result); + + if (status == errSecSuccess && result != NULL) { + NSData *keyData = (__bridge NSData *)result; + NSString *authenticationKey = [[[NSString alloc] initWithData:keyData + encoding:NSUTF8StringEncoding] autorelease]; + CFRelease(result); + return authenticationKey; + } + + return nil; } + (OSStatus)setAuthenticationKey:(NSString *)authenticationKey { - const char *service = [kKeychainService UTF8String]; - const char *account = [kAuthenticationKeyKeychainKey UTF8String]; - const char *authenticationKeyCString = [authenticationKey UTF8String]; - - // First try to find existing item - SecKeychainItemRef item = NULL; - OSStatus findStatus = SecKeychainFindGenericPassword( - NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account, - NULL, NULL, &item); - - OSStatus status; - if (findStatus == errSecSuccess) { - // Update existing item - status = SecKeychainItemModifyAttributesAndData( - item, NULL, (UInt32)strlen(authenticationKeyCString), authenticationKeyCString); - CFRelease(item); - } else { - // Create new item - status = SecKeychainAddGenericPassword( - NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), - account, (UInt32)strlen(authenticationKeyCString), authenticationKeyCString, NULL); - } - - return status; + NSData *passwordData = [authenticationKey dataUsingEncoding:NSUTF8StringEncoding]; + + // First try to update existing item + CFDictionaryRef query = (__bridge CFDictionaryRef)@{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: kKeychainService, + (__bridge NSString *)kSecAttrAccount: kAuthenticationKeyKeychainKey + }; + + CFDictionaryRef attributesToUpdate = (__bridge CFDictionaryRef)@{ + (__bridge NSString *)kSecValueData: passwordData + }; + + OSStatus status = SecItemUpdate(query, attributesToUpdate); + + if (status == errSecItemNotFound) { + // Item doesn't exist, create new one + CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: kKeychainService, + (__bridge NSString *)kSecAttrAccount: kAuthenticationKeyKeychainKey, + (__bridge NSString *)kSecValueData: passwordData, + (__bridge NSString *)kSecAttrAccessible: (__bridge NSString *)kSecAttrAccessibleWhenUnlockedThisDeviceOnly + }; + + status = SecItemAdd(attributes, NULL); + } + + return status; } -+ (OSStatus) deleteAuthenticationKey { - const char *service = [kKeychainService UTF8String]; - const char *account = [kAuthenticationKeyKeychainKey UTF8String]; - - SecKeychainItemRef item = NULL; - OSStatus findStatus = SecKeychainFindGenericPassword( - NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account, - NULL, NULL, &item); - - if (findStatus == errSecSuccess) { - OSStatus deleteStatus = SecKeychainItemDelete(item); - CFRelease(item); - return deleteStatus; - } - - return findStatus; ++ (OSStatus)deleteAuthenticationKey { + CFDictionaryRef query = (__bridge CFDictionaryRef)@{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: kKeychainService, + (__bridge NSString *)kSecAttrAccount: kAuthenticationKeyKeychainKey + }; + + OSStatus status = SecItemDelete(query); + return status; } @end diff --git a/QSThingsPlugin/Helpers/SQLHelper.m b/QSThingsPlugin/Helpers/SQLHelper.m index 6d94bb5..c44ea22 100644 --- a/QSThingsPlugin/Helpers/SQLHelper.m +++ b/QSThingsPlugin/Helpers/SQLHelper.m @@ -6,7 +6,6 @@ + (NSArray *) executeSql:(NSString *)query onFile:(NSString *)path { NSMutableArray *objects = [NSMutableArray arrayWithCapacity:0]; - NSFileManager *manager = [NSFileManager defaultManager]; FMDatabase *database = [FMDatabase databaseWithPath:path]; if (![database openWithFlags:0x00000001]) { // 1 = SQLITE_OPEN_READONLY diff --git a/QSThingsPlugin/Helpers/TextParsingHelper.h b/QSThingsPlugin/Helpers/TextParsingHelper.h new file mode 100644 index 0000000..2c7029c --- /dev/null +++ b/QSThingsPlugin/Helpers/TextParsingHelper.h @@ -0,0 +1,22 @@ +#import "Constants.h" +#import <Foundation/Foundation.h> +#import <Security/Security.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface TextParsingHelper : NSObject + +/** + Parses a string and returns a dictionary of parameters according to the following rules: + * The first line is parsed as title + * If the first line contains >> then everything after >> is considered notes. + * If a line starts with #, the rest will be interpreted as a comma separated list of tags. + * If a line starts with @, the rest will be interpreted as the date. + * If a line starts with !, the rest will be interpreted as the deadline. + @param text the + @return A dictionary with the parameters for the `add` route of the things API + */ ++ (NSMutableDictionary *)parseText:(NSString*)text; +@end + +NS_ASSUME_NONNULL_END diff --git a/QSThingsPlugin/Helpers/TextParsingHelper.m b/QSThingsPlugin/Helpers/TextParsingHelper.m new file mode 100644 index 0000000..3fbbd3d --- /dev/null +++ b/QSThingsPlugin/Helpers/TextParsingHelper.m @@ -0,0 +1,111 @@ +#import "TextParsingHelper.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation TextParsingHelper + ++ (NSMutableDictionary *)parseText:(NSString *)text { + if (!text || [text length] == 0) { + return nil; + } + + NSArray *lines = [text componentsSeparatedByString:@"\n"]; + if ([lines count] == 0) { + return nil; + } + + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + NSMutableString *notes = nil; + BOOL processingNotes = NO; + + for (NSInteger i = 0; i < [lines count]; i++) { + NSString *line = [lines objectAtIndex:i]; + + // The first line is always interpreted as the title. No matter what. Notes start with >> + if (i == 0) { + NSRange rangeOfSeparator = [line rangeOfString:@">>"]; + if (rangeOfSeparator.location != NSNotFound) { + NSString *title = [line substringToIndex:rangeOfSeparator.location]; + NSString *notesPart = [line substringFromIndex:rangeOfSeparator.location + 2]; + + if ([title length] > 0) { + [result setObject:title forKey:@"title"]; + } + + if ([notesPart length] > 0) { + notes = [[NSMutableString alloc] initWithString:notesPart]; + processingNotes = YES; + } + } else { + if ([line length] > 0) { + [result setObject:line forKey:@"title"]; + } + } + } else { + if ([line length] > 0) { + unichar firstChar = [line characterAtIndex:0]; + + // Once we encounter a special line, we stop parsing notes. + if (firstChar == '#' || firstChar == '@' || firstChar == '!') { + processingNotes = NO; + + NSString *content = nil; + if ([line length] > 1) { + content = [line substringFromIndex:1]; + } + + if (content && [content length] > 0) { + switch (firstChar) { + case '#': + if (![result objectForKey:@"tags"]) { + [result setObject:content forKey:@"tags"]; + } + break; + case '@': + if (![result objectForKey:@"when"]) { + [result setObject:content forKey:@"when"]; + } + break; + case '!': + if (![result objectForKey:@"deadline"]) { + [result setObject:content forKey:@"deadline"]; + } + break; + } + } + } else if (processingNotes) { + if (notes) { + [notes appendString:@"\n"]; + [notes appendString:line]; + } else { + notes = [[NSMutableString alloc] initWithString:line]; + } + } + } else if (processingNotes) { + // This is an empty notes line + if (notes) { + [notes appendString:@"\n"]; + } + } + } + } + + if (notes && [notes length] > 0) { + [result setObject:[NSString stringWithString:notes] forKey:@"notes"]; + } + + if (notes) { + [notes release]; + } + + if ([result count] == 0) { + [result release]; + return nil; + } + + return [result autorelease]; +} + +@end + +NS_ASSUME_NONNULL_END |