From 07a80857055eadebf1c97bbd6b62353bdaf3ab4d Mon Sep 17 00:00:00 2001 From: michael starke Date: Fri, 17 Nov 2017 17:45:10 +0100 Subject: [PATCH] Extended plugin settings Plugins can be installed via drag and drop Plugins can be uninstalled via the remove button --- MacPass.xcodeproj/project.pbxproj | 6 ++ MacPass/Base.lproj/PluginSettings.xib | 33 ++++++--- MacPass/MPAttachmentTableDataSource.m | 2 +- MacPass/MPConstants.h | 1 + MacPass/MPConstants.m | 7 +- MacPass/MPDocumentWindowController.m | 2 +- MacPass/MPPlugin.h | 2 - MacPass/MPPlugin.m | 14 ++-- MacPass/MPPluginHost.h | 3 + MacPass/MPPluginHost.m | 49 ++++++++++---- MacPass/MPPluginSettingsController.m | 98 ++++++++++++++++++++++++--- MacPass/MPPluginTabelCellView.h | 15 ++++ MacPass/MPPluginTabelCellView.m | 13 ++++ MacPass/MPSettingsHelper.h | 1 + MacPass/MPSettingsHelper.m | 23 ++++++- MacPass/MacPass-Info.plist | 2 +- MacPass/de.lproj/Localizable.strings | 3 + MacPass/en.lproj/Localizable.strings | 73 +++++++++++++------- 18 files changed, 278 insertions(+), 69 deletions(-) create mode 100644 MacPass/MPPluginTabelCellView.h create mode 100644 MacPass/MPPluginTabelCellView.m diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index a62e4263..456ff2bd 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -247,6 +247,7 @@ 4CEED1C617D7BD0E007180F1 /* NSError+Messages.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CEED1C517D7BD0E007180F1 /* NSError+Messages.m */; }; 4CF29BF417879D0000851B60 /* 26_FileSaveTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4CF29BF317879D0000851B60 /* 26_FileSaveTemplate.pdf */; }; 4CF5BE6D1BF33E3000048505 /* NSApplication+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF5BE6C1BF33E3000048505 /* NSApplication+MPAdditions.m */; }; + 4CF6C3021FBF39BF0055AD03 /* MPPluginTabelCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF6C3011FBF39BF0055AD03 /* MPPluginTabelCellView.m */; }; 4CF6C711176F4533007A811D /* MPStringLengthValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF6C710176F4533007A811D /* MPStringLengthValueTransformer.m */; }; 4CF78064176E75AD0032EE71 /* MPIntegrationSettingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF78063176E75AD0032EE71 /* MPIntegrationSettingsController.m */; }; 4CFB18E418A17FA20097A34B /* MPUpdateSettingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CFB18E318A17FA20097A34B /* MPUpdateSettingsController.m */; }; @@ -761,6 +762,8 @@ 4CF29BF317879D0000851B60 /* 26_FileSaveTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 26_FileSaveTemplate.pdf; sourceTree = ""; }; 4CF5BE6B1BF33E3000048505 /* NSApplication+MPAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+MPAdditions.h"; sourceTree = ""; }; 4CF5BE6C1BF33E3000048505 /* NSApplication+MPAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+MPAdditions.m"; sourceTree = ""; }; + 4CF6C3001FBF39BF0055AD03 /* MPPluginTabelCellView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPPluginTabelCellView.h; sourceTree = ""; }; + 4CF6C3011FBF39BF0055AD03 /* MPPluginTabelCellView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPPluginTabelCellView.m; sourceTree = ""; }; 4CF6C70F176F4533007A811D /* MPStringLengthValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStringLengthValueTransformer.h; sourceTree = ""; }; 4CF6C710176F4533007A811D /* MPStringLengthValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStringLengthValueTransformer.m; sourceTree = ""; }; 4CF78062176E75AD0032EE71 /* MPIntegrationSettingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPIntegrationSettingsController.h; sourceTree = ""; }; @@ -936,6 +939,8 @@ 4CCEDE29179F203B008402BE /* MPOutlineView.m */, 4CE082C11F6FCD2A0034FF56 /* MPCollectionView.h */, 4CE082C21F6FCD2A0034FF56 /* MPCollectionView.m */, + 4CF6C3001FBF39BF0055AD03 /* MPPluginTabelCellView.h */, + 4CF6C3011FBF39BF0055AD03 /* MPPluginTabelCellView.m */, ); name = Views; sourceTree = ""; @@ -1867,6 +1872,7 @@ 4C4B728518E4B9B400A1A5D5 /* MPDockTileHelper.m in Sources */, 4C5FE9AE17843CE20001D5A8 /* MPSelectedAttachmentTableCellView.m in Sources */, 4C3666411787327E00B249F1 /* MPDocument+Attachments.m in Sources */, + 4CF6C3021FBF39BF0055AD03 /* MPPluginTabelCellView.m in Sources */, 4C10412C178CDD44001B5239 /* NSDate+Humanized.m in Sources */, 4C0C59F118B17F10009C7B76 /* DDHotKeyUtilities.m in Sources */, 4CEE46DD181C301D006BF1E5 /* MPAutotypeDaemon.m in Sources */, diff --git a/MacPass/Base.lproj/PluginSettings.xib b/MacPass/Base.lproj/PluginSettings.xib index 4f4305ae..a0b3ce4e 100644 --- a/MacPass/Base.lproj/PluginSettings.xib +++ b/MacPass/Base.lproj/PluginSettings.xib @@ -44,13 +44,13 @@ - + - + @@ -70,21 +70,38 @@ - - + + - - - + + + + + + + + + + + + + + + + + + + + @@ -152,7 +169,7 @@ - + diff --git a/MacPass/MPAttachmentTableDataSource.m b/MacPass/MPAttachmentTableDataSource.m index a16fb9f1..14f55821 100644 --- a/MacPass/MPAttachmentTableDataSource.m +++ b/MacPass/MPAttachmentTableDataSource.m @@ -51,7 +51,7 @@ KPKEntry *entry = document.selectedEntries.count == 1 ? document.selectedEntries.lastObject : nil; NSPasteboard *draggingPasteBoard = [info draggingPasteboard]; - NSArray *arrayOfURLs = [draggingPasteBoard readObjectsForClasses:@[[NSURL class]] options:nil]; + NSArray *arrayOfURLs = [draggingPasteBoard readObjectsForClasses:@[NSURL.class] options:nil]; for(NSURL *fileUrl in arrayOfURLs) { [document addAttachment:fileUrl toEntry:entry]; diff --git a/MacPass/MPConstants.h b/MacPass/MPConstants.h index 2270f560..f3ccf058 100644 --- a/MacPass/MPConstants.h +++ b/MacPass/MPConstants.h @@ -28,6 +28,7 @@ FOUNDATION_EXPORT NSString *const MPPasteBoardType; FOUNDATION_EXPORT NSString *const MPKdbDocumentUTI; FOUNDATION_EXPORT NSString *const MPKdbxDocumentUTI; +FOUNDATION_EXPORT NSString *const MPPluginUTI; #endif diff --git a/MacPass/MPConstants.m b/MacPass/MPConstants.m index ab99a031..2d9e3f70 100644 --- a/MacPass/MPConstants.m +++ b/MacPass/MPConstants.m @@ -22,6 +22,7 @@ #import "MPConstants.h" -NSString *const MPPasteBoardType = @"com.hicknhack.macpass.pasteboard"; -NSString *const MPKdbDocumentUTI = @"com.hicknhack.macpass.kdb"; -NSString *const MPKdbxDocumentUTI = @"com.hicknhack.macpass.kdbx"; +NSString *const MPPasteBoardType = @"com.hicknhack.macpass.pasteboard"; +NSString *const MPKdbDocumentUTI = @"com.hicknhack.macpass.kdb"; +NSString *const MPKdbxDocumentUTI = @"com.hicknhack.macpass.kdbx"; +NSString *const MPPluginUTI = @"com.hicknhack.macpass.plugin"; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 30fba062..e3498b21 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -556,7 +556,7 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword); alert.informativeText = NSLocalizedString(@"RECOMMEND_PASSWORD_CHANGE_ALERT_DESCRIPTION", "Informative text for the recommend password change alert"); [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_PASSWORD_WITH_DOTS", "Button to show the password change dialog")]; - [alert addButtonWithTitle:NSLocalizedString(@"CANCEL", "Cancel button to postpone password change")]; + [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_LATER", "Button to postpone the password change")]; alert.buttons[1].keyEquivalent = [NSString stringWithFormat:@"%c", 0x1b]; diff --git a/MacPass/MPPlugin.h b/MacPass/MPPlugin.h index 171e9280..350c40ca 100644 --- a/MacPass/MPPlugin.h +++ b/MacPass/MPPlugin.h @@ -48,8 +48,6 @@ FOUNDATION_EXPORT NSString *const kMPPluginFileExtension; @end -@class KPKTree; - @interface MPPlugin (Deprecated) - (instancetype)initWithPluginManager:(id)manager; diff --git a/MacPass/MPPlugin.m b/MacPass/MPPlugin.m index 8846e601..56d1070b 100644 --- a/MacPass/MPPlugin.m +++ b/MacPass/MPPlugin.m @@ -48,10 +48,16 @@ NSString *const kMPPluginFileExtension = @"mpplugin"; - (NSString *)version { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *version; if(bundle) { - version = bundle.infoDictionary[(NSString *)kCFBundleVersionKey]; - if(version) { + NSString *humanVersion = bundle.infoDictionary[@"CFBundleShortVersionString"]; + NSString *version = bundle.infoDictionary[(NSString *)kCFBundleVersionKey]; + if(humanVersion && version) { + return [NSString stringWithFormat:@"%@ (%@)", humanVersion, version]; + } + else if(humanVersion) { + return humanVersion; + } + else if(version) { return version; } } @@ -71,7 +77,7 @@ NSString *const kMPPluginFileExtension = @"mpplugin"; NSLog(@"Deprecated initalizer. Use initWithPluginHost: instead!"); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - self = [self initWithPluginHost:nil]; + self = [self initWithPluginHost:manager]; #pragma cland diagnostic pop return self; } diff --git a/MacPass/MPPluginHost.h b/MacPass/MPPluginHost.h index fe4f09b7..2611d851 100644 --- a/MacPass/MPPluginHost.h +++ b/MacPass/MPPluginHost.h @@ -39,5 +39,8 @@ FOUNDATION_EXPORT NSString *const MPPluginHostPluginBundleIdentifiyerKey; - (instancetype)init NS_UNAVAILABLE; - (BOOL)installPluginAtURL:(NSURL *)url error:(NSError *__autoreleasing *)error; +- (BOOL)uninstallPlugin:(MPPlugin *)plugin error:(NSError *__autoreleasing *)error; +- (void)disablePlugin:(MPPlugin *)plugin; +- (void)enablePlugin:(MPPlugin *)plugin; @end diff --git a/MacPass/MPPluginHost.m b/MacPass/MPPluginHost.m index d3d1bfda..996c730b 100644 --- a/MacPass/MPPluginHost.m +++ b/MacPass/MPPluginHost.m @@ -37,10 +37,10 @@ NSString *const MPPluginHostDidLoadPlugin = @"comt.hicknhack.macpass.MPPluginHos NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBundleIdentifiyerKey"; -@interface MPPluginHost () - +@interface MPPluginHost () @property (strong) NSMutableArray *mutablePlugins; @property (nonatomic) BOOL loadUnsecurePlugins; +@property (copy) NSArray *disabledPlugins; @end @@ -67,13 +67,18 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun self = [super init]; if(self) { _mutablePlugins = [[NSMutableArray alloc] init]; + _disabledPlugins = [[NSUserDefaults standardUserDefaults] arrayForKey:kMPSettingsKeyLoadUnsecurePlugins]; _loadUnsecurePlugins = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyLoadUnsecurePlugins]; [self _loadPlugins]; [self bind:NSStringFromSelector(@selector(loadUnsecurePlugins)) - toObject:[NSUserDefaultsController sharedUserDefaultsController] + toObject:NSUserDefaultsController.sharedUserDefaultsController withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyLoadUnsecurePlugins] options:nil]; + [self bind:NSStringFromSelector(@selector(disabledPlugins)) + toObject:NSUserDefaultsController.sharedUserDefaultsController + withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyDisabledPlugins] + options:nil]; } return self; } @@ -95,16 +100,21 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun } NSURL *appSupportURL = [NSApp applicationSupportDirectoryURL:YES]; NSURL *destinationURL = [appSupportURL URLByAppendingPathComponent:fileName]; - NSFileManager.defaultManager.delegate = self; return [NSFileManager.defaultManager moveItemAtURL:url toURL:destinationURL error:error]; } -#pragma mark - NSFileManagerDelegate - -- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error movingItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL { - return NO; +- (BOOL)uninstallPlugin:(MPPlugin *)plugin error:(NSError *__autoreleasing *)error { + NSBundle *pluginBundle = [NSBundle bundleForClass:plugin.class]; + return [NSFileManager.defaultManager trashItemAtURL:pluginBundle.bundleURL resultingItemURL:nil error:error]; } +- (void)disablePlugin:(MPPlugin *)plugin { +} + +- (void)enablePlugin:(MPPlugin *)plugin { +} + + #pragma mark - Plugin Loading - (void)_loadPlugins { @@ -146,6 +156,12 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun NSLog(@"Could not create bundle %@", pluginURL.path); continue; } + + if(pluginBundle.bundleIdentifier) { + + } + + NSError *error; if(![pluginBundle preflightAndReturnError:&error]) { NSLog(@"Preflight Error %@ %@", error.localizedDescription, error.localizedFailureReason ); @@ -166,11 +182,19 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun NSLog(@"Wrong principal Class."); continue; } - if([pluginBundle.principalClass instancesRespondToSelector:NSSelectorFromString(@"initWithPluginManager:")]) { - NSLog(@"Plugin uses old interface. Update plugin to use initWithPluginHost: instead of initWithPluginManager:!"); - } - MPPlugin *plugin = [[pluginBundle.principalClass alloc] initWithPluginHost:self]; + IMP defaultImp = [MPPlugin.class instanceMethodForSelector:@selector(initWithPluginManager:)]; + IMP pluginImp = [pluginBundle.principalClass instanceMethodForSelector:@selector(initWithPluginManager:)]; + + MPPlugin *plugin; + if(defaultImp != pluginImp) { + NSLog(@"Plugin uses old interface. Update plugin to use initWithPluginHost: instead of initWithPluginManager:!"); + plugin = [[pluginBundle.principalClass alloc] initWithPluginManager:self]; + } + else { + plugin = [[pluginBundle.principalClass alloc] initWithPluginHost:self]; + } + if(plugin) { NSLog(@"Loaded plugin instance %@", pluginBundle.principalClass); [[NSNotificationCenter defaultCenter] postNotificationName:MPPluginHostWillLoadPlugin @@ -252,5 +276,4 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun return NO; } - @end diff --git a/MacPass/MPPluginSettingsController.m b/MacPass/MPPluginSettingsController.m index af343c99..ed6d3a7f 100644 --- a/MacPass/MPPluginSettingsController.m +++ b/MacPass/MPPluginSettingsController.m @@ -21,9 +21,11 @@ // #import "MPPluginSettingsController.h" +#import "MPPluginTabelCellView.h" #import "MPPluginHost.h" #import "MPPlugin.h" +#import "MPConstants.h" #import "MPSettingsHelper.h" #import "NSApplication+MPAdditions.h" @@ -71,18 +73,20 @@ typedef NS_ENUM(NSUInteger, MPPluginSegmentType) { toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyLoadUnsecurePlugins] options:nil]; - - + [self.pluginTableView registerForDraggedTypes:@[(NSString *)kUTTypeFileURL]]; } +# pragma mark - TableView + - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { return [MPPluginHost sharedHost].plugins.count; } - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { MPPlugin *plugin = [self pluginForRow:row]; - NSTableCellView *view = [tableView makeViewWithIdentifier:tableColumn.identifier owner:nil]; - view.textField.stringValue = plugin.name; + MPPluginTabelCellView *view = [tableView makeViewWithIdentifier:tableColumn.identifier owner:nil]; + view.textField.stringValue = [NSString stringWithFormat:@"%@", plugin.name]; + view.addionalTextField.stringValue = [NSString stringWithFormat:NSLocalizedString(@"PLUGIN_VERSION_%@", "Plugin version. Include a %@ placeholder for version string"), plugin.version]; return view; } @@ -110,11 +114,48 @@ typedef NS_ENUM(NSUInteger, MPPluginSegmentType) { - (void)tableViewSelectionDidChange:(NSNotification *)notification { NSTableView *table = notification.object; + if(table != self.pluginTableView) { + return; // wrong tableview + } MPPlugin *plugin = [self pluginForRow:table.selectedRow]; - //[self.addRemovePluginsControl setEnabled:(nil != plugin) forSegment:MPRemovePluginSegment]; + [self.addRemovePluginsControl setEnabled:(nil != plugin) forSegment:MPRemovePluginSegment]; [self showSettingsForPlugin:plugin]; } +- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation { + NSArray *arrayOfURLs = [[info draggingPasteboard] readObjectsForClasses:@[NSURL.class] options:nil]; + if(arrayOfURLs.count != 1) { + return NO; + } + NSURL *pluginURL = arrayOfURLs.firstObject; + if(!pluginURL.isFileURL) { + return NO; + } + if(![pluginURL.lastPathComponent.pathExtension isEqualToString:kMPPluginFileExtension]) { + return NO; + } + [tableView setDropRow:-1 dropOperation:NSTableViewDropOn]; + return YES; +} + +- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(nonnull id)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation { + /* dispatch installation since we do not want to wait for the result */ + if(dropOperation != NSTableViewDropOn) { + return NO; + } + NSPasteboard *draggingPasteboard = [info draggingPasteboard]; + NSArray *arrayOfURLs = [draggingPasteboard readObjectsForClasses:@[NSURL.class] options:nil]; + if(arrayOfURLs.count != 1) { + return NO; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self _addPlugin:arrayOfURLs.firstObject]; + }); + return YES; +} + +#pragma mark - Actions + - (IBAction)browsePlugins:(id)sender { [NSWorkspace.sharedWorkspace openURL:[NSApp applicationSupportDirectoryURL:YES]]; } @@ -128,6 +169,7 @@ typedef NS_ENUM(NSUInteger, MPPluginSegmentType) { [self showAddPluginPanel]; break; case MPRemovePluginSegment: + [self showRemovePluginAlert]; break; default: break; @@ -162,11 +204,49 @@ typedef NS_ENUM(NSUInteger, MPPluginSegmentType) { NSAlert *alert = [[NSAlert alloc] init]; alert.alertStyle = NSAlertStyleInformational; alert.messageText = NSLocalizedString(@"ALERT_MESSAGE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART", "Alert message text when a plugin was successfully installed"); - alert.informativeText = NSLocalizedString(@"ALERT_INFORMATIVE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART", "ALert informative text when a plugin was sucessfully installed"); - [alert addButtonWithTitle:NSLocalizedString(@"CANCEL", @"Cancel button in plugin installed, request restart alert")]; - [alert addButtonWithTitle:NSLocalizedString(@"RESTART", @"Restart button in plugin installed, request restart alert")]; + alert.informativeText = NSLocalizedString(@"ALERT_INFORMATIVE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART", "Alert informative text when a plugin was sucessfully installed"); + [alert addButtonWithTitle:NSLocalizedString(@"RESTART", @"Restart")]; + [alert addButtonWithTitle:NSLocalizedString(@"KEEP_RUNNING", @"Do not restart MacPass")]; [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) { - if(returnCode == NSAlertSecondButtonReturn) { + if(returnCode == NSAlertFirstButtonReturn) { + [NSApp relaunchAfterDelay:3]; + } + }]; + } +} + +- (void)showRemovePluginAlert { + MPPlugin *plugin = [self pluginForRow:self.pluginTableView.selectedRow]; + if(!plugin) { + return; + } + NSAlert *alert = [[NSAlert alloc] init]; + alert.alertStyle = NSAlertStyleWarning; + alert.messageText = [NSString stringWithFormat:NSLocalizedString(@"ALERT_MESSAGE_TEXT_REALLY_UNINSTALL_PLUGIN_%@", "Alert message text to ask the user if he really want to uninstall the plugin. Include %@ placeholder for plugin name"), plugin.name]; + alert.informativeText = NSLocalizedString(@"ALERT_INFORMATIVE_TEXT_REALLY_UNINSTALL_PLUGIN", "Alert informative text to ask the user if he really want to uninstall the plugin"); + [alert addButtonWithTitle:NSLocalizedString(@"UNINSTALL", @"Uninstall plugin")]; + [alert addButtonWithTitle:NSLocalizedString(@"KEEP_PLUGIN", @"Do not install the plugin")]; + [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) { + if(returnCode == NSAlertFirstButtonReturn) { + [self _removePlugin:plugin]; + } + }]; +} + +- (void)_removePlugin:(MPPlugin *)plugin { + NSError *error; + if(![[MPPluginHost sharedHost] uninstallPlugin:plugin error:&error]) { + [NSApp presentError:error modalForWindow:self.view.window delegate:nil didPresentSelector:NULL contextInfo:NULL]; + } + else { + NSAlert *alert = [[NSAlert alloc] init]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = [NSString stringWithFormat:NSLocalizedString(@"ALERT_MESSAGE_TEXT_PLUGIN_%@_UNINSTALLED_SUGGEST_RESTART", "Alert message text when a plugin was successfully uninstalled. Include %@ placeholder for plugin name"), plugin.name]; + alert.informativeText = NSLocalizedString(@"ALERT_INFORMATIVE_TEXT_PLUGIN_UNINSTALLED_SUGGEST_RESTART", "Alert informative text when a plugin was sucessfully uninstalled"); + [alert addButtonWithTitle:NSLocalizedString(@"RESTART", @"Restart")]; + [alert addButtonWithTitle:NSLocalizedString(@"KEEP_RUNNING", @"Do not restart MacPass")]; + [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) { + if(returnCode == NSAlertFirstButtonReturn) { [NSApp relaunchAfterDelay:3]; } }]; diff --git a/MacPass/MPPluginTabelCellView.h b/MacPass/MPPluginTabelCellView.h new file mode 100644 index 00000000..f653aa49 --- /dev/null +++ b/MacPass/MPPluginTabelCellView.h @@ -0,0 +1,15 @@ +// +// MPPluginTabelCellView.h +// MacPass +// +// Created by Michael Starke on 17.11.17. +// Copyright © 2017 HicknHack Software GmbH. All rights reserved. +// + +#import + +@interface MPPluginTabelCellView : NSTableCellView + +@property (weak) IBOutlet NSTextField *addionalTextField; + +@end diff --git a/MacPass/MPPluginTabelCellView.m b/MacPass/MPPluginTabelCellView.m new file mode 100644 index 00000000..40e63523 --- /dev/null +++ b/MacPass/MPPluginTabelCellView.m @@ -0,0 +1,13 @@ +// +// MPPluginTabelCellView.m +// MacPass +// +// Created by Michael Starke on 17.11.17. +// Copyright © 2017 HicknHack Software GmbH. All rights reserved. +// + +#import "MPPluginTabelCellView.h" + +@implementation MPPluginTabelCellView + +@end diff --git a/MacPass/MPSettingsHelper.h b/MacPass/MPSettingsHelper.h index a6757943..fac2ea77 100644 --- a/MacPass/MPSettingsHelper.h +++ b/MacPass/MPSettingsHelper.h @@ -79,6 +79,7 @@ APPKIT_EXTERN NSString *const kMPSettingsKeyUpdatePasswordOnTemplateEntries; /* Plugins */ APPKIT_EXTERN NSString *const kMPSettingsKeyLoadUnsecurePlugins; // If set to YES this will load all plugins regardless of their codesignature status +APPKIT_EXTERN NSString *const kMPSettingsKeyDisabledPlugins; // NSArray of bundle identifiers of disabled plugins typedef NS_ENUM(NSUInteger, MPFileChangeStrategy) { MPFileChangeStrategyAsk, diff --git a/MacPass/MPSettingsHelper.m b/MacPass/MPSettingsHelper.m index 94488123..6cfef0b7 100644 --- a/MacPass/MPSettingsHelper.m +++ b/MacPass/MPSettingsHelper.m @@ -74,7 +74,8 @@ NSString *const kMPSettingsKeyDoubleClickURLAction = @"Double NSString *const kMPSettingsKeyDoubleClickTitleAction = @"DoubleClickTitleAction"; NSString *const kMPSettingsKeyUpdatePasswordOnTemplateEntries = @"UpdatePasswordOnTemplateEntries"; -NSString *const kMPSettingsKeyLoadUnsecurePlugins = @"MPLoadUnsecurePlugins"; +NSString *const kMPSettingsKeyLoadUnsecurePlugins = @"LoadUnsecurePlugins"; +NSString *const kMPSettingsKeyDisabledPlugins = @"DisabledPlugins"; /* Deprecated */ NSString *const kMPDeprecatedSettingsKeyRememberKeyFilesForDatabases = @"kMPSettingsKeyRememberKeyFilesForDatabases"; @@ -86,6 +87,7 @@ NSString *const kMPDeprecatedSettingsKeyHttpPort = @"Ht NSString *const kMPDeprecatedSettingsKeyEnableHttpServer = @"EnableHttpServer"; NSString *const kMPDeprecatedSettingsKeyShowMenuItem = @"ShowMenuItem"; NSString *const kMPDeprecatedSettingsKeyDefaultPasswordRounds = @"KeyDefaultPasswordRounds"; +NSString *const kMPDepricatedSettingsKeyLoadUnsecurePlugins = @"MPLoadUnsecurePlugins"; @implementation MPSettingsHelper @@ -99,6 +101,7 @@ NSString *const kMPDeprecatedSettingsKeyDefaultPasswordRounds = @"Ke [self _migrateURLDoubleClickPreferences]; [self _migrateEntrySearchFlags]; [self _migrateRememberedKeyFiles]; + [self _migrateLoadUnsecurePlugins]; [self _removeDeprecatedValues]; } @@ -143,7 +146,8 @@ NSString *const kMPDeprecatedSettingsKeyDefaultPasswordRounds = @"Ke kMPSettingsKeyDoubleClickURLAction: @(MPDoubleClickURLActionCopy), kMPSettingsKeyDoubleClickTitleAction: @(MPDoubleClickTitleActionInspect), kMPSettingsKeyLoadUnsecurePlugins: @NO, - kMPSettingsKeyUpdatePasswordOnTemplateEntries: @YES + kMPSettingsKeyUpdatePasswordOnTemplateEntries: @YES, + kMPSettingsKeyDisabledPlugins: @[] }; }); return standardDefaults; @@ -162,7 +166,8 @@ NSString *const kMPDeprecatedSettingsKeyDefaultPasswordRounds = @"Ke /* Moved to KeePassHttp Plugin */ kMPDeprecatedSettingsKeyHttpPort, kMPDeprecatedSettingsKeyEnableHttpServer, - kMPDeprecatedSettingsKeyShowMenuItem + kMPDeprecatedSettingsKeyShowMenuItem, + kMPDepricatedSettingsKeyLoadUnsecurePlugins ]; }); return deprecatedSettings; @@ -251,4 +256,16 @@ NSString *const kMPDeprecatedSettingsKeyDefaultPasswordRounds = @"Ke } } ++ (void)_migrateLoadUnsecurePlugins { + id value = [NSUserDefaults.standardUserDefaults objectForKey:kMPDepricatedSettingsKeyLoadUnsecurePlugins]; + if(!value) { + return; // value already migrated or was set to default value + } + BOOL oldValue = [NSUserDefaults.standardUserDefaults boolForKey:kMPDepricatedSettingsKeyLoadUnsecurePlugins]; + if(oldValue != [[self _standardDefaults][kMPDepricatedSettingsKeyLoadUnsecurePlugins] boolValue]) { + [NSUserDefaults.standardUserDefaults setBool:oldValue forKey:kMPSettingsKeyLoadUnsecurePlugins]; + } + +} + @end diff --git a/MacPass/MacPass-Info.plist b/MacPass/MacPass-Info.plist index 7286153f..1e04a25e 100644 --- a/MacPass/MacPass-Info.plist +++ b/MacPass/MacPass-Info.plist @@ -68,7 +68,7 @@ MPHelpURL https://github.com/mstarke/MacPass NSHumanReadableCopyright - Copyright © 2012-2016 HicknHack Software GmbH. All rights reserved. + Copyright © 2012-2017 HicknHack Software GmbH. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/MacPass/de.lproj/Localizable.strings b/MacPass/de.lproj/Localizable.strings index 33b972ce..68dffa64 100644 --- a/MacPass/de.lproj/Localizable.strings +++ b/MacPass/de.lproj/Localizable.strings @@ -352,6 +352,9 @@ /* Label for plugin settings tab */ "PLUGIN_SETTINGS" = "Plugins"; +/* Plugin version. Include a %@ placeholder for version string */ +"PLUGIN_VERSION_%@" = "Version: %@"; + /* Menu item to preview the selected attached file. */ "PREVIEW" = "Vorschau"; diff --git a/MacPass/en.lproj/Localizable.strings b/MacPass/en.lproj/Localizable.strings index 564777d5..d52e08aa 100644 --- a/MacPass/en.lproj/Localizable.strings +++ b/MacPass/en.lproj/Localizable.strings @@ -19,24 +19,30 @@ /* Button label to abort a merge on a file with changed master key! */ "ABORT_MERGE_KEEP_MINE" = "Abort Merge. Keep Mine."; -/* No comment provided by engineer. */ +/* Toolbar item with action menu */ "ACTION" = "Action"; -/* No comment provided by engineer. */ +/* Action to add an entry via template */ "ADD_TREMPLATE_ENTRY" = "Create Template Entry"; -/* ALert informative text when a plugin was sucessfully installed */ -"ALERT_INFORMATIVE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART" = "Plugins can only be loaded at start up. To activate the installed pluing, please restart MacPass."; +/* Alert informative text when a plugin was sucessfully installed */ +"ALERT_INFORMATIVE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART" = "Plugins can only be loaded at start up. To activate the installed plugin, please restart MacPass."; -/* (No Comment) */ -"ALERT_KDB_UNSUPPORTED_ADD_ENTRY_INFORMATIVE" = "The KDB format does not support entries inside this group. The entry will be moved when the file is saved."; +/* Alert informative text when a plugin was sucessfully uninstalled */ +"ALERT_INFORMATIVE_TEXT_PLUGIN_UNINSTALLED_SUGGEST_RESTART" = "Plugins cannot be removed while MacPass is running. Please restart MacPass to unload the plugin."; -/* (No Comment) */ -"ALERT_KDB_UNSUPPORTED_ADD_ENTRY_MESSAGE" = "Adding entries not supported"; +/* Alert informative text to ask the user if he really want to uninstall the plugin */ +"ALERT_INFORMATIVE_TEXT_REALLY_UNINSTALL_PLUGIN" = "The Plugin will be moved to the Trash."; /* Alert message text when a plugin was successfully installed */ "ALERT_MESSAGE_TEXT_PLUGIN_INSTALLED_SUGGEST_RESTART" = "Sucessfully installed Plugin!"; +/* Alert message text when a plugin was successfully uninstalled. Include %@ placeholder for plugin name */ +"ALERT_MESSAGE_TEXT_PLUGIN_%@_UNINSTALLED_SUGGEST_RESTART" = "Plugin %@ uninstalled!"; + +/* Alert message text to ask the user if he really want to uninstall the plugin. Include %@ placeholder for plugin name */ +"ALERT_MESSAGE_TEXT_REALLY_UNINSTALL_PLUGIN_%@" = "Should the Plugin %@ really be uninstalled?"; + /* Attachments column title (shows counts) Menu item to toggle display of attachment count column in entry table */ "ATTACHMENTS" = "Attachments"; @@ -65,9 +71,7 @@ /* Sucessfully merged external changes */ "AUTO_MERGE_NOTIFICATION_TEXT" = "Auto merge successfull!"; -/* Cancel - Cancel button in plugin installed, request restart alert - Cancel button to postpone password change */ +/* Cancel */ "CANCEL" = "Cancel"; /* Menu item in the database outline context menu to change the database name */ @@ -76,6 +80,9 @@ /* (No Comment) */ "CHANGE_FORMAT" = "Change File Format to KDBX"; +/* Button to postpone the password change */ +"CHANGE_LATER" = "Change Later"; + /* Button to show the password change dialog Single button to show the password change dialog */ "CHANGE_PASSWORD_WITH_DOTS" = "Change Password…"; @@ -89,7 +96,7 @@ /* Clear Autotype Button */ "CLEAR_AUTOTYPE" = "Clear Autotype"; -/* No comment provided by engineer. */ +/* Menu to clear recent searches */ "CLEAR_RECENT_SEARCHES" = "Clear recent searches"; /* Field name that was copied to the pasteboard */ @@ -119,13 +126,15 @@ /* Action title for copying a group via drag and drop */ "COPY_GROUP" = "Copy Group"; -/* Menu item to copy the password of an entry */ +/* Menu item to copy the password of an entry + Toolbar item copy password */ "COPY_PASSWORD" = "Copy Password"; /* Menu item to copy the URL of an entry */ "COPY_URL" = "Copy URL"; -/* Menu item to copy the username of an entry */ +/* Menu item to copy the username of an entry + Toolbar item copy username */ "COPY_USERNAME" = "Copy Username"; /* (No Comment) */ @@ -137,7 +146,7 @@ /* Title for menu for custom search filters */ "CUSTOM_SEARCH_FILTER_MENU" = "Custom Search Filter…"; -/* Default name database */ +/* Default display name for KDB databases */ "DATABASE" = "Database"; /* Default Browser */ @@ -161,7 +170,8 @@ /* Menu item in the database outline context menu to delete the node from the trash Menu item to delete an entry Menu item to delete the selected attached file - Menu item to delete the selected custom icon */ + Menu item to delete the selected custom icon + Toolbar item delete item */ "DELETE" = "Delete"; /* No comment provided by engineer. */ @@ -266,7 +276,7 @@ /* Imports a dragged URL for a new entry */ "IMPORT_URL" = "Import URL"; -/* No comment provided by engineer. */ +/* Toolbar item toggle inspector */ "INSPECTOR" = "Inspector"; /* Label for the integration settings tab */ @@ -284,10 +294,16 @@ /* Reopen the file! */ "KEEP_OTHER_DISCARD_MINE" = "Keep Other, Discard Mine"; +/* Do not install the plugin */ +"KEEP_PLUGIN" = "Keep Plugin"; + +/* Do not restart MacPass */ +"KEEP_RUNNING" = "Keep Running"; + /* last week */ "LAST_WEEK" = "Last week"; -/* No comment provided by engineer. */ +/* Toolbar item to Lock the database */ "LOCK" = "Lock"; /* Message in the open panel to add attachments to an entry */ @@ -312,13 +328,15 @@ /* Name for a newly created Database */ "NEW_DATABASE" = "Database"; -/* Menu item to create a new entry */ +/* Menu item to create a new entry + Toolbar item new entry */ "NEW_ENTRY" = "New Entry"; /* Submenu to add an entry via template */ "NEW_ENTRY_WITH_TEMPLATE_%@" = "Create Entry with Template %@"; -/* Menu item to create a new group */ +/* Menu item to create a new group + Toolbar item new group */ "NEW_GROUP" = "New Group"; /* Placeholder text for input fields if no entry or group is selected */ @@ -401,10 +419,13 @@ /* Label for plugin settings tab */ "PLUGIN_SETTINGS" = "Plugins"; +/* Plugin version. Include a %@ placeholder for version string */ +"PLUGIN_VERSION_%@" = "Version: %@"; + /* Menu item to preview the selected attached file. */ "PREVIEW" = "Preview"; -/* No comment provided by engineer. */ +/* Recent searches menu item */ "RECENT_SEARCHES" = "Recent searches"; /* Informative text for the recommend password change alert */ @@ -413,7 +434,7 @@ /* Message text for the recommend password change alert */ "RECOMMEND_PASSWORD_CHANGE_ALERT_TITLE" = "Please change the database password!"; -/* Restart button in plugin installed, request restart alert */ +/* Restart */ "RESTART" = "Restart"; /* Action to restore and Entry to a previous state of it's history */ @@ -432,7 +453,7 @@ /* Save file menu item title when save will prompt for a location to save or ask for a password/key */ "SAVE_WITH_DOTS" = "Save…"; -/* No comment provided by engineer. */ +/* Search input in Toolbar */ "SEARCH" = "Search"; /* Search option: Find duplicate passwords */ @@ -477,7 +498,8 @@ /* (No Comment) */ "SHORT_FILE_CHANGE_STRATEGY_ASK" = "Ask"; -/* Menu item to show the history of the selected entry */ +/* Menu item to show the history of the selected entry + Toolbar item to toggel history display */ "SHOW_HISTORY" = "Show History"; /* Menu item to show the reference builder in a text view's context menu */ @@ -498,6 +520,9 @@ /* Move Group to Trash */ "TRASH_GROUP" = "Trash Group"; +/* Uninstall plugin */ +"UNINSTALL" = "Uninstall"; + /* No comment provided by engineer. */ "UNKNOWN_FILE_VERSION" = "Unknown File Version";