From 7c5cea9c8d0aaae973d5f19dfe9afefc8ebd41c5 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Tue, 8 May 2012 13:41:54 +0200 Subject: [PATCH] Mac support for preferences: rememberKey, saveKey, useICloud. [ADDED] OS X: Lots of warning checks when compiling. [ADDED] OS X: Ability to enable and disable iCloud. [ADDED] OS X: Support for rememberKey & saveKey. [UPDATED] storeKey -> saveKey: makes more sense to people. [IMPROVED] OS X: Password display window fancier. --- External/Pearl | 2 +- MasterPassword-Mac.xcodeproj/project.pbxproj | 57 +++- MasterPassword/MPAppDelegate_Key.m | 12 +- MasterPassword/MPAppDelegate_Store.m | 6 - MasterPassword/MPConfig.h | 3 +- MasterPassword/MPConfig.m | 12 +- MasterPassword/Mac/MPAppDelegate.h | 8 +- MasterPassword/Mac/MPAppDelegate.m | 112 ++++---- .../Mac/MPPasswordWindowController.h | 9 +- .../Mac/MPPasswordWindowController.m | 104 ++++++- .../Mac/MPPasswordWindowController.xib | 11 +- MasterPassword/Mac/en.lproj/MainMenu.xib | 264 +++++++++++++++++- MasterPassword/iOS/MPAppDelegate.m | 2 +- 13 files changed, 503 insertions(+), 99 deletions(-) diff --git a/External/Pearl b/External/Pearl index a78cd7da..a8346d67 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit a78cd7daf1fae0fc9e86702de9b9f6d5782ae352 +Subproject commit a8346d6715eb258d11d41a4775e8f1abee620312 diff --git a/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword-Mac.xcodeproj/project.pbxproj index cc9f7dc0..e6606be5 100644 --- a/MasterPassword-Mac.xcodeproj/project.pbxproj +++ b/MasterPassword-Mac.xcodeproj/project.pbxproj @@ -6093,7 +6093,12 @@ ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CXX0X_EXTENSIONS = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -6104,10 +6109,32 @@ "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; @@ -6125,15 +6152,42 @@ ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CXX0X_EXTENSIONS = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_OBJCPP_ARC_ABI = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; @@ -6209,6 +6263,7 @@ DA79A9F5155821E500BAA07A /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; DAB8D982150374AC00CED3BC /* Build configuration list for PBXProject "MasterPassword-Mac" */ = { isa = XCConfigurationList; diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m index 88971fc0..58f3f428 100644 --- a/MasterPassword/MPAppDelegate_Key.m +++ b/MasterPassword/MPAppDelegate_Key.m @@ -17,7 +17,7 @@ static NSDictionary *keyQuery() { static NSDictionary *MPKeyQuery = nil; if (!MPKeyQuery) MPKeyQuery = [PearlKeyChain createQueryForClass:kSecClassGenericPassword - attributes:[NSDictionary dictionaryWithObject:@"Stored Master Password" + attributes:[NSDictionary dictionaryWithObject:@"Saved Master Password" forKey:(__bridge id)kSecAttrService] matches:nil]; @@ -38,7 +38,7 @@ static NSDictionary *keyHashQuery() { - (void)forgetKey { - dbg(@"Deleting master key and hash from key chain."); + dbg(@"Deleting key and hash from key chain."); [PearlKeyChain deleteItemForQuery:keyQuery()]; [PearlKeyChain deleteItemForQuery:keyHashQuery()]; @@ -55,15 +55,15 @@ static NSDictionary *keyHashQuery() { - (void)loadStoredKey { - if ([[MPConfig get].storeKey boolValue]) { + if ([[MPConfig get].saveKey boolValue]) { // Key is stored in keychain. Load it. dbg(@"Loading key from key chain."); [self updateKey:[PearlKeyChain dataOfItemForQuery:keyQuery()]]; dbg(@" -> Key %@.", self.key? @"found": @"NOT found"); } else { // Key should not be stored in keychain. Delete it. - dbg(@"Deleting key from key chain."); - [PearlKeyChain deleteItemForQuery:keyQuery()]; + if ([PearlKeyChain deleteItemForQuery:keyQuery()] != errSecItemNotFound) + dbg(@"Deleted key from key chain."); #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED [TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored]; #endif @@ -121,7 +121,7 @@ static NSDictionary *keyHashQuery() { kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible, #endif nil]]; - if ([[MPConfig get].storeKey boolValue]) { + if ([[MPConfig get].saveKey boolValue]) { dbg(@"Storing key in key chain."); [PearlKeyChain addOrUpdateItemForQuery:keyQuery() withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: diff --git a/MasterPassword/MPAppDelegate_Store.m b/MasterPassword/MPAppDelegate_Store.m index 278c49a2..aceb8772 100644 --- a/MasterPassword/MPAppDelegate_Store.m +++ b/MasterPassword/MPAppDelegate_Store.m @@ -109,12 +109,6 @@ return self.managedObjectContext; } -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch { - - dbg(@"didSwitchToiCloud:%@", [NSNumber numberWithBool:didSwitch]); - //[masterViewController.iCloudSwitch setOn:didSwitch animated:YES]; -} - - (void)saveContext { [self.managedObjectContext performBlock:^{ diff --git a/MasterPassword/MPConfig.h b/MasterPassword/MPConfig.h index dcd3c99c..b9ac8829 100644 --- a/MasterPassword/MPConfig.h +++ b/MasterPassword/MPConfig.h @@ -8,8 +8,7 @@ @interface MPConfig : PearlConfig -@property (nonatomic, retain) NSNumber *dataStoreError; -@property (nonatomic, retain) NSNumber *storeKey; +@property (nonatomic, retain) NSNumber *saveKey; @property (nonatomic, retain) NSNumber *rememberKey; + (MPConfig *)get; diff --git a/MasterPassword/MPConfig.m b/MasterPassword/MPConfig.m index 4f675e71..018155e9 100644 --- a/MasterPassword/MPConfig.m +++ b/MasterPassword/MPConfig.m @@ -7,21 +7,23 @@ // #import "MPConfig.h" +#import "MPAppDelegate_Shared.h" @implementation MPConfig -@dynamic dataStoreError, storeKey, rememberKey; +@dynamic saveKey, rememberKey; - (id)init { if(!(self = [super init])) - return self; + return nil; [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)), - [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKey)), - [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)), + [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(saveKey)), + [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)), nil]]; + self.delegate = [MPAppDelegate get]; + return self; } diff --git a/MasterPassword/Mac/MPAppDelegate.h b/MasterPassword/Mac/MPAppDelegate.h index 555433b4..d13afce6 100644 --- a/MasterPassword/Mac/MPAppDelegate.h +++ b/MasterPassword/Mac/MPAppDelegate.h @@ -9,15 +9,17 @@ #import #import "MPPasswordWindowController.h" -@interface MPAppDelegate : NSObject +@interface MPAppDelegate : NSObject @property (strong) NSStatusItem *statusItem; -@property (weak) IBOutlet NSMenuItem *unlockItem; @property (weak) IBOutlet NSMenuItem *lockItem; @property (weak) IBOutlet NSMenuItem *showItem; @property (strong) IBOutlet NSMenu *statusMenu; +@property (weak) IBOutlet NSMenuItem *useICloudItem; +@property (weak) IBOutlet NSMenuItem *rememberPasswordItem; +@property (weak) IBOutlet NSMenuItem *savePasswordItem; - (IBAction)activate:(id)sender; -- (IBAction)unlock:(id)sender; +- (IBAction)togglePreference:(NSMenuItem *)sender; @end diff --git a/MasterPassword/Mac/MPAppDelegate.m b/MasterPassword/Mac/MPAppDelegate.m index 5da510c6..44a3d867 100644 --- a/MasterPassword/Mac/MPAppDelegate.m +++ b/MasterPassword/Mac/MPAppDelegate.m @@ -20,16 +20,19 @@ @implementation MPAppDelegate @synthesize statusItem; -@synthesize unlockItem; @synthesize lockItem; @synthesize showItem; @synthesize statusMenu; +@synthesize useICloudItem; +@synthesize rememberPasswordItem; +@synthesize savePasswordItem; @synthesize passwordWindow; @synthesize key; @synthesize keyHash; @synthesize keyHashHex; +#pragma GCC diagnostic ignored "-Wfour-char-constants" static EventHotKeyID MPShowHotKey = { .signature = 'show', .id = 1 }; + (void)initialize { @@ -59,18 +62,40 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven - (void)showMenu { - [self.showItem setEnabled:![self.passwordWindow.window isVisible]]; + self.rememberPasswordItem.state = [[MPConfig get].rememberKey boolValue]? NSOnState: NSOffState; + self.savePasswordItem.state = [[MPConfig get].saveKey boolValue]? NSOnState: NSOffState; + self.showItem.enabled = ![self.passwordWindow.window isVisible]; + [self.statusItem popUpStatusItemMenu:self.statusMenu]; } - (IBAction)activate:(id)sender { - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; + if ([[NSApplication sharedApplication] isActive]) + [self applicationDidBecomeActive:nil]; + else + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; +} + +- (IBAction)togglePreference:(NSMenuItem *)sender { + + if (sender == useICloudItem) + [self.storeManager useiCloudStore:sender.state == NSOffState]; + if (sender == rememberPasswordItem) + [MPConfig get].rememberKey = [NSNumber numberWithBool:![[MPConfig get].rememberKey boolValue]]; + if (sender == savePasswordItem) + [MPConfig get].saveKey = [NSNumber numberWithBool:![[MPConfig get].saveKey boolValue]]; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Setup delegates and listeners. + [MPConfig get].delegate = self; [self addObserver:self forKeyPath:@"key" options:0 context:nil]; + + // Initially, use iCloud. + if ([[MPConfig get].firstRun boolValue]) + [[self storeManager] useiCloudStore:YES]; // Status item. self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; @@ -89,17 +114,40 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef); if(status != noErr) err(@"Error registering hotkey: %d", status); +} + +- (void)applicationWillBecomeActive:(NSNotification *)notification { - [[self storeManager] useiCloudStore:YES]; + if (!self.passwordWindow) + self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; } - (void)applicationDidBecomeActive:(NSNotification *)notification { - if (!self.passwordWindow) - self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"]; - [self.passwordWindow showWindow:self]; + static BOOL firstTime = YES; + if (firstTime) + firstTime = NO; + else + [self.passwordWindow showWindow:self]; +} + +- (void)applicationWillResignActive:(NSNotification *)notification { - [self unlock:self]; + if (![[MPConfig get].rememberKey boolValue]) + self.key = nil; +} + +- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { + + if (configKey == @selector(rememberKey)) + self.rememberPasswordItem.state = [[MPConfig get].rememberKey boolValue]? NSOnState: NSOffState; + if (configKey == @selector(saveKey)) + self.savePasswordItem.state = [[MPConfig get].saveKey boolValue]? NSOnState: NSOffState; +} + +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch { + + self.useICloudItem.state = didSwitch? NSOnState: NSOffState; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @@ -114,54 +162,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven } } -- (IBAction)unlock:(id)sender { - - 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(), ^{ - if (self.key) - return; - - NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked." - defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Quit" - informativeTextWithFormat:@"Your master password is required to unlock the application."]; - NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)]; - [alert setAccessoryView:passwordField]; - [alert layout]; - [passwordField becomeFirstResponder]; - [alert beginSheetModalForWindow:self.passwordWindow.window modalDelegate:self - didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL]; - - [self printStore]; - }); -} - -- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - - if (returnCode == NSAlertAlternateReturn) - // "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]; - - if (returnCode == NSAlertOtherReturn) - // "Quit" button. - [[NSApplication sharedApplication] terminate:self]; - - if (![self tryMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]]) - [self unlock:self]; -} - - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window { return [[self managedObjectContext] undoManager]; diff --git a/MasterPassword/Mac/MPPasswordWindowController.h b/MasterPassword/Mac/MPPasswordWindowController.h index 9c83d3a3..4cc3ddd9 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.h +++ b/MasterPassword/Mac/MPPasswordWindowController.h @@ -8,10 +8,17 @@ #import -@interface MPPasswordWindowController : NSWindowController +@interface MPPasswordWindowController : NSWindowController { + + NSString *_content; +} + +@property (strong) NSString *content; @property (weak) IBOutlet NSTextField *siteField; @property (weak) IBOutlet NSTextField *contentField; @property (weak) IBOutlet NSTextField *tipField; +- (void)unlock; + @end diff --git a/MasterPassword/Mac/MPPasswordWindowController.m b/MasterPassword/Mac/MPPasswordWindowController.m index 8d27aed4..0b0ab652 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.m +++ b/MasterPassword/Mac/MPPasswordWindowController.m @@ -26,11 +26,12 @@ - (void)windowDidLoad { - [self.contentField setStringValue:@""]; + [self setContent:@""]; [self.tipField setStringValue:@""]; [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil usingBlock:^(NSNotification *note) { + [self unlock]; [self.siteField selectText:self]; }]; [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil @@ -53,6 +54,59 @@ [super windowDidLoad]; } +- (void)unlock { + + if (![MPAppDelegate get].key) + // Try and load the key from the keychain. + [[MPAppDelegate get] loadStoredKey]; + + if (![MPAppDelegate get].key) + // Ask the user to set the key through his master password. + dispatch_async(dispatch_get_main_queue(), ^{ + if ([MPAppDelegate get].key) + return; + + NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked." + defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Quit" + informativeTextWithFormat:@"Your master password is required to unlock the application."]; + NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)]; + [alert setAccessoryView:passwordField]; + [alert layout]; + [passwordField becomeFirstResponder]; + [alert beginSheetModalForWindow:self.window modalDelegate:self + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + }); +} + +- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { + + switch (returnCode) { + case NSAlertAlternateReturn: + // "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) + [[MPAppDelegate get] forgetKey]; + break; + + case NSAlertOtherReturn: + // "Quit" button. + [[NSApplication sharedApplication] terminate:self]; + return; + + case NSAlertDefaultReturn: + // "Unlock" button. + [[MPAppDelegate get] tryMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]]; + } + + [self unlock]; +} + - (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index { NSString *query = [[control stringValue] substringWithRange:charRange]; @@ -83,26 +137,30 @@ - (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector { - if (commandSelector == @selector(cancel:)) + if (commandSelector == @selector(cancel:)) { [self.window close]; - if (commandSelector == @selector(insertNewline:) && [[self.contentField stringValue] length]) { + return YES; + } + if (commandSelector == @selector(insertNewline:) && [self.content length]) { [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - if ([[NSPasteboard generalPasteboard] setString:[self.contentField stringValue] forType:NSPasteboardTypeString]) { + if ([[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) { self.tipField.alphaValue = 1; [self.tipField setStringValue:@"Copied!"]; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC); + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^{ [NSAnimationContext beginGrouping]; [[NSAnimationContext currentContext] setDuration:0.2f]; [self.tipField.animator setAlphaValue:0]; [NSAnimationContext endGrouping]; }); + + [[self findElement] use]; return YES; } else wrn(@"Couldn't copy password to pasteboard."); } - return NO; + return NO; } - (void)controlTextDidEndEditing:(NSNotification *)obj { @@ -111,21 +169,47 @@ [self trySite]; } +- (NSString *)content { + + return _content; +} + +- (void)setContent:(NSString *)content { + + _content = content; + + NSShadow *shadow = [NSShadow new]; + shadow.shadowColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.6f]; + shadow.shadowOffset = NSMakeSize(1.0f, -1.0f); + shadow.shadowBlurRadius = 1.2f; + + NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new]; + paragraph.alignment = NSCenterTextAlignment; + + [self.contentField setAttributedStringValue: + [[NSAttributedString alloc] initWithString:_content + attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys: + shadow, NSShadowAttributeName, + paragraph, NSParagraphStyleAttributeName, + nil]]]; +} + - (BOOL)trySite { MPElementEntity *result = [self findElement]; if (!result) { - [self.contentField setStringValue:@""]; + [self setContent:@""]; [self.tipField setStringValue:@""]; return NO; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSString *description = [result.content description]; - [result use]; + if (!description) + description = @""; dispatch_async(dispatch_get_main_queue(), ^{ - [self.contentField setStringValue:description]; + [self setContent:description]; [self.tipField setStringValue:@"Hit enter to copy the password."]; self.tipField.alphaValue = 1; }); @@ -147,7 +231,7 @@ [element use]; dispatch_async(dispatch_get_main_queue(), ^{ - [self.contentField setStringValue:description? description: @""]; + [self setContent:description]; }); }]; */ diff --git a/MasterPassword/Mac/MPPasswordWindowController.xib b/MasterPassword/Mac/MPPasswordWindowController.xib index 6184cee0..097a9015 100644 --- a/MasterPassword/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/Mac/MPPasswordWindowController.xib @@ -36,7 +36,7 @@ NSApplication - 8209 + 277 2 {{600, 530}, {480, 159}} 611845120 @@ -69,7 +69,7 @@ 13 1044 - Site + Site name _NS:9 1 @@ -115,6 +115,7 @@ _NS:9 + YES 6 System @@ -165,8 +166,8 @@ _NS:21 {{0, 0}, {1680, 1028}} - {480, 153} - {480, 339} + {480, 150} + {480, 336} YES @@ -594,7 +595,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - + diff --git a/MasterPassword/Mac/en.lproj/MainMenu.xib b/MasterPassword/Mac/en.lproj/MainMenu.xib index 1bbb2c0d..0efe1acc 100644 --- a/MasterPassword/Mac/en.lproj/MainMenu.xib +++ b/MasterPassword/Mac/en.lproj/MainMenu.xib @@ -61,6 +61,94 @@ NSMenuMixedState + + + Preferences + + 2147483647 + + + submenuAction: + + Preferences + + + + Use iCloud + + 2147483647 + + + + + + YES + Synchronize available sites from your iCloud account. + + 2147483647 + + + + Synchronize available sites from your iCloud account. + + + Helvetica + 12 + 16 + + + 4 + + + + + + + + Remember Password + + 2147483647 + + + + + + YES + Remember the password while the application is running. + + 2147483647 + + + + Remember the password while the application is running. + + + + + + Save Password + + 2147483647 + + + + + + YES + Save the password in your keychain so you don't need to enter it again. + + 2147483647 + + + + Save the password in your keychain so you don't need to enter it again. + + + + + YES + + YES @@ -150,6 +238,54 @@ 736 + + + useICloudItem + + + + 749 + + + + rememberPasswordItem + + + + 750 + + + + savePasswordItem + + + + 751 + + + + togglePreference: + + + + 752 + + + + togglePreference: + + + + 753 + + + + togglePreference: + + + + 754 + @@ -201,6 +337,7 @@ + @@ -224,6 +361,57 @@ + + 739 + + + + + + + + 742 + + + + + + + + + + + + + 743 + + + + + 745 + + + + + 744 + + + + + 746 + + + + + 747 + + + + + 748 + + + @@ -238,14 +426,86 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - 736 + 754 + + + + + MPAppDelegate + NSObject + + id + id + NSMenuItem + + + + activate: + id + + + signOut: + id + + + togglePreference: + NSMenuItem + + + + NSMenuItem + NSMenuItem + NSMenuItem + NSMenuItem + NSMenu + NSMenuItem + + + + lockItem + NSMenuItem + + + rememberPasswordItem + NSMenuItem + + + savePasswordItem + NSMenuItem + + + showItem + NSMenuItem + + + statusMenu + NSMenu + + + useICloudItem + NSMenuItem + + + + IBProjectSource + ./Classes/MPAppDelegate.h + + + - 0 IBCocoaFramework diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m index dc4bac71..1dbb43d0 100644 --- a/MasterPassword/iOS/MPAppDelegate.m +++ b/MasterPassword/iOS/MPAppDelegate.m @@ -160,7 +160,7 @@ [[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock:^(NSNotification *note) { - if ([NSStringFromSelector(@selector(storeKey)) + if ([NSStringFromSelector(@selector(saveKey)) isEqualToString:[note.object description]]) { [self updateKey:self.key]; [self loadKey:YES];