From 3eb98a516cd19a24928679bd618b4fcdd4bbe879 Mon Sep 17 00:00:00 2001 From: michael starke Date: Tue, 16 Dec 2014 20:37:55 +0100 Subject: [PATCH] Autotype for a selected entry. WIP --- MacPass/AutotypeCandidateSelectionWindow.xib | 15 ++-- MacPass/MPAppDelegate.h | 6 +- MacPass/MPAppDelegate.m | 3 +- MacPass/MPAutotypeDaemon.h | 4 +- MacPass/MPAutotypeDaemon.m | 90 +++++++++++--------- MacPass/MPDocument+Autotype.h | 22 ++--- MacPass/MPDocument+Autotype.m | 4 +- MacPass/MPDocumentWindowController.h | 2 + MacPass/MPDocumentWindowController.m | 38 +++++---- 9 files changed, 101 insertions(+), 83 deletions(-) diff --git a/MacPass/AutotypeCandidateSelectionWindow.xib b/MacPass/AutotypeCandidateSelectionWindow.xib index bc6b74a8..b7dad74d 100644 --- a/MacPass/AutotypeCandidateSelectionWindow.xib +++ b/MacPass/AutotypeCandidateSelectionWindow.xib @@ -1,8 +1,8 @@ - + - - + + @@ -12,7 +12,7 @@ - + @@ -23,7 +23,6 @@ - @@ -64,7 +61,6 @@ DQ - @@ -73,7 +69,6 @@ DQ - diff --git a/MacPass/MPAppDelegate.h b/MacPass/MPAppDelegate.h index 4a90b065..064dc769 100644 --- a/MacPass/MPAppDelegate.h +++ b/MacPass/MPAppDelegate.h @@ -22,12 +22,15 @@ #import -FOUNDATION_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings; +APPKIT_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings; + +@class MPAutotypeDaemon; @interface MPAppDelegate : NSObject @property (strong) IBOutlet NSWindow *passwordCreatorWindow; @property (strong) IBOutlet NSWindow *welcomeWindow; +@property (strong) MPAutotypeDaemon *autotypeDaemon; @property (weak) IBOutlet NSMenuItem *saveMenuItem; @property (nonatomic, assign) BOOL isAllowedToStoreKeyFile; @@ -41,7 +44,6 @@ FOUNDATION_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings; */ - (IBAction)clearRememberdKeyFiles:(id)sender; - - (NSString *)applicationName; - (void)lockAllDocuments; diff --git a/MacPass/MPAppDelegate.m b/MacPass/MPAppDelegate.m index f9ad564b..02838738 100644 --- a/MacPass/MPAppDelegate.m +++ b/MacPass/MPAppDelegate.m @@ -44,7 +44,6 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi @private MPServerDaemon *serverDaemon; MPLockDaemon *lockDaemon; - MPAutotypeDaemon *autotypeDaemon; MPDockTileHelper *dockTileHelper; BOOL _shouldOpenFile; // YES if app was started to open a } @@ -158,7 +157,7 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi - (void)applicationDidFinishLaunching:(NSNotification *)notification { serverDaemon = [[MPServerDaemon alloc] init]; lockDaemon = [[MPLockDaemon alloc] init]; - autotypeDaemon = [[MPAutotypeDaemon alloc] init]; + self.autotypeDaemon = [[MPAutotypeDaemon alloc] init]; //dockTileHelper = [[MPDockTileHelper alloc] init]; } diff --git a/MacPass/MPAutotypeDaemon.h b/MacPass/MPAutotypeDaemon.h index afa33d79..10a7c974 100644 --- a/MacPass/MPAutotypeDaemon.h +++ b/MacPass/MPAutotypeDaemon.h @@ -9,6 +9,7 @@ #import @class DDHotKey; +@class KPKEntry; /** * The autotype daemon is repsonsible for registering the globa hotkey and to perform any autotype actions @@ -19,7 +20,8 @@ @property (weak) IBOutlet NSPopUpButton *matchSelectionButton; @property (readonly, strong) DDHotKey *registredHotKey; -- (IBAction)executeAutotypeWithSelectedMatch:(id)sender; +- (void)performAutotypeForEntry:(KPKEntry *)entryOrNil; +- (IBAction)performAutotypeWithSelectedMatch:(id)sender; - (IBAction)cancelAutotypeSelection:(id)sender; @end diff --git a/MacPass/MPAutotypeDaemon.m b/MacPass/MPAutotypeDaemon.m index 0c3c2b20..b9accd23 100644 --- a/MacPass/MPAutotypeDaemon.m +++ b/MacPass/MPAutotypeDaemon.m @@ -44,7 +44,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; #pragma mark - #pragma mark Lifecylce -- (id)init { +- (instancetype)init { self = [super init]; if (self) { _enabled = NO; @@ -59,16 +59,17 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyGlobalAutotypeKeyDataKey] options:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_applicationWillBecomeActive:) - name:NSApplicationWillBecomeActiveNotification - object:[NSApplication sharedApplication]]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self + selector:@selector(_didDeactivateApplication:) + name:NSWorkspaceDidDeactivateApplicationNotification + object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; [self unbind:NSStringFromSelector(@selector(enabled))]; [self unbind:NSStringFromSelector(@selector(hotKeyData))]; } @@ -93,11 +94,12 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; } } +- (void)executeAutotypeForEntry:(KPKEntry *)entry { +} #pragma mark - #pragma mark Actions - -- (void)executeAutotypeWithSelectedMatch:(id)sender { +- (void)performAutotypeWithSelectedMatch:(id)sender { NSMenuItem *item = [self.matchSelectionButton selectedItem]; MPAutotypeContext *context = [item representedObject]; [self.matchSelectionWindow orderOut:self]; @@ -107,7 +109,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; - (void)cancelAutotypeSelection:(id)sender { [self.matchSelectionWindow orderOut:sender]; if(self.targetPID) { - [MPAutotypeDaemon _orderApplicationToFront:self.targetPID]; + [self _orderApplicationToFront:self.targetPID]; } } @@ -115,13 +117,11 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; #pragma mark Hotkey evaluation - (void)_didPressHotKey { - [self _performAutotypeUsingCurrentWindowAndApplication:YES]; + [self _updateTargetInfoForFrontMostApplication]; + [self performAutotypeForEntry:nil]; } -- (void)_performAutotypeUsingCurrentWindowAndApplication:(BOOL)useCurrentWindowAndApplication { - if(useCurrentWindowAndApplication) { - [self _updateTargetApplicationAndWindow]; - } +- (void)performAutotypeForEntry:(KPKEntry *)entryOrNil { NSInteger pid = [[NSProcessInfo processInfo] processIdentifier]; if(self.targetPID == pid) { return; // We do not perform Autotype on ourselves @@ -129,11 +129,16 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; MPDocument *document = [self _findAutotypeDocument]; if(!document) { - return; // nothing to do + /* We do not have a document. This can be + a) there is none - nothing happens + b) there is at least one, but locked - we get called again after the document has been unlocked + */ + return; } - MPAutotypeContext *context = [self _autotypeContextInDocument:document forWindowTitle:self.targetWindowTitle]; - if(useCurrentWindowAndApplication) { + MPAutotypeContext *context = [self _autotypeContextInDocument:document forWindowTitle:self.targetWindowTitle preferredEntry:entryOrNil]; + /* TODO: that's popping up if the mulit seleciton dialog goes up! */ + if(!entryOrNil) { NSImage *appIcon = [[NSApplication sharedApplication] applicationIconImage]; NSString *label = context ? NSLocalizedString(@"AUTOTYPE_OVERLAY_SINGLE_MATCH", "") : NSLocalizedString(@"AUTOTYPE_OVERLAY_NO_MATCH", ""); [[MPOverlayWindowController sharedController] displayOverlayImage:appIcon label:label atView:nil]; @@ -159,12 +164,12 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; return currentDocument; } -- (MPAutotypeContext *)_autotypeContextInDocument:(MPDocument *)document forWindowTitle:(NSString *)windowTitle { +- (MPAutotypeContext *)_autotypeContextInDocument:(MPDocument *)document forWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entry { /* Query the document to generate a autotype command list for the window title We do not care where this came form, just get the autotype commands */ - NSArray *autotypeCandidates = [document autotypContextsForWindowTitle:windowTitle]; + NSArray *autotypeCandidates = [document autotypContextsForWindowTitle:windowTitle preferredEntry:entry]; NSUInteger candidates = [autotypeCandidates count]; if(candidates == 0) { return nil; @@ -180,13 +185,13 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; if(nil == context) { return; // No context to work with } - + if([self _orderApplicationToFront:self.targetPID]) { + /* Sleep a bit after the app was activated */ + /* TODO - we can use a saver way and use a notification to chekc if the app actally was activated */ + usleep(1 * NSEC_PER_MSEC); + } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSArray *commands = [MPAutotypeCommand commandsForContext:context]; - if([MPAutotypeDaemon _orderApplicationToFront:self.targetPID]) { - /* Sleep a bit after the app was activated */ - usleep(0.5 * NSEC_PER_MSEC); - } for(MPAutotypeCommand *command in commands) { [command execute]; } @@ -218,9 +223,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; } } -- (NSDictionary *)_frontMostApplicationInfoDict { - NSRunningApplication *frontApplication = [[NSWorkspace sharedWorkspace] frontmostApplication]; - +- (NSDictionary *)_infoDictionaryForApplication:(NSRunningApplication *)application { NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID)); for(NSDictionary *windowDict in currentWindows) { NSString *windowTitle = windowDict[(NSString *)kCGWindowName]; @@ -228,7 +231,7 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; continue; } NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID]; - if(processId && [processId isEqualToNumber:@(frontApplication.processIdentifier)]) { + if(processId && [processId isEqualToNumber:@(application.processIdentifier)]) { return @{ kMPWindowTitleKey: windowDict[(NSString *)kCGWindowName], kMPProcessIdentifierKey : processId @@ -265,28 +268,28 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; [self.matchSelectionButton setMenu:associationMenu]; [self.matchSelectionWindow makeKeyAndOrderFront:self]; [NSApp activateIgnoringOtherApps:YES]; - /* Setup Items in Popup */ } #pragma mark - #pragma mark MPDocument Notifications - (void)_didUnlockDatabase:(NSNotification *)notification { + /* Remove ourselves and call again to search matches */ [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self _performAutotypeUsingCurrentWindowAndApplication:NO]; + [self performAutotypeForEntry:nil]; } #pragma mark - #pragma mark NSApplication Notifications -- (void)_applicationWillBecomeActive:(NSNotification *)notification { - //[self _updateTargetApplicationAndWindow]; - //NSLog(@"_applicaiontWillBecomActive"); +- (void)_didDeactivateApplication:(NSNotification *)notification { + NSDictionary *userInfo = notification.userInfo; + [self _updateTargeInformationForApplication:userInfo[NSWorkspaceApplicationKey]]; } #pragma mark - #pragma mark Application information -+ (BOOL)_orderApplicationToFront:(pid_t)processIdentifier { +- (BOOL)_orderApplicationToFront:(pid_t)processIdentifier { NSRunningApplication *runingApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:processIdentifier]; NSRunningApplication *frontApplication = [[NSWorkspace sharedWorkspace] frontmostApplication]; if(frontApplication.processIdentifier == processIdentifier) { @@ -295,15 +298,20 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; [runingApplication activateWithOptions:0]; return YES; } +- (void)_updateTargetInfoForFrontMostApplication { + [self _updateTargeInformationForApplication:[[NSWorkspace sharedWorkspace] frontmostApplication]]; +} -- (void)_updateTargetApplicationAndWindow { - /* - Determine the window title of the current front most application - Start searching the db for the best fit (based on title, then on window associations - */ - NSDictionary *frontApplicationInfoDict = [self _frontMostApplicationInfoDict]; - self.targetPID = [frontApplicationInfoDict[kMPProcessIdentifierKey] intValue]; - self.targetWindowTitle = frontApplicationInfoDict[kMPWindowTitleKey]; +- (void)_updateTargeInformationForApplication:(NSRunningApplication *)application { + if(!application) { + self.targetPID = -1; + self.targetWindowTitle = @""; + } + else { + NSDictionary *frontApplicationInfoDict = [self _infoDictionaryForApplication:application]; + self.targetPID = [frontApplicationInfoDict[kMPProcessIdentifierKey] intValue]; + self.targetWindowTitle = frontApplicationInfoDict[kMPWindowTitleKey]; + } } @end diff --git a/MacPass/MPDocument+Autotype.h b/MacPass/MPDocument+Autotype.h index e66d6b58..1d82e084 100644 --- a/MacPass/MPDocument+Autotype.h +++ b/MacPass/MPDocument+Autotype.h @@ -25,23 +25,25 @@ @interface MPDocument (Autotype) /** - * Tests the given item for a possible wrong autotype format - * MacPass 0.4 and 0.4.1 did store wrong Autotype sequences and thus mangled database files - * - * @param item Item to test for malformation. Allowed Items are KPKNode, KPKEntry, KPKGroup and KPKAutotype - * - * @return YES if the given item is considered a possible candidate. NO in all other cases - */ +* Tests the given item for a possible wrong autotype format +* MacPass 0.4 and 0.4.1 did store wrong Autotype sequences and thus mangled database files +* +* @param item Item to test for malformation. Allowed Items are KPKNode, KPKEntry, KPKGroup and KPKAutotype +* +* @return YES if the given item is considered a possible candidate. NO in all other cases +*/ + (BOOL)isCandidateForMalformedAutotype:(id)item; /** - * Returns an NSArray containing all Autotype Contexts that match the given window title + * Returns an NSArray containing all Autotype Contexts that match the given window title. + * If no entry is set, all entries in the document will be searched * * @param windowTitle Window title to search matches for + * @param entry Entry to use for lookup. If nil lookup will be performed in complete document * - * @return NSArray of MPAutotypeContexts for the given window title + * @return NSArray of MPAutotypeContext objects matching the window title. */ -- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle; +- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entryOrNil; /** * Checks if the document has malformed autotype items * diff --git a/MacPass/MPDocument+Autotype.m b/MacPass/MPDocument+Autotype.m index 0f58fb58..84c1f9e0 100644 --- a/MacPass/MPDocument+Autotype.m +++ b/MacPass/MPDocument+Autotype.m @@ -46,11 +46,11 @@ return (NSOrderedSame == [@"{TAB}{USERNAME}{TAB}{PASSWORD}{ENTER}" compare:keystrokeSequence options:NSCaseInsensitiveSearch]); } -- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle { +- (NSArray *)autotypContextsForWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entry { if(!windowTitle) { return nil; } - NSArray *autotypeEntries = [self.root autotypeableChildEntries]; + NSArray *autotypeEntries = entry ? [[NSArray alloc] initWithObjects:entry, nil] : [self.root autotypeableChildEntries]; NSMutableArray *contexts = [[NSMutableArray alloc] initWithCapacity:MAX(1,ceil([autotypeEntries count] / 4.0))]; for(KPKEntry *entry in autotypeEntries) { /* TODO: diff --git a/MacPass/MPDocumentWindowController.h b/MacPass/MPDocumentWindowController.h index fb4de2b9..d57f702e 100644 --- a/MacPass/MPDocumentWindowController.h +++ b/MacPass/MPDocumentWindowController.h @@ -50,6 +50,8 @@ - (IBAction)pickExpiryDate:(id)sender; +- (IBAction)performAutotypeForEntry:(id)sender; + #pragma mark Helper - (IBAction)fixAutotype:(id)sender; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 1ddd57a6..46a17095 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -7,25 +7,26 @@ // #import "MPDocumentWindowController.h" -#import "MPDocument.h" -#import "MPPasswordInputController.h" -#import "MPEntryViewController.h" -#import "MPToolbarDelegate.h" -#import "MPOutlineViewController.h" -#import "MPInspectorViewController.h" -#import "MPAppDelegate.h" #import "MPActionHelper.h" -#import "MPDatabaseSettingsWindowController.h" -#import "MPPasswordEditWindowController.h" +#import "MPAppDelegate.h" +#import "MPAutotypeDaemon.h" #import "MPConstants.h" -#import "MPSettingsHelper.h" -#import "MPDocumentWindowDelegate.h" -#import "MPFixAutotypeWindowController.h" - #import "MPContextToolbarButton.h" -#import "KPKTree.h" -#import "KPKEntry.h" +#import "MPDatabaseSettingsWindowController.h" +#import "MPDocument.h" +#import "MPDocumentWindowDelegate.h" +#import "MPEntryViewController.h" +#import "MPFixAutotypeWindowController.h" +#import "MPInspectorViewController.h" +#import "MPOutlineViewController.h" +#import "MPPasswordEditWindowController.h" +#import "MPPasswordInputController.h" +#import "MPSettingsHelper.h" +#import "MPToolbarDelegate.h" + #import "KPKCompositeKey.h" +#import "KPKEntry.h" +#import "KPKTree.h" typedef NS_ENUM(NSUInteger, MPAlertContext) { MPAlertLossySaveWarning, @@ -369,6 +370,13 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword); [[NSUserDefaults standardUserDefaults] setBool:!inspectorWasVisible forKey:kMPSettingsKeyShowInspector]; } +- (void)performAutotypeForEntry:(id)sender { + id entryResolver = [NSApp targetForAction:@selector(currentTargetEntry)]; + KPKEntry *targetEntry = [entryResolver currentTargetEntry]; + MPAutotypeDaemon *autotyped = ((MPAppDelegate *)[NSApplication sharedApplication]).autotypeDaemon; + [autotyped performAutotypeForEntry:targetEntry]; +} + - (void)showInspector:(id)sender { if(![self _isInspectorVisible]) { [self toggleInspector:sender];