Added rudementary support to add plugins via the plugin settings tab

This commit is contained in:
michael starke
2017-11-16 17:34:03 +01:00
parent 95659e6121
commit 29a6c39c1f
8 changed files with 144 additions and 18 deletions

View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
<capability name="box content view" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPPluginSettingsController">
<connections>
<outlet property="addRemovePluginsControl" destination="B9Q-hq-K4N" id="Oqj-Ko-8UR"/>
<outlet property="loadInsecurePlugsinCheckButton" destination="CqP-oK-S8k" id="YET-o6-7Cc"/>
<outlet property="pluginTableView" destination="Ocu-C0-03d" id="jbH-qr-bVT"/>
<outlet property="settingsView" destination="tD5-Na-7XI" id="Pa0-Tt-20U"/>
@@ -18,24 +19,24 @@
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="520" height="434"/>
<rect key="frame" x="0.0" y="0.0" width="520" height="473"/>
<subviews>
<box borderType="line" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="vBs-Ga-aq0">
<rect key="frame" x="175" y="16" width="328" height="342"/>
<rect key="frame" x="175" y="46" width="328" height="351"/>
<view key="contentView" id="tD5-Na-7XI">
<rect key="frame" x="1" y="1" width="326" height="340"/>
<rect key="frame" x="1" y="1" width="326" height="349"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</box>
<button translatesAutoresizingMaskIntoConstraints="NO" id="CqP-oK-S8k">
<rect key="frame" x="18" y="398" width="159" height="18"/>
<rect key="frame" x="18" y="437" width="159" height="18"/>
<buttonCell key="cell" type="check" title="Load unsecure Plugins" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="C4B-6z-ZqX">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aoG-FD-ds8">
<rect key="frame" x="18" y="364" width="484" height="28"/>
<rect key="frame" x="18" y="403" width="484" height="28"/>
<textFieldCell key="cell" controlSize="small" sendsActionOnEndEditing="YES" id="2bX-8S-9XM">
<font key="font" metaFont="smallSystem"/>
<string key="title">If enabled, only properly signed Plugins will be loaded. Keep in mind, that Plugins have full access to your data! Changes take affect on restart.</string>
@@ -44,13 +45,13 @@
</textFieldCell>
</textField>
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fCk-fL-jU8">
<rect key="frame" x="20" y="20" width="150" height="336"/>
<rect key="frame" x="20" y="50" width="150" height="345"/>
<clipView key="contentView" id="lTL-Q2-k45">
<rect key="frame" x="1" y="1" width="148" height="334"/>
<rect key="frame" x="1" y="1" width="148" height="343"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="Ocu-C0-03d">
<rect key="frame" x="0.0" y="0.0" width="148" height="334"/>
<rect key="frame" x="0.0" y="0.0" width="148" height="343"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -106,23 +107,42 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="B9Q-hq-K4N">
<rect key="frame" x="20" y="18" width="67" height="25"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedSquare" trackingMode="momentary" id="cj3-R6-g1E">
<font key="font" metaFont="system"/>
<segments>
<segment image="NSAddTemplate" width="32"/>
<segment image="NSRemoveTemplate" width="32" tag="1"/>
</segments>
</segmentedCell>
<connections>
<action selector="addOrRemovePlugin:" target="-2" id="ywK-Vi-MR4"/>
</connections>
</segmentedControl>
</subviews>
<constraints>
<constraint firstItem="CqP-oK-S8k" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="1Rj-zS-7t2"/>
<constraint firstItem="vBs-Ga-aq0" firstAttribute="top" secondItem="fCk-fL-jU8" secondAttribute="top" id="2h6-C9-4N5"/>
<constraint firstItem="B9Q-hq-K4N" firstAttribute="top" secondItem="fCk-fL-jU8" secondAttribute="bottom" constant="8" symbolic="YES" id="3vA-Oh-cFO"/>
<constraint firstAttribute="bottom" secondItem="B9Q-hq-K4N" secondAttribute="bottom" constant="20" symbolic="YES" id="7HD-ji-Whc"/>
<constraint firstAttribute="trailing" secondItem="aoG-FD-ds8" secondAttribute="trailing" constant="20" symbolic="YES" id="95O-Jh-0KG"/>
<constraint firstItem="vBs-Ga-aq0" firstAttribute="bottom" secondItem="fCk-fL-jU8" secondAttribute="bottom" id="BQ2-Wp-Fsh"/>
<constraint firstAttribute="trailing" secondItem="vBs-Ga-aq0" secondAttribute="trailing" constant="20" id="LUb-Un-azV"/>
<constraint firstItem="aoG-FD-ds8" firstAttribute="leading" secondItem="CqP-oK-S8k" secondAttribute="leading" id="NcW-ya-DPx"/>
<constraint firstItem="B9Q-hq-K4N" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" symbolic="YES" id="Rtj-Ad-zkg"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="CqP-oK-S8k" secondAttribute="trailing" constant="20" symbolic="YES" id="TXL-mf-nxu"/>
<constraint firstAttribute="bottom" secondItem="fCk-fL-jU8" secondAttribute="bottom" constant="20" id="aeb-kZ-RSU"/>
<constraint firstAttribute="bottom" secondItem="vBs-Ga-aq0" secondAttribute="bottom" constant="20" id="czn-HC-o7k"/>
<constraint firstItem="fCk-fL-jU8" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" id="fzW-4b-L8S"/>
<constraint firstItem="CqP-oK-S8k" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" id="rN1-3Z-BBi"/>
<constraint firstItem="fCk-fL-jU8" firstAttribute="top" secondItem="aoG-FD-ds8" secondAttribute="bottom" constant="8" id="vl9-MY-WW1"/>
<constraint firstItem="vBs-Ga-aq0" firstAttribute="leading" secondItem="fCk-fL-jU8" secondAttribute="trailing" constant="8" id="xNu-Sj-xQO"/>
<constraint firstItem="aoG-FD-ds8" firstAttribute="top" secondItem="CqP-oK-S8k" secondAttribute="bottom" constant="8" symbolic="YES" id="zSW-h3-BrT"/>
</constraints>
<point key="canvasLocation" x="108" y="118"/>
<point key="canvasLocation" x="-214" y="11"/>
</customView>
</objects>
<resources>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
</resources>
</document>

View File

@@ -38,4 +38,6 @@ FOUNDATION_EXPORT NSString *const MPPluginHostPluginBundleIdentifiyerKey;
- (instancetype)init NS_UNAVAILABLE;
- (BOOL)installPluginAtURL:(NSURL *)url error:(NSError *__autoreleasing *)error;
@end

View File

@@ -26,6 +26,8 @@
#import "NSApplication+MPAdditions.h"
#import "MPSettingsHelper.h"
#import "NSError+Messages.h"
#import "KeePassKit/KeePassKit.h"
@@ -35,7 +37,7 @@ NSString *const MPPluginHostDidLoadPlugin = @"comt.hicknhack.macpass.MPPluginHos
NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBundleIdentifiyerKey";
@interface MPPluginHost ()
@interface MPPluginHost () <NSFileManagerDelegate>
@property (strong) NSMutableArray<MPPlugin __kindof *> *mutablePlugins;
@property (nonatomic) BOOL loadUnsecurePlugins;
@@ -80,6 +82,31 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun
return [self.mutablePlugins copy];
}
- (BOOL)installPluginAtURL:(NSURL *)url error:(NSError *__autoreleasing *)error {
if(![self _validURL:url]) {
if(error) {
*error = [NSError errorWithCode:MPErrorInvalidPlugin description:NSLocalizedString(@"ERROR_INVALID_PLUGIN", @"Error description given when adding an invalid plugin")];
}
return NO;
}
NSString *fileName;
if(![url getResourceValue:&fileName forKey:NSURLNameKey error:error]) {
return NO;
}
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;
}
#pragma mark - Plugin Loading
- (void)_loadPlugins {
NSURL *appSupportDir = [NSApp applicationSupportDirectoryURL:YES];
NSError *error;
@@ -131,7 +158,7 @@ NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBun
}
if(![pluginBundle loadAndReturnError:&error]) {
NSLog(@"Bunlde Loading Error %@ %@", error.localizedDescription, error.localizedFailureReason);
NSLog(@"Bundle Loading Error %@ %@", error.localizedDescription, error.localizedFailureReason);
continue;
}

View File

@@ -25,4 +25,6 @@
@interface MPPluginSettingsController : MPViewController <MPSettingsTab>
- (IBAction)addOrRemovePlugin:(id)sender;
@end

View File

@@ -26,11 +26,21 @@
#import "MPSettingsHelper.h"
#import "NSApplication+MPAdditions.h"
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, MPPluginSegmentType) {
MPAddPluginSegment = 0,
MPRemovePluginSegment = 1
};
@interface MPPluginSettingsController () <NSTableViewDataSource, NSTableViewDelegate>
@property (weak) IBOutlet NSTableView *pluginTableView;
@property (weak) IBOutlet NSView *settingsView;
@property (weak) IBOutlet NSButton *loadInsecurePlugsinCheckButton;
@property (weak) IBOutlet NSSegmentedControl *addRemovePluginsControl;
@end
@@ -55,12 +65,14 @@
- (void)viewDidLoad {
self.pluginTableView.delegate = self;
self.pluginTableView.dataSource = self;
[self.addRemovePluginsControl setEnabled:NO forSegment:MPRemovePluginSegment];
[self.loadInsecurePlugsinCheckButton bind:NSValueBinding
toObject:[NSUserDefaultsController sharedUserDefaultsController]
withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyLoadUnsecurePlugins]
options:nil];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
@@ -98,7 +110,61 @@
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *table = notification.object;
[self showSettingsForPlugin:[self pluginForRow:table.selectedRow]];
MPPlugin *plugin = [self pluginForRow:table.selectedRow];
[self.addRemovePluginsControl setEnabled:(nil != plugin) forSegment:MPRemovePluginSegment];
[self showSettingsForPlugin:plugin];
}
- (IBAction)addOrRemovePlugin:(id)sender {
if(sender != self.addRemovePluginsControl) {
return;
}
switch(self.addRemovePluginsControl.selectedSegment) {
case MPAddPluginSegment:
[self showAddPluginPanel];
break;
case MPRemovePluginSegment:
break;
default:
break;
}
}
- (void)showAddPluginPanel {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.allowedFileTypes = @[kMPPluginFileExtension];
openPanel.allowsMultipleSelection = NO;
openPanel.canChooseFiles = YES;
openPanel.canChooseDirectories = NO;
[openPanel beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse result) {
if(NSModalResponseOK) {
if(openPanel.URLs.count == 1) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self _addPlugin:openPanel.URLs.firstObject];
});
}
}
}];
}
- (void)_addPlugin:(NSURL *)bundleURL {
NSError *error;
if(![[MPPluginHost sharedHost] installPluginAtURL:bundleURL 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 = 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 beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) {
if(returnCode == NSAlertSecondButtonReturn) {
[NSApp relaunchAfterDelay:3];
}
}];
}
}
@end

View File

@@ -29,8 +29,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (copy, readonly) NSString *applicationName;
@property (copy, readonly, nullable) NSURL *applicationSupportDirectoryURL;
- (NSURL * _Nullable)applicationSupportDirectoryURL:(BOOL)create;
- (NSURL *_Nullable)applicationSupportDirectoryURL:(BOOL)create;
- (void)relaunchAfterDelay:(CGFloat)seconds;
@end
NS_ASSUME_NONNULL_END

View File

@@ -52,4 +52,12 @@
return nil;
}
- (void)relaunchAfterDelay:(CGFloat)seconds {
NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/bin/sh";
task.arguments = @[ @"-c", [NSString stringWithFormat:@"sleep %f; open \"%@\"", seconds, NSBundle.mainBundle.bundlePath] ];
[task launch];
[self terminate:nil];
}
@end

View File

@@ -26,6 +26,7 @@ FOUNDATION_EXPORT NSString *const MPErrorDomain;
typedef NS_ENUM(NSInteger, MPErrorCodes) {
MPErrorNoPasswordOrKeyFile = 10000,
MPErrorInvalidPlugin
};
@interface NSError (Messages)