mirror of
https://github.com/MacPass/MacPass.git
synced 2025-12-13 21:42:32 +00:00
Added rudementary support to add plugins via the plugin settings tab
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -38,4 +38,6 @@ FOUNDATION_EXPORT NSString *const MPPluginHostPluginBundleIdentifiyerKey;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (BOOL)installPluginAtURL:(NSURL *)url error:(NSError *__autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,4 +25,6 @@
|
||||
|
||||
@interface MPPluginSettingsController : MPViewController <MPSettingsTab>
|
||||
|
||||
- (IBAction)addOrRemovePlugin:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,6 +26,7 @@ FOUNDATION_EXPORT NSString *const MPErrorDomain;
|
||||
|
||||
typedef NS_ENUM(NSInteger, MPErrorCodes) {
|
||||
MPErrorNoPasswordOrKeyFile = 10000,
|
||||
MPErrorInvalidPlugin
|
||||
};
|
||||
|
||||
@interface NSError (Messages)
|
||||
|
||||
Reference in New Issue
Block a user