From 030dbd5be71e07ef1011ea5414baebcc99c08b59 Mon Sep 17 00:00:00 2001 From: michael starke Date: Sat, 20 Jul 2013 16:31:20 +0200 Subject: [PATCH] Fixed #46 Binaries get now deleted when entries are delete on trans cleaning MenuEntries are validated and disabled to fix #58. Model still doesn't do any testing --- KeePassKit | 2 +- MacPass/Base.lproj/MainMenu.xib | 31 +++++++++++++- MacPass/MPActionHelper.h | 3 +- MacPass/MPActionHelper.m | 43 +++++++++++++------ MacPass/MPDocument.h | 3 ++ MacPass/MPDocument.m | 42 ++++++++++++++++++- MacPass/MPDocumentWindowController.h | 8 +++- MacPass/MPDocumentWindowController.m | 62 ++++++++++++++++++++++------ MacPass/MPEntryViewController.m | 5 +++ MacPass/MPOutlineViewController.m | 23 +++++++++++ MacPass/MPToolbarDelegate.m | 3 -- 11 files changed, 192 insertions(+), 33 deletions(-) diff --git a/KeePassKit b/KeePassKit index 8008d620..428a53af 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit 8008d6209f3ee95d66c5ac00e661a27b15a458a7 +Subproject commit 428a53af6c68fc4d8c5602fe953ce5dca6ae06cd diff --git a/MacPass/Base.lproj/MainMenu.xib b/MacPass/Base.lproj/MainMenu.xib index b7fd19ba..b01e4e18 100644 --- a/MacPass/Base.lproj/MainMenu.xib +++ b/MacPass/Base.lproj/MainMenu.xib @@ -235,6 +235,15 @@ + + + Lock + L + 262144 + 2147483647 + + + Close @@ -828,6 +837,14 @@ 1260 + + + lock: + + + + 1263 + showPreferences: @@ -932,6 +949,7 @@ + @@ -1272,6 +1290,11 @@ + + 1261 + + + @@ -1291,6 +1314,7 @@ 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 @@ -1346,7 +1370,7 @@ - 1260 + 1263 @@ -1407,6 +1431,7 @@ id id + id id @@ -1418,6 +1443,10 @@ exportDatabase: id + + lock: + id + showDatabaseSettings: id diff --git a/MacPass/MPActionHelper.h b/MacPass/MPActionHelper.h index 0aec7b4d..b53ee40a 100644 --- a/MacPass/MPActionHelper.h +++ b/MacPass/MPActionHelper.h @@ -9,9 +9,9 @@ #import typedef NS_ENUM(NSUInteger, MPActionType) { + MPUnkownAction, // Netural element to be used for returns MPActionAddEntry, // Add an new entry MPActionAddGroup, // Add a new group - MPActionEdit, // Edit entry or group MPActionDelete, // Delete entry or group MPActionCopyUsername, // copy username to pasteboard MPActionCopyPassword, // copy password to pasteboard @@ -25,5 +25,6 @@ typedef NS_ENUM(NSUInteger, MPActionType) { @interface MPActionHelper : NSObject + (SEL)actionOfType:(MPActionType)type; ++ (MPActionType)typeForAction:(SEL)action; @end diff --git a/MacPass/MPActionHelper.m b/MacPass/MPActionHelper.m index ab87bc88..b16bd45f 100644 --- a/MacPass/MPActionHelper.m +++ b/MacPass/MPActionHelper.m @@ -10,25 +10,42 @@ @implementation MPActionHelper -+ (SEL)actionOfType:(MPActionType)type { ++ (NSDictionary *)_actionDictionary { static NSDictionary *actionDict; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ actionDict = @{ - @(MPActionAddEntry) : @"createEntry:", - @(MPActionAddGroup) : @"createGroup:", - @(MPActionCopyPassword) : @"copyPassword:", - @(MPActionCopyURL) : @"copyURL:", - @(MPActionCopyUsername) : @"copyUsername:", - @(MPActionDelete) : @"deleteNode:", - @(MPActionEdit) : @"editEntry:", - @(MPActionOpenURL) : @"openURL:", - @(MPActionToggleInspector) : @"toggleInspector:", - @(MPActionLock) : @"lock:", - @(MPActionEmptyTrash) : @"emptyTrash:" - }; + @(MPActionAddEntry) : @"createEntry:", + @(MPActionAddGroup) : @"createGroup:", + @(MPActionCopyPassword) : @"copyPassword:", + @(MPActionCopyURL) : @"copyURL:", + @(MPActionCopyUsername) : @"copyUsername:", + @(MPActionDelete) : @"deleteNode:", + @(MPActionOpenURL) : @"openURL:", + @(MPActionToggleInspector) : @"toggleInspector:", + @(MPActionLock) : @"lock:", + @(MPActionEmptyTrash) : @"emptyTrash:" + }; }); + return actionDict; +} + ++ (SEL)actionOfType:(MPActionType)type { + NSDictionary *actionDict = [self _actionDictionary]; return NSSelectorFromString(actionDict[@(type)]); } ++ (MPActionType)typeForAction:(SEL)action { + NSString *selectorString = NSStringFromSelector(action); + NSArray *selectors = [[self _actionDictionary] allValues]; + NSUInteger index = [selectors indexOfObject:selectorString]; + if(index == NSNotFound) { + return MPUnkownAction; + } + NSArray *keys = [[self _actionDictionary] allKeysForObject:selectorString]; + NSAssert([keys count] == 1, @"There should only be one object for the specified key"); + return [[keys lastObject] integerValue]; +} + + @end diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index a4fcd720..ca75ea97 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -42,6 +42,7 @@ APPKIT_EXTERN NSString *const MPDocumentGroupKey; @property (strong, readonly, nonatomic) KdbTree *tree; @property (weak, readonly, nonatomic) KdbGroup *root; @property (readonly, strong) MPRootAdapter *rootAdapter; +@property (weak, readonly) KdbGroup *trash; @property (nonatomic, copy) NSString *password; @property (nonatomic, strong) NSURL *key; @@ -68,6 +69,8 @@ APPKIT_EXTERN NSString *const MPDocumentGroupKey; - (void)useGroupAsTrash:(KdbGroup *)group; - (void)useGroupAsTemplate:(KdbGroup *)group; +- (BOOL)isItemTrashed:(id)item; + #pragma mark Export - (void)writeXMLToURL:(NSURL *)url; diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 44fcd015..9d11e1f4 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -65,7 +65,6 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey"; @property (strong) NSURL *lockFileURL; @property (readonly) BOOL useTrash; -@property (weak, readonly) KdbGroup *trash; @property (strong) IBOutlet NSView *warningView; @property (weak) IBOutlet NSImageView *warningViewImage; @@ -307,6 +306,26 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey"; return nil; } +- (BOOL)isItemTrashed:(id)item { + BOOL validItem = [item isKindOfClass:[KdbEntry class]] || [item isKindOfClass:[KdbGroup class]]; + if(!item) { + return NO; + } + if(item == self.trash) { + return NO; // No need to look further as this is the trashcan + } + if(validItem) { + BOOL isTrashed = NO; + id parent = [item parent]; + while( parent && !isTrashed ) { + isTrashed = (parent == self.trash); + parent = [parent parent]; + } + return isTrashed; + } + return NO; +} + - (void)useGroupAsTrash:(KdbGroup *)group { if(self.useTrash) { Kdb4Group *groupv4 = (Kdb4Group *)group; @@ -449,6 +468,9 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey"; #pragma mark Actions - (void)emptyTrash:(id)sender { + if(self.version != MPDatabaseVersion4) { + return; // We have no trash on those file types + } NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:NSWarningAlertStyle]; [alert setMessageText:NSLocalizedString(@"WARNING_ON_EMPTY_TRASH_TITLE", "")]; @@ -474,6 +496,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey"; BOOL hasEntries = [self.trash.entries count] > 0; return (hasEntries || hasGroups); } + return [super validateUserInterfaceItem:anItem]; } @@ -517,7 +540,24 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey"; for(KdbGroup *group in [self.trash childGroups]) { [[self undoManager] removeAllActionsWithTarget:group]; } + [self _cleanTrashedBinaries]; [self.trash clear]; } +- (void)_cleanTrashedBinaries { + NSMutableSet *clearKeys = [[NSMutableSet alloc] initWithCapacity:20]; + NSMutableArray *clearBinaries = [[NSMutableArray alloc] initWithCapacity:[self.treeV4.binaries count]]; + for(Kdb4Entry *entry in [self.trash childEntries]) { + for(BinaryRef *binaryRef in entry.binaries) { + [clearKeys addObject:@(binaryRef.ref)]; + } + } + for(Binary *binary in self.treeV4.binaries) { + if([clearKeys containsObject:@(binary.binaryId)]) { + [clearBinaries addObject:binary]; + } + } + [self.treeV4.binaries removeObjectsInArray:clearBinaries]; +} + @end diff --git a/MacPass/MPDocumentWindowController.h b/MacPass/MPDocumentWindowController.h index b381b69f..c4d68702 100644 --- a/MacPass/MPDocumentWindowController.h +++ b/MacPass/MPDocumentWindowController.h @@ -32,6 +32,12 @@ APPKIT_EXTERN NSString *const MPCurrentItemChangedNotification; @property (readonly, unsafe_unretained) KdbGroup *currentGroup; @property (readonly, unsafe_unretained) KdbEntry *currentEntry; +/** + @param action The action that should be validatet + @param item The item that the action affects. Pass nil to fall back for default item + @returns YES if the action is valid, NO otherwise + */ +- (BOOL)validateAction:(SEL)action forItem:(id)item; - (void)showEntries; - (void)showPasswordInput; @@ -40,7 +46,7 @@ APPKIT_EXTERN NSString *const MPCurrentItemChangedNotification; - (IBAction)showDatabaseSettings:(id)sender; - (IBAction)exportDatabase:(id)sender; -- (void)lock:(id)sender; +- (IBAction)lock:(id)sender; - (void)createGroup:(id)sender; - (void)toggleInspector:(id)sender; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index b2550046..5ba56dc2 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -192,6 +192,9 @@ NSString *const MPCurrentItemChangedNotification = @"com.hicknhack.macpass.MPCur if(itemAction == @selector(exportDatabase:)) { enabled = (nil != document.treeV4); } + if(itemAction == [MPActionHelper actionOfType:MPActionDelete]) { + enabled &= (nil != _currentItem) && (_currentItem != document.trash); + } enabled &= !( !document.decrypted || document.isLocked || document.isReadOnly ); return enabled; @@ -202,20 +205,55 @@ NSString *const MPCurrentItemChangedNotification = @"com.hicknhack.macpass.MPCur if(!document.decrypted || document.isLocked || document.isReadOnly) { return NO; } - SEL itemAction = [theItem action]; - if( itemAction == [MPActionHelper actionOfType:MPActionLock]) { - return document.hasPasswordOrKey; + MPActionType actionType = [MPActionHelper typeForAction:[theItem action]]; + switch (actionType) { + case MPActionAddGroup: + case MPActionAddEntry: + return (nil != _outlineViewController.selectedGroup); + case MPActionDelete: { + BOOL valid = (nil != _currentItem); + valid &= (_currentItem != document.trash); + valid &= ![document isItemTrashed:_currentItem]; + return valid; + } + case MPActionLock: + return document.hasPasswordOrKey; + + case MPActionToggleInspector: + return (nil != [_splitView superview]); + + default: + return YES; } - if(itemAction == [MPActionHelper actionOfType:MPActionAddEntry]) { - return (nil != _outlineViewController.selectedGroup); + return YES; +} + +- (BOOL)validateAction:(SEL)action forItem:(id)item { + MPDocument *document = [self document]; + if(!document.decrypted || document.isLocked || document.isReadOnly) { + return NO; } - if(itemAction == [MPActionHelper actionOfType:MPActionDelete]) { - return (nil != _currentItem); + MPActionType actionType = [MPActionHelper typeForAction:action]; + switch (actionType) { + case MPActionAddGroup: + case MPActionAddEntry: + // test if Group is in trash + return (nil != _outlineViewController.selectedGroup); + case MPActionDelete: { + BOOL valid = (nil != _currentItem); + valid &= (_currentItem != document.trash); + valid &= ![document isItemTrashed:_currentItem]; + return valid; + } + case MPActionLock: + return document.hasPasswordOrKey; + + case MPActionToggleInspector: + return (nil != [_splitView superview]); + + default: + return YES; } - if(itemAction == [MPActionHelper actionOfType:MPActionToggleInspector]) { - return (nil != [_splitView superview]); - } - return YES; } @@ -235,7 +273,7 @@ NSString *const MPCurrentItemChangedNotification = @"com.hicknhack.macpass.MPCur [self _showDatabaseSetting:MPDatabaseSettingsTabGeneral]; } -- (void)lock:(id)sender { +- (IBAction)lock:(id)sender { MPDocument *document = [self document]; if(!document.hasPasswordOrKey) { return; // Document needs a password/keyfile to be lockable diff --git a/MacPass/MPEntryViewController.m b/MacPass/MPEntryViewController.m index dc1532a0..7e490708 100644 --- a/MacPass/MPEntryViewController.m +++ b/MacPass/MPEntryViewController.m @@ -513,6 +513,11 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername"; [document deleteEntry:entry]; } +#pragma mark Validation +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + return YES; +} + - (void)_toggleFilterSpace:(id)sender { NSButton *button = sender; NSNumber *value = self.filterButtonToMode[[button identifier]]; diff --git a/MacPass/MPOutlineViewController.m b/MacPass/MPOutlineViewController.m index 8868d6df..869c7f73 100644 --- a/MacPass/MPOutlineViewController.m +++ b/MacPass/MPOutlineViewController.m @@ -137,6 +137,29 @@ NSString *const _MPOutlinveViewHeaderViewIdentifier = @"HeaderCell"; } } +#pragma mark Validation + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + MPActionType actionType = [MPActionHelper typeForAction:[menuItem action]]; + switch(actionType) { + case MPActionAddEntry: + case MPActionAddGroup: + case MPActionDelete: { + MPDocument *document = [[self windowController] document]; + id selected = [self _clickedOrSelectedGroup]; + if(!selected) { + return NO; + } + if(selected == document.trash) { + return NO; + } + return ![document isItemTrashed:selected]; + } + default: + return YES; // We are only validated for three targets + } +} + #pragma mark - #pragma mark Actions diff --git a/MacPass/MPToolbarDelegate.m b/MacPass/MPToolbarDelegate.m index d8686fbe..1bf08f96 100644 --- a/MacPass/MPToolbarDelegate.m +++ b/MacPass/MPToolbarDelegate.m @@ -17,7 +17,6 @@ NSString *const MPToolbarItemLock = @"TOOLBAR_LOCK"; NSString *const MPToolbarItemAddGroup = @"TOOLBAR_ADD_GROUP"; NSString *const MPToolbarItemAddEntry = @"TOOLBAR_ADD_ENTRY"; -NSString *const MPToolbarItemEdit = @"TOOLBAR_EDIT"; NSString *const MPToolbarItemDelete =@"TOOLBAR_DELETE"; NSString *const MPToolbarItemAction = @"TOOLBAR_ACTION"; NSString *const MPToolbarItemInspector = @"TOOLBAR_INSPECTOR"; @@ -135,7 +134,6 @@ NSString *const MPToolbarItemInspector = @"TOOLBAR_INSPECTOR"; MPToolbarItemAddEntry: NSLocalizedString(@"ADD_ENTRY", @""), MPToolbarItemAddGroup: NSLocalizedString(@"ADD_GROUP", @""), MPToolbarItemDelete: NSLocalizedString(@"DELETE", @""), - MPToolbarItemEdit: NSLocalizedString(@"EDIT", @""), MPToolbarItemInspector: NSLocalizedString(@"INSPECTOR", @"") }; return labelDict[identifier]; @@ -146,7 +144,6 @@ NSString *const MPToolbarItemInspector = @"TOOLBAR_INSPECTOR"; MPToolbarItemAddEntry: @(MPActionAddEntry), MPToolbarItemAddGroup: @(MPActionAddGroup), MPToolbarItemDelete: @(MPActionDelete), - MPToolbarItemEdit: @(MPActionEdit), MPToolbarItemInspector: @(MPActionToggleInspector) }; MPActionType actionType = (MPActionType)[actionDict[identifier] integerValue];