Working OS X prototype application.
[ADDED]     OS X: Master password input and changing.
[ADDED]     OS X: Autocompletion by searching Core Data for previously
            used sites.
[ADDED]     OS X: Working password generation for sites.
[FIXED]     Bad ciphering on little endian machines: Convert bytes to
            network endian before using them as numbers.
[FIXED]     Opening of Core Data store when iCloud is unavailable.
			
			
This commit is contained in:
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1870,10 +1870,10 @@
 | 
				
			|||||||
			children = (
 | 
								children = (
 | 
				
			||||||
				DAB8D9B11503757D00CED3BC /* Supporting Files */,
 | 
									DAB8D9B11503757D00CED3BC /* Supporting Files */,
 | 
				
			||||||
				DAB8D44215036BCF00CED3BC /* MainStoryboard_iPhone.storyboard */,
 | 
									DAB8D44215036BCF00CED3BC /* MainStoryboard_iPhone.storyboard */,
 | 
				
			||||||
				DAB8D44615036BCF00CED3BC /* MPAppDelegate.h */,
 | 
					 | 
				
			||||||
				DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */,
 | 
					 | 
				
			||||||
				DAB8D44815036BCF00CED3BC /* MPiOSConfig.h */,
 | 
									DAB8D44815036BCF00CED3BC /* MPiOSConfig.h */,
 | 
				
			||||||
				DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */,
 | 
									DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */,
 | 
				
			||||||
 | 
									DAB8D44615036BCF00CED3BC /* MPAppDelegate.h */,
 | 
				
			||||||
 | 
									DAB8D44715036BCF00CED3BC /* MPAppDelegate.m */,
 | 
				
			||||||
				DAB8D44A15036BCF00CED3BC /* MPGuideViewController.h */,
 | 
									DAB8D44A15036BCF00CED3BC /* MPGuideViewController.h */,
 | 
				
			||||||
				DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */,
 | 
									DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */,
 | 
				
			||||||
				DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */,
 | 
									DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ static NSDictionary *keyQuery() {
 | 
				
			|||||||
    static NSDictionary *MPKeyQuery = nil;
 | 
					    static NSDictionary *MPKeyQuery = nil;
 | 
				
			||||||
    if (!MPKeyQuery)
 | 
					    if (!MPKeyQuery)
 | 
				
			||||||
        MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
 | 
					        MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
 | 
				
			||||||
                                             attributes:[NSDictionary dictionaryWithObject:@"Master Password Key"
 | 
					                                             attributes:[NSDictionary dictionaryWithObject:@"Stored Master Password"
 | 
				
			||||||
                                                                                    forKey:(__bridge id)kSecAttrService]
 | 
					                                                                                    forKey:(__bridge id)kSecAttrService]
 | 
				
			||||||
                                                matches:nil];
 | 
					                                                matches:nil];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -28,7 +28,7 @@ static NSDictionary *keyHashQuery() {
 | 
				
			|||||||
    static NSDictionary *MPKeyHashQuery = nil;
 | 
					    static NSDictionary *MPKeyHashQuery = nil;
 | 
				
			||||||
    if (!MPKeyHashQuery)
 | 
					    if (!MPKeyHashQuery)
 | 
				
			||||||
        MPKeyHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
 | 
					        MPKeyHashQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword
 | 
				
			||||||
                                                 attributes:[NSDictionary dictionaryWithObject:@"Master Password Key Hash"
 | 
					                                                 attributes:[NSDictionary dictionaryWithObject:@"Master Password Verification"
 | 
				
			||||||
                                                                                        forKey:(__bridge id)kSecAttrService]
 | 
					                                                                                        forKey:(__bridge id)kSecAttrService]
 | 
				
			||||||
                                                    matches:nil];
 | 
					                                                    matches:nil];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -70,7 +70,7 @@ static NSDictionary *keyHashQuery() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
+ (MPAppDelegate *)get {
 | 
					+ (MPAppDelegate *)get {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
					#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
				
			||||||
    return (MPAppDelegate *)[UIApplication sharedApplication].delegate;
 | 
					    return (MPAppDelegate *)[UIApplication sharedApplication].delegate;
 | 
				
			||||||
#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
 | 
					#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
 | 
				
			||||||
@@ -126,18 +126,18 @@ static NSDictionary *keyHashQuery() {
 | 
				
			|||||||
        dbg(@"Updating key hash to: %@.", self.keyHashHex);
 | 
					        dbg(@"Updating key hash to: %@.", self.keyHashHex);
 | 
				
			||||||
        [PearlKeyChain addOrUpdateItemForQuery:keyHashQuery()
 | 
					        [PearlKeyChain addOrUpdateItemForQuery:keyHashQuery()
 | 
				
			||||||
                                withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
					                                withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
				
			||||||
                                                self.keyHash,                                      (__bridge id)kSecValueData,
 | 
					                                                self.keyHash,                                       (__bridge id)kSecValueData,
 | 
				
			||||||
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
					#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
				
			||||||
                                                kSecAttrAccessibleWhenUnlocked,                          (__bridge id)kSecAttrAccessible,
 | 
					                                                kSecAttrAccessibleWhenUnlocked,                     (__bridge id)kSecAttrAccessible,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
                                                nil]];
 | 
					                                                nil]];
 | 
				
			||||||
        if ([[MPConfig get].storeKey boolValue]) {
 | 
					        if ([[MPConfig get].storeKey boolValue]) {
 | 
				
			||||||
            dbg(@"Storing key in key chain.");
 | 
					            dbg(@"Storing key in key chain.");
 | 
				
			||||||
            [PearlKeyChain addOrUpdateItemForQuery:keyQuery()
 | 
					            [PearlKeyChain addOrUpdateItemForQuery:keyQuery()
 | 
				
			||||||
                                    withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
					                                    withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
				
			||||||
                                                    key,  (__bridge id)kSecValueData,
 | 
					                                                    key,                                            (__bridge id)kSecValueData,
 | 
				
			||||||
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
					#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 | 
				
			||||||
                                                    kSecAttrAccessibleWhenUnlocked,                      (__bridge id)kSecAttrAccessible,
 | 
					                                                    kSecAttrAccessibleWhenUnlocked,                 (__bridge id)kSecAttrAccessible,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
                                                    nil]];
 | 
					                                                    nil]];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
NSData *keyForPassword(NSString *password) {
 | 
					NSData *keyForPassword(NSString *password) {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
 | 
					    NSData *key = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
 | 
				
			||||||
                             usingSalt:MP_salt N:MP_N r:MP_r p:MP_p];
 | 
					                                         usingSalt:MP_salt N:MP_N r:MP_r p:MP_p];
 | 
				
			||||||
 | 
					    trc(@"password: %@ derives to key: %@", password, key);
 | 
				
			||||||
 | 
					    return key;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
NSData *keyHashForPassword(NSString *password) {
 | 
					NSData *keyHashForPassword(NSString *password) {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -111,28 +113,33 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, ui
 | 
				
			|||||||
    // Determine the hash whose bytes will be used for calculating a password: md4(name-key)
 | 
					    // Determine the hash whose bytes will be used for calculating a password: md4(name-key)
 | 
				
			||||||
    assert(name && key);
 | 
					    assert(name && key);
 | 
				
			||||||
    uint16_t ncounter = htons(counter);
 | 
					    uint16_t ncounter = htons(counter);
 | 
				
			||||||
 | 
					    trc(@"key hash from: %@-%@-%u", name, key, ncounter);
 | 
				
			||||||
    NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas:
 | 
					    NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas:
 | 
				
			||||||
                        [name dataUsingEncoding:NSUTF8StringEncoding],
 | 
					                        [name dataUsingEncoding:NSUTF8StringEncoding],
 | 
				
			||||||
                        key,
 | 
					                        key,
 | 
				
			||||||
                        [NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
 | 
					                        [NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
 | 
				
			||||||
                        nil] hashWith:PearlDigestSHA1];
 | 
					                        nil] hashWith:PearlDigestSHA1];
 | 
				
			||||||
 | 
					    trc(@"key hash is: %@", keyHash);
 | 
				
			||||||
    const char *keyBytes = keyHash.bytes;
 | 
					    const char *keyBytes = keyHash.bytes;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Determine the cipher from the first hash byte.
 | 
					    // Determine the cipher from the first hash byte.
 | 
				
			||||||
    assert([keyHash length]);
 | 
					    assert([keyHash length]);
 | 
				
			||||||
    NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)]
 | 
					    NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)]
 | 
				
			||||||
                            valueForKey:NSStringFromMPElementType(type)];
 | 
					                            valueForKey:NSStringFromMPElementType(type)];
 | 
				
			||||||
    NSString *cipher = [typeCiphers objectAtIndex:keyBytes[0] % [typeCiphers count]];
 | 
					    NSString *cipher = [typeCiphers objectAtIndex:htons(keyBytes[0]) % [typeCiphers count]];
 | 
				
			||||||
 | 
					    trc(@"type %d, ciphers: %@, selected: %@", type, typeCiphers, cipher);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Encode the content, character by character, using subsequent hash bytes and the cipher.
 | 
					    // Encode the content, character by character, using subsequent hash bytes and the cipher.
 | 
				
			||||||
    assert([keyHash length] >= [cipher length] + 1);
 | 
					    assert([keyHash length] >= [cipher length] + 1);
 | 
				
			||||||
    NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
 | 
					    NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
 | 
				
			||||||
    for (NSUInteger c = 0; c < [cipher length]; ++c) {
 | 
					    for (NSUInteger c = 0; c < [cipher length]; ++c) {
 | 
				
			||||||
        const char keyByte = keyBytes[c + 1];
 | 
					        uint16_t keyByte = htons(keyBytes[c + 1]);
 | 
				
			||||||
        NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)];
 | 
					        NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)];
 | 
				
			||||||
        NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
 | 
					        NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
 | 
				
			||||||
 | 
					        NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)];
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [content appendString:[cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)]];
 | 
					        trc(@"class %@ has characters: %@, selected: %@", cipherClass, cipherClassCharacters, character);
 | 
				
			||||||
 | 
					        [content appendString:character];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return content;
 | 
					    return content;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,4 +22,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- (IBAction)saveAction:(id)sender;
 | 
					- (IBAction)saveAction:(id)sender;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)loadKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#import "MPAppDelegate_Key.h"
 | 
					#import "MPAppDelegate_Key.h"
 | 
				
			||||||
 | 
					#import "MPConfig.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@interface MPAppDelegate ()
 | 
					@interface MPAppDelegate ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,6 +26,15 @@
 | 
				
			|||||||
@synthesize keyHash;
 | 
					@synthesize keyHash;
 | 
				
			||||||
@synthesize keyHashHex;
 | 
					@synthesize keyHashHex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					+ (void)initialize {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [MPConfig get];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					#ifdef DEBUG
 | 
				
			||||||
 | 
					    [PearlLogger get].autoprintLevel = PearlLogLevelTrace;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
+ (NSManagedObjectContext *)managedObjectContext {
 | 
					+ (NSManagedObjectContext *)managedObjectContext {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return [[self get] managedObjectContext];
 | 
					    return [[self get] managedObjectContext];
 | 
				
			||||||
@@ -44,12 +54,61 @@
 | 
				
			|||||||
    if (!self.passwordWindow)
 | 
					    if (!self.passwordWindow)
 | 
				
			||||||
        self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
 | 
					        self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
 | 
				
			||||||
    [self.passwordWindow showWindow:self];
 | 
					    [self.passwordWindow showWindow:self];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    [self loadKey];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)loadKey {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (!self.key)
 | 
				
			||||||
 | 
					        // Try and load the key from the keychain.
 | 
				
			||||||
 | 
					        [self loadStoredKey];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (!self.key)
 | 
				
			||||||
 | 
					        // Ask the user to set the key through his master password.
 | 
				
			||||||
 | 
					        dispatch_async(dispatch_get_main_queue(), ^{
 | 
				
			||||||
 | 
					            NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
 | 
				
			||||||
 | 
					                                             defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Quit"
 | 
				
			||||||
 | 
					                                 informativeTextWithFormat:@"Your master password is required to unlock the application."];
 | 
				
			||||||
 | 
					            NSTextField *passwordField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)];
 | 
				
			||||||
 | 
					            [alert setAccessoryView:passwordField];
 | 
				
			||||||
 | 
					            [alert layout];
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                NSInteger button = [alert runModal];
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (button == 0)
 | 
				
			||||||
 | 
					                    // "Change" button.
 | 
				
			||||||
 | 
					                    if ([[NSAlert alertWithMessageText:@"Changing Master Password"
 | 
				
			||||||
 | 
					                                         defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
 | 
				
			||||||
 | 
					                             informativeTextWithFormat:
 | 
				
			||||||
 | 
					                          @"This will allow you to log in with a different master password.\n\n"
 | 
				
			||||||
 | 
					                          @"Note that you will only see the sites and passwords for the master password you log in with.\n"
 | 
				
			||||||
 | 
					                          @"If you log in with a different master password, your current sites will be unavailable.\n\n"
 | 
				
			||||||
 | 
					                          @"You can always change back to your current master password later.\n"
 | 
				
			||||||
 | 
					                          @"Your current sites and passwords will then become available again."] runModal] == 1) {
 | 
				
			||||||
 | 
					                        [self forgetKey];
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                if (button == -1) {
 | 
				
			||||||
 | 
					                    // "Quit" button.
 | 
				
			||||||
 | 
					                    [[NSApplication sharedApplication] terminate:self];
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } while (![self tryMasterPassword:[passwordField stringValue]]);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (NSURL *)applicationFilesDirectory {
 | 
					- (NSURL *)applicationFilesDirectory {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
 | 
					    NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
 | 
				
			||||||
    return [appSupportURL URLByAppendingPathComponent:@"com.lyndir.lhunath.MasterPassword"];
 | 
					    NSURL *applicationFilesDirectory = [appSupportURL URLByAppendingPathComponent:@"com.lyndir.lhunath.MasterPassword"];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    NSError *error = nil;
 | 
				
			||||||
 | 
					    [[NSFileManager defaultManager] createDirectoryAtURL:applicationFilesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
 | 
				
			||||||
 | 
					    if (error)
 | 
				
			||||||
 | 
					        [[NSApplication sharedApplication] presentError:error];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return applicationFilesDirectory;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma mark - Core Data stack
 | 
					#pragma mark - Core Data stack
 | 
				
			||||||
@@ -97,20 +156,20 @@
 | 
				
			|||||||
    if (__persistentStoreCoordinator)
 | 
					    if (__persistentStoreCoordinator)
 | 
				
			||||||
        return __persistentStoreCoordinator;
 | 
					        return __persistentStoreCoordinator;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    NSURL *storeURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:@"MasterPassword.storedata"];
 | 
					    NSURL *storeURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
 | 
					    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
 | 
				
			||||||
    [__persistentStoreCoordinator lock];
 | 
					    [__persistentStoreCoordinator lock];
 | 
				
			||||||
    NSError *error = nil;
 | 
					    NSError *error = nil;
 | 
				
			||||||
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:storeURL
 | 
					    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
 | 
				
			||||||
                                                          options:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
					                                                          options:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
				
			||||||
                                                                   [NSNumber numberWithBool:YES],   NSInferMappingModelAutomaticallyOption,
 | 
					                                                                   [NSNumber numberWithBool:YES],   NSInferMappingModelAutomaticallyOption,
 | 
				
			||||||
                                                                   [NSNumber numberWithBool:YES],   NSMigratePersistentStoresAutomaticallyOption,
 | 
					                                                                   [NSNumber numberWithBool:YES],   NSMigratePersistentStoresAutomaticallyOption,
 | 
				
			||||||
                                                                   @"MasterPassword.store",         NSPersistentStoreUbiquitousContentNameKey,
 | 
					 | 
				
			||||||
                                                                   [[[NSFileManager defaultManager]
 | 
					                                                                   [[[NSFileManager defaultManager]
 | 
				
			||||||
                                                                     URLForUbiquityContainerIdentifier:nil]
 | 
					                                                                     URLForUbiquityContainerIdentifier:nil]
 | 
				
			||||||
                                                                    URLByAppendingPathComponent:@"store"
 | 
					                                                                    URLByAppendingPathComponent:@"store"
 | 
				
			||||||
                                                                    isDirectory:YES],               NSPersistentStoreUbiquitousContentURLKey,
 | 
					                                                                    isDirectory:YES],               NSPersistentStoreUbiquitousContentURLKey,
 | 
				
			||||||
 | 
					                                                                   @"MasterPassword.store",         NSPersistentStoreUbiquitousContentNameKey,
 | 
				
			||||||
                                                                   nil]
 | 
					                                                                   nil]
 | 
				
			||||||
                                                            error:&error]) {
 | 
					                                                            error:&error]) {
 | 
				
			||||||
        err(@"Unresolved error %@, %@", error, [error userInfo]);
 | 
					        err(@"Unresolved error %@, %@", error, [error userInfo]);
 | 
				
			||||||
@@ -118,7 +177,7 @@
 | 
				
			|||||||
        wrn(@"Deleted datastore: %@", storeURL);
 | 
					        wrn(@"Deleted datastore: %@", storeURL);
 | 
				
			||||||
        [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];        
 | 
					        [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];        
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        [[NSApplication sharedApplication] presentError:error];
 | 
					        [[NSApplication sharedApplication] presentError:error];
 | 
				
			||||||
        return nil;
 | 
					        return nil;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,15 +8,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#import "MPPasswordWindowController.h"
 | 
					#import "MPPasswordWindowController.h"
 | 
				
			||||||
#import "MPAppDelegate_Key.h"
 | 
					#import "MPAppDelegate_Key.h"
 | 
				
			||||||
 | 
					#import "MPElementEntity.h"
 | 
				
			||||||
 | 
					#import "MPElementGeneratedEntity.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@interface MPPasswordWindowController ()
 | 
					@interface MPPasswordWindowController ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@property (nonatomic, assign) BOOL completingSiteName;
 | 
					@property (nonatomic, strong) NSString                      *oldSiteName;
 | 
				
			||||||
 | 
					@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@implementation MPPasswordWindowController
 | 
					@implementation MPPasswordWindowController
 | 
				
			||||||
@synthesize completingSiteName;
 | 
					@synthesize oldSiteName, siteResults;
 | 
				
			||||||
@synthesize siteField;
 | 
					@synthesize siteField;
 | 
				
			||||||
@synthesize contentField;
 | 
					@synthesize contentField;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,11 +32,11 @@
 | 
				
			|||||||
                                                  }];
 | 
					                                                  }];
 | 
				
			||||||
    [[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil
 | 
					    [[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil
 | 
				
			||||||
                                                  usingBlock:^(NSNotification *note) {
 | 
					                                                  usingBlock:^(NSNotification *note) {
 | 
				
			||||||
                                                      if (!self.completingSiteName) {
 | 
					                                                      NSString *newSiteName = [self.siteField stringValue];
 | 
				
			||||||
                                                          self.completingSiteName = YES;
 | 
					                                                      BOOL shouldComplete = [self.oldSiteName length] < [newSiteName length];
 | 
				
			||||||
 | 
					                                                      self.oldSiteName = newSiteName;
 | 
				
			||||||
 | 
					                                                      if (shouldComplete)
 | 
				
			||||||
                                                          [[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
 | 
					                                                          [[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
 | 
				
			||||||
                                                          self.completingSiteName = NO;
 | 
					 | 
				
			||||||
                                                      }
 | 
					 | 
				
			||||||
                                                  }];
 | 
					                                                  }];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [super windowDidLoad];
 | 
					    [super windowDidLoad];
 | 
				
			||||||
@@ -42,22 +45,70 @@
 | 
				
			|||||||
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
 | 
					- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    NSString *query = [[control stringValue] substringWithRange:charRange];
 | 
					    NSString *query = [[control stringValue] substringWithRange:charRange];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    assert(query);
 | 
				
			||||||
 | 
					    assert([MPAppDelegate get].keyHashHex);
 | 
				
			||||||
    NSFetchRequest *fetchRequest = [MPAppDelegate.managedObjectModel
 | 
					    NSFetchRequest *fetchRequest = [MPAppDelegate.managedObjectModel
 | 
				
			||||||
                                    fetchRequestFromTemplateWithName:@"MPElements"
 | 
					                                    fetchRequestFromTemplateWithName:@"MPElements"
 | 
				
			||||||
                                    substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
					                                    substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
				
			||||||
                                                           query,                                   @"query",
 | 
					                                                           query,                                   @"query",
 | 
				
			||||||
                                                           [MPAppDelegate get].keyHashHex,    @"mpHashHex",
 | 
					                                                           [MPAppDelegate get].keyHashHex,          @"mpHashHex",
 | 
				
			||||||
                                                           nil]];
 | 
					                                                           nil]];
 | 
				
			||||||
 | 
					    [fetchRequest setSortDescriptors:
 | 
				
			||||||
    return [NSArray arrayWithObjects:@"cow", @"milk", @"hippopotamus", nil];
 | 
					     [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    NSError *error = nil;
 | 
				
			||||||
 | 
					    self.siteResults = [[MPAppDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error];
 | 
				
			||||||
 | 
					    if (error)
 | 
				
			||||||
 | 
					        err(@"Couldn't fetch elements: %@", error);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
 | 
				
			||||||
 | 
					    if (self.siteResults)
 | 
				
			||||||
 | 
					        for (MPElementEntity *element in self.siteResults)
 | 
				
			||||||
 | 
					            [mutableResults addObject:element.name];
 | 
				
			||||||
 | 
					    [mutableResults addObject:query];
 | 
				
			||||||
 | 
					    return mutableResults;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)controlTextDidEndEditing:(NSNotification *)obj {
 | 
					- (void)controlTextDidEndEditing:(NSNotification *)obj {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (obj.object == self.siteField) {
 | 
					    if (obj.object == self.siteField) {
 | 
				
			||||||
//        NSString *siteName = [self.siteField stringValue];
 | 
					        NSString *siteName = [self.siteField stringValue];
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
//        [self.contentField setStringValue:];
 | 
					        MPElementEntity *result = nil;
 | 
				
			||||||
 | 
					        for (MPElementEntity *element in self.siteResults)
 | 
				
			||||||
 | 
					            if ([element.name isEqualToString:siteName]) {
 | 
				
			||||||
 | 
					                result = element;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (result)
 | 
				
			||||||
 | 
					            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
 | 
				
			||||||
 | 
					                NSString *description = [result description];
 | 
				
			||||||
 | 
					                [result use];
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                dispatch_async(dispatch_get_main_queue(), ^{
 | 
				
			||||||
 | 
					                    [self.contentField setStringValue:description];
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            [[MPAppDelegate get].managedObjectContext performBlock:^{
 | 
				
			||||||
 | 
					                MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
 | 
				
			||||||
 | 
					                                                                         inManagedObjectContext:[MPAppDelegate get].managedObjectContext];
 | 
				
			||||||
 | 
					                assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
 | 
				
			||||||
 | 
					                assert([MPAppDelegate get].keyHashHex);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                element.name = siteName;
 | 
				
			||||||
 | 
					                element.mpHashHex = [MPAppDelegate get].keyHashHex;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                NSString *description = [element description];
 | 
				
			||||||
 | 
					                [element use];
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                dispatch_async(dispatch_get_main_queue(), ^{
 | 
				
			||||||
 | 
					                    [self.contentField setStringValue:description? description: @""];
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
	<key>CFBundleExecutable</key>
 | 
						<key>CFBundleExecutable</key>
 | 
				
			||||||
	<string>${EXECUTABLE_NAME}</string>
 | 
						<string>${EXECUTABLE_NAME}</string>
 | 
				
			||||||
	<key>CFBundleIconFile</key>
 | 
						<key>CFBundleIconFile</key>
 | 
				
			||||||
	<string></string>
 | 
						<string>iTunesArtwork-Rounded.png</string>
 | 
				
			||||||
	<key>CFBundleIdentifier</key>
 | 
						<key>CFBundleIdentifier</key>
 | 
				
			||||||
	<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string>
 | 
						<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string>
 | 
				
			||||||
	<key>CFBundleInfoDictionaryVersion</key>
 | 
						<key>CFBundleInfoDictionaryVersion</key>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,12 +12,6 @@
 | 
				
			|||||||
#import "MPMainViewController.h"
 | 
					#import "MPMainViewController.h"
 | 
				
			||||||
#import "IASKSettingsReader.h"
 | 
					#import "IASKSettingsReader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@interface MPAppDelegate ()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- (void)askKey:(BOOL)animated;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@implementation MPAppDelegate
 | 
					@implementation MPAppDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@synthesize managedObjectModel = __managedObjectModel;
 | 
					@synthesize managedObjectModel = __managedObjectModel;
 | 
				
			||||||
@@ -165,25 +159,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- (void)loadKey:(BOOL)animated {
 | 
					- (void)loadKey:(BOOL)animated {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (self.key)
 | 
					    if (!self.key)
 | 
				
			||||||
        return;
 | 
					        // Try and load the key from the keychain.
 | 
				
			||||||
 | 
					        [self loadStoredKey];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [self loadStoredKey];
 | 
					    if (!self.key)
 | 
				
			||||||
    if (!self.key) {
 | 
					        // Ask the user to set the key through his master password.
 | 
				
			||||||
        // Key is not known.  Ask user to set/specify it.
 | 
					        dispatch_async(dispatch_get_main_queue(), ^{
 | 
				
			||||||
        dbg(@"Key not known.  Will ask user.");
 | 
					            [self.navigationController presentViewController:
 | 
				
			||||||
        [self askKey:animated];
 | 
					             [self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"]
 | 
				
			||||||
        return;
 | 
					                                                    animated:animated completion:nil];
 | 
				
			||||||
    }
 | 
					        });
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- (void)askKey:(BOOL)animated {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dispatch_async(dispatch_get_main_queue(), ^{
 | 
					 | 
				
			||||||
        [self.navigationController presentViewController:
 | 
					 | 
				
			||||||
                [self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"]
 | 
					 | 
				
			||||||
                                                animated:animated completion:nil];
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)applicationWillResignActive:(UIApplication *)application {
 | 
					- (void)applicationWillResignActive:(UIApplication *)application {
 | 
				
			||||||
@@ -207,11 +193,6 @@
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
+ (MPAppDelegate *)get {
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    return (MPAppDelegate *)[super get];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
+ (NSManagedObjectContext *)managedObjectContext {
 | 
					+ (NSManagedObjectContext *)managedObjectContext {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return [[self get] managedObjectContext];
 | 
					    return [[self get] managedObjectContext];
 | 
				
			||||||
@@ -285,11 +266,11 @@
 | 
				
			|||||||
                                                          options:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
					                                                          options:[NSDictionary dictionaryWithObjectsAndKeys:
 | 
				
			||||||
                                                                   [NSNumber numberWithBool:YES],   NSInferMappingModelAutomaticallyOption,
 | 
					                                                                   [NSNumber numberWithBool:YES],   NSInferMappingModelAutomaticallyOption,
 | 
				
			||||||
                                                                   [NSNumber numberWithBool:YES],   NSMigratePersistentStoresAutomaticallyOption,
 | 
					                                                                   [NSNumber numberWithBool:YES],   NSMigratePersistentStoresAutomaticallyOption,
 | 
				
			||||||
                                                                   @"MasterPassword.store",         NSPersistentStoreUbiquitousContentNameKey,
 | 
					 | 
				
			||||||
                                                                   [[[NSFileManager defaultManager]
 | 
					                                                                   [[[NSFileManager defaultManager]
 | 
				
			||||||
                                                                     URLForUbiquityContainerIdentifier:nil]
 | 
					                                                                     URLForUbiquityContainerIdentifier:nil]
 | 
				
			||||||
                                                                    URLByAppendingPathComponent:@"store"
 | 
					                                                                    URLByAppendingPathComponent:@"store"
 | 
				
			||||||
                                                                    isDirectory:YES],               NSPersistentStoreUbiquitousContentURLKey,
 | 
					                                                                    isDirectory:YES],               NSPersistentStoreUbiquitousContentURLKey,
 | 
				
			||||||
 | 
					                                                                   @"MasterPassword.store",         NSPersistentStoreUbiquitousContentNameKey,
 | 
				
			||||||
                                                                   nil]
 | 
					                                                                   nil]
 | 
				
			||||||
                                                            error:&error]) {
 | 
					                                                            error:&error]) {
 | 
				
			||||||
        err(@"Unresolved error %@, %@", error, [error userInfo]);
 | 
					        err(@"Unresolved error %@, %@", error, [error userInfo]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -181,7 +181,7 @@
 | 
				
			|||||||
        self.passwordCounter.text = [NSString stringWithFormat:@"%u", ((MPElementGeneratedEntity *) self.activeElement).counter];
 | 
					        self.passwordCounter.text = [NSString stringWithFormat:@"%u", ((MPElementGeneratedEntity *) self.activeElement).counter];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
 | 
					    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
 | 
				
			||||||
        NSString *description = self.activeElement.description;
 | 
					        NSString *description = [self.activeElement description];
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        dispatch_async(dispatch_get_main_queue(), ^{
 | 
					        dispatch_async(dispatch_get_main_queue(), ^{
 | 
				
			||||||
            self.contentField.text = description;
 | 
					            self.contentField.text = description;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -264,6 +264,7 @@
 | 
				
			|||||||
                                      MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
 | 
					                                      MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
 | 
				
			||||||
                                                                                               inManagedObjectContext:self.fetchedResultsController.managedObjectContext];
 | 
					                                                                                               inManagedObjectContext:self.fetchedResultsController.managedObjectContext];
 | 
				
			||||||
                                      assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
 | 
					                                      assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
 | 
				
			||||||
 | 
					                                      assert([MPAppDelegate get].keyHashHex);
 | 
				
			||||||
                                      
 | 
					                                      
 | 
				
			||||||
                                      element.name = siteName;
 | 
					                                      element.name = siteName;
 | 
				
			||||||
                                      element.mpHashHex = [MPAppDelegate get].keyHashHex;
 | 
					                                      element.mpHashHex = [MPAppDelegate get].keyHashHex;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								Resources/iTunesArtwork-Rounded.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Resources/iTunesArtwork-Rounded.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 109 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								iTunesArtwork.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								iTunesArtwork.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 152 KiB  | 
		Reference in New Issue
	
	Block a user