Removed old Code. Added simple plugin system

Signed-off-by: michael starke <michael.starke@hicknhack-software.com>
This commit is contained in:
michael starke
2015-11-12 12:43:04 +01:00
parent a21e027998
commit e8ab0602e1
23 changed files with 426 additions and 185 deletions

View File

@@ -231,6 +231,8 @@
4CEE46DD181C301D006BF1E5 /* MPAutotypeDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */; };
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 */; };
4CF5BEC41BF3461800048505 /* MPPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF5BEC31BF3461800048505 /* MPPlugin.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 */; };
@@ -413,7 +415,6 @@
4C2E382516D1470200037A9D /* MPViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPViewController.m; sourceTree = "<group>"; };
4C31FEB11B57CDDB008E7CE3 /* MPPluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPluginManager.h; sourceTree = "<group>"; };
4C31FEB21B57CDDB008E7CE3 /* MPPluginManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPluginManager.m; sourceTree = "<group>"; };
4C31FEBD1B57CE45008E7CE3 /* MPGenericPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGenericPlugin.h; sourceTree = "<group>"; };
4C32B0E51A1D4436007E12F1 /* KPKFormat+MPUTIDetection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KPKFormat+MPUTIDetection.h"; sourceTree = "<group>"; };
4C32B0E61A1D4436007E12F1 /* KPKFormat+MPUTIDetection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KPKFormat+MPUTIDetection.m"; sourceTree = "<group>"; };
4C3666401787327E00B249F1 /* MPDocument+Attachments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPDocument+Attachments.m"; sourceTree = "<group>"; };
@@ -717,9 +718,12 @@
4CEED1C417D7BD0E007180F1 /* NSError+Messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+Messages.h"; sourceTree = "<group>"; };
4CEED1C517D7BD0E007180F1 /* NSError+Messages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+Messages.m"; sourceTree = "<group>"; };
4CF29BF317879D0000851B60 /* 26_FileSaveTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 26_FileSaveTemplate.pdf; sourceTree = "<group>"; };
4CF5BE6B1BF33E3000048505 /* NSApplication+MPAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+MPAdditions.h"; sourceTree = "<group>"; };
4CF5BE6C1BF33E3000048505 /* NSApplication+MPAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+MPAdditions.m"; sourceTree = "<group>"; };
4CF5BEC21BF3461800048505 /* MPPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPlugin.h; sourceTree = "<group>"; };
4CF5BEC31BF3461800048505 /* MPPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPlugin.m; sourceTree = "<group>"; };
4CF6C70F176F4533007A811D /* MPStringLengthValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStringLengthValueTransformer.h; sourceTree = "<group>"; };
4CF6C710176F4533007A811D /* MPStringLengthValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStringLengthValueTransformer.m; sourceTree = "<group>"; };
4CF6C715176F5183007A811D /* MPServerRequestHandling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPServerRequestHandling.h; sourceTree = "<group>"; };
4CF78062176E75AD0032EE71 /* MPIntegrationSettingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPIntegrationSettingsController.h; sourceTree = "<group>"; };
4CF78063176E75AD0032EE71 /* MPIntegrationSettingsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPIntegrationSettingsController.m; sourceTree = "<group>"; };
4CFB18E218A17FA20097A34B /* MPUpdateSettingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUpdateSettingsController.h; sourceTree = "<group>"; };
@@ -881,6 +885,8 @@
4C104129178CDD26001B5239 /* Categories */ = {
isa = PBXGroup;
children = (
4CF5BE6B1BF33E3000048505 /* NSApplication+MPAdditions.h */,
4CF5BE6C1BF33E3000048505 /* NSApplication+MPAdditions.m */,
4C46B88317063A070046109A /* NSString+MPPasswordCreation.h */,
4C46B88417063A070046109A /* NSString+MPPasswordCreation.m */,
4C10412A178CDD44001B5239 /* NSDate+Humanized.h */,
@@ -944,14 +950,15 @@
name = Helper;
sourceTree = "<group>";
};
4C31FEBC1B57CDE0008E7CE3 /* Plugins */ = {
4C31FEBC1B57CDE0008E7CE3 /* Plugin */ = {
isa = PBXGroup;
children = (
4C31FEB11B57CDDB008E7CE3 /* MPPluginManager.h */,
4C31FEB21B57CDDB008E7CE3 /* MPPluginManager.m */,
4C31FEBD1B57CE45008E7CE3 /* MPGenericPlugin.h */,
4CF5BEC21BF3461800048505 /* MPPlugin.h */,
4CF5BEC31BF3461800048505 /* MPPlugin.m */,
);
name = Plugins;
name = Plugin;
sourceTree = "<group>";
};
4C37A84115B8B47D005EF8EE /* Delegates */ = {
@@ -1231,7 +1238,7 @@
4C77E36C15B84A240093A587 /* MacPass */ = {
isa = PBXGroup;
children = (
4C31FEBC1B57CDE0008E7CE3 /* Plugins */,
4C31FEBC1B57CDE0008E7CE3 /* Plugin */,
4C217D8E17A32BCF00609FAA /* Common */,
4C104129178CDD26001B5239 /* Categories */,
4C89F525182FB4C50069C73C /* Autotype */,
@@ -1347,7 +1354,6 @@
4CA0B30E15BCB70200654E32 /* Protocolls */ = {
isa = PBXGroup;
children = (
4CF6C715176F5183007A811D /* MPServerRequestHandling.h */,
4CA0B30D15BCB6FD00654E32 /* MPSettingsTab.h */,
4C2B0B7419F66F6400E48913 /* MPTargetNodeResolving.h */,
);
@@ -1802,6 +1808,7 @@
buildActionMask = 2147483647;
files = (
4C77E37315B84A240093A587 /* main.m in Sources */,
4CF5BEC41BF3461800048505 /* MPPlugin.m in Sources */,
4CBA2ABA17074C07006D8139 /* MPSettingsHelper.m in Sources */,
4C77E37A15B84A240093A587 /* MPAppDelegate.m in Sources */,
4C37A84015B8B474005EF8EE /* MPOutlineDataSource.m in Sources */,
@@ -1847,6 +1854,7 @@
4C3C4EA618D6FEA100153127 /* TTTJSONTransformer.m in Sources */,
4C89B71019B4B4A300DC0A6A /* MPTreeDelegate.m in Sources */,
4C88C66918D9F8D600F43852 /* MPTemporaryFileStorageCenter.m in Sources */,
4CF5BE6D1BF33E3000048505 /* NSApplication+MPAdditions.m in Sources */,
4CE30ACC1A312B7F0063FCC6 /* MPReferenceBuilderViewController.m in Sources */,
4C6F228C19A4AA700012310C /* MPAutotypeDelay.m in Sources */,
4C3C4EA718D6FEA100153127 /* TTTStringTransformers.m in Sources */,

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9059"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9060"/>
<capability name="box content view" minToolsVersion="7.0"/>
</dependencies>
<objects>
@@ -10,7 +10,6 @@
<connections>
<outlet property="enableGlobalAutotypeCheckBox" destination="tik-Ar-FJg" id="yVA-bM-sSh"/>
<outlet property="enableQuicklookCheckBox" destination="LNw-5t-TS4" id="YiZ-Kk-0dF"/>
<outlet property="enableServerCheckBox" destination="2" id="GTJ-GU-nxB"/>
<outlet property="hotKeyTextField" destination="Kvg-he-3c8" id="c7p-1F-zth"/>
<outlet property="hotkeyWarningTextField" destination="f5q-EW-RHf" id="Z2Q-Sb-HCS"/>
<outlet property="matchHostCheckBox" destination="TiO-ah-BlR" id="MWr-Uh-TRB"/>
@@ -24,34 +23,8 @@
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1">
<rect key="frame" x="0.0" y="0.0" width="400" height="424"/>
<rect key="frame" x="0.0" y="0.0" width="400" height="366"/>
<subviews>
<box autoresizesSubviews="NO" verticalHuggingPriority="500" title="Keepass HTTP" borderType="line" translatesAutoresizingMaskIntoConstraints="NO" id="KbH-0Q-5Tw">
<rect key="frame" x="17" y="350" width="366" height="54"/>
<view key="contentView" id="8nz-mp-KUp">
<rect key="frame" x="1" y="1" width="364" height="38"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button translatesAutoresizingMaskIntoConstraints="NO" id="2">
<rect key="frame" x="16" y="12" width="186" height="18"/>
<animations/>
<buttonCell key="cell" type="check" title="Enable KeePassHttp server" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="truncatingMiddle" state="on" inset="2" id="3">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<animations/>
</view>
<constraints>
<constraint firstItem="2" firstAttribute="leading" secondItem="KbH-0Q-5Tw" secondAttribute="leading" constant="16" id="2Qk-ad-9AX"/>
<constraint firstAttribute="bottom" secondItem="2" secondAttribute="bottom" constant="11" id="W6e-Pj-6qk"/>
<constraint firstItem="2" firstAttribute="top" secondItem="KbH-0Q-5Tw" secondAttribute="top" constant="25" id="nEn-ND-YR7"/>
</constraints>
<animations/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</box>
<box autoresizesSubviews="NO" verticalHuggingPriority="500" title="Autotype" borderType="line" translatesAutoresizingMaskIntoConstraints="NO" id="P9N-HM-wER">
<rect key="frame" x="17" y="115" width="366" height="231"/>
<view key="contentView" id="faU-Ok-HJ3">
@@ -221,15 +194,12 @@
</subviews>
<constraints>
<constraint firstAttribute="width" constant="400" id="19"/>
<constraint firstAttribute="trailing" secondItem="KbH-0Q-5Tw" secondAttribute="trailing" constant="20" symbolic="YES" id="0x4-nr-DuM"/>
<constraint firstItem="P9N-HM-wER" firstAttribute="top" secondItem="1" secondAttribute="top" constant="20" symbolic="YES" id="8fy-q5-VJy"/>
<constraint firstAttribute="bottom" secondItem="VVs-b5-cX9" secondAttribute="bottom" constant="20" symbolic="YES" id="TZB-qe-7eg"/>
<constraint firstItem="VVs-b5-cX9" firstAttribute="top" secondItem="P9N-HM-wER" secondAttribute="bottom" constant="8" id="VCX-JW-cBe"/>
<constraint firstItem="KbH-0Q-5Tw" firstAttribute="leading" secondItem="1" secondAttribute="leading" constant="20" symbolic="YES" id="dln-JS-u45"/>
<constraint firstItem="P9N-HM-wER" firstAttribute="leading" secondItem="KbH-0Q-5Tw" secondAttribute="leading" id="iZD-AQ-EeG"/>
<constraint firstItem="VVs-b5-cX9" firstAttribute="trailing" secondItem="P9N-HM-wER" secondAttribute="trailing" id="k9i-4T-WY0"/>
<constraint firstItem="KbH-0Q-5Tw" firstAttribute="top" secondItem="1" secondAttribute="top" constant="20" symbolic="YES" id="mtr-dI-3QM"/>
<constraint firstItem="P9N-HM-wER" firstAttribute="top" secondItem="KbH-0Q-5Tw" secondAttribute="bottom" constant="8" symbolic="YES" id="oFy-uk-HLJ"/>
<constraint firstItem="P9N-HM-wER" firstAttribute="trailing" secondItem="KbH-0Q-5Tw" secondAttribute="trailing" id="sIZ-ZD-A8K"/>
<constraint firstAttribute="trailing" secondItem="P9N-HM-wER" secondAttribute="trailing" constant="20" id="n5w-Cw-Bbt"/>
<constraint firstItem="P9N-HM-wER" firstAttribute="leading" secondItem="1" secondAttribute="leading" constant="20" id="ulV-xL-ldJ"/>
<constraint firstItem="VVs-b5-cX9" firstAttribute="leading" secondItem="P9N-HM-wER" secondAttribute="leading" id="z4a-9C-78h"/>
</constraints>
<animations/>

View File

@@ -32,6 +32,7 @@ APPKIT_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings;
@property (strong) IBOutlet NSWindow *welcomeWindow;
@property (strong) MPAutotypeDaemon *autotypeDaemon;
@property (weak) IBOutlet NSMenuItem *saveMenuItem;
@property (nonatomic, assign) BOOL isAllowedToStoreKeyFile;
- (IBAction)showPreferences:(id)sender;
@@ -45,7 +46,6 @@ APPKIT_EXTERN NSString *const MPDidChangeStoredKeyFilesSettings;
*/
- (IBAction)clearRememberdKeyFiles:(id)sender;
- (NSString *)applicationName;
- (void)lockAllDocuments;
@end

View File

@@ -29,6 +29,7 @@
#import "MPDocumentWindowController.h"
#import "MPLockDaemon.h"
#import "MPPasswordCreatorViewController.h"
#import "MPPluginManager.h"
#import "MPSettingsHelper.h"
#import "MPSettingsWindowController.h"
#import "MPStringLengthValueTransformer.h"
@@ -41,7 +42,6 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi
@interface MPAppDelegate () {
@private
MPLockDaemon *lockDaemon;
MPDockTileHelper *dockTileHelper;
BOOL _shouldOpenFile; // YES if app was started to open a
}
@@ -153,13 +153,11 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
lockDaemon = [[MPLockDaemon alloc] init];
self.autotypeDaemon = [[MPAutotypeDaemon alloc] init];
//dockTileHelper = [[MPDockTileHelper alloc] init];
}
- (NSString *)applicationName {
return [[NSBundle mainBundle] infoDictionary][@"CFBundleName"];
/* Daemon instanziieren */
[MPLockDaemon defaultDaemon];
[MPAutotypeDaemon defaultDaemon];
/* Load plugins */
[[MPPluginManager sharedManager] loadPlugins];
}
#pragma mark -

View File

@@ -20,6 +20,9 @@
@property (weak) IBOutlet NSPopUpButton *matchSelectionButton;
@property (readonly, strong) DDHotKey *registredHotKey;
+ (instancetype)defaultDaemon;
- (instancetype)init NS_UNAVAILABLE;
- (void)performAutotypeForEntry:(KPKEntry *)entry;
- (IBAction)performAutotypeWithSelectedMatch:(id)sender;
- (IBAction)cancelAutotypeSelection:(id)sender;

View File

@@ -43,7 +43,23 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
#pragma mark -
#pragma mark Lifecylce
static MPAutotypeDaemon *_sharedInstance;
+ (instancetype)defaultDaemon {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[MPAutotypeDaemon alloc] _init];
});
return _sharedInstance;
}
- (instancetype)init {
return nil;
}
- (instancetype)_init {
NSAssert(_sharedInstance == nil, @"Multiple initializations not allowed on singleton");
self = [super init];
if (self) {
_enabled = NO;

View File

@@ -1,28 +0,0 @@
//
// MPGenericPlugin.h
// MacPass
//
// Created by Michael Starke on 16/07/15.
// Copyright (c) 2015 HicknHack Software GmbH. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MPPluginManager
@protocol MPGenericPlugin <NSObject>
@required
@property (readonly) NSString *name;
@property (readonly) NSString *version;
@property (readonly) NSInteger *versionNumber;
- (instancetype)initWithPluginManager:(MPPluginManager *)manager;
@optional
- (void)manager:(MPPluginManager *)manager willAddNode:(KPKNode *)node;
- (void)manager:(MPPluginManager *)manager didAddNode(KPKNode *)node;
- (void)manager:(MPPluginManager *)manager willRemoveNode:(KPKNode *)node;
- (void)manager:(MPPluginManager *)manager didRemoveNode:(KPKNode *)node;
@end

View File

@@ -12,10 +12,6 @@
@class DDHotKeyTextField;
@interface MPIntegrationSettingsController : MPViewController <MPSettingsTab, NSTextFieldDelegate>
/* Keepass HTTP */
@property (weak) IBOutlet NSButton *enableServerCheckBox;
/* Autotype */
@property (weak) IBOutlet NSButton *enableGlobalAutotypeCheckBox;
@property (weak) IBOutlet DDHotKeyTextField *hotKeyTextField;

View File

@@ -40,17 +40,13 @@
- (void)awakeFromNib {
NSUserDefaultsController *defaultsController = [NSUserDefaultsController sharedUserDefaultsController];
NSString *serverKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableHttpServer];
NSString *enableGlobalAutotypeKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableGlobalAutotype];
NSString *quicklookKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyEnableQuicklookPreview];
[self.enableServerCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:serverKeyPath options:nil];
[self.enableServerCheckBox setEnabled:NO];
[self.enableGlobalAutotypeCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:enableGlobalAutotypeKeyPath options:nil];
[self.enableQuicklookCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:quicklookKeyPath options:nil];
[self.hotKeyTextField bind:NSEnabledBinding toObject:defaultsController withKeyPath:enableGlobalAutotypeKeyPath options:nil];
self.hotKeyTextField.delegate = self;
[self.matchTitleCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyAutotypeMatchTitle ] options:nil];
[self.matchURLCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyAutotypeMatchURL] options:nil];
[self.matchHostCheckBox bind:NSValueBinding toObject:defaultsController withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyAutotypeMatchHost] options:nil];

View File

@@ -10,6 +10,7 @@
@interface MPLockDaemon : NSObject
+ (MPLockDaemon *)sharedInstance;
+ (instancetype)defaultDaemon;
+ (instancetype)init NS_UNAVAILABLE;
@end

View File

@@ -22,16 +22,22 @@
@implementation MPLockDaemon
+ (MPLockDaemon *)sharedInstance {
static id sharedInstance;
static MPLockDaemon *_sharedInstance;
+ (instancetype)defaultDaemon {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MPLockDaemon alloc] init];
_sharedInstance = [[MPLockDaemon alloc] _init];
});
return sharedInstance;
return _sharedInstance;
}
- (id)init {
- (instancetype)init {
return nil;
}
- (instancetype)_init {
NSAssert(_sharedInstance == nil, @"Multiple instances of MPLockDaemon not allowed!");
self = [super init];
if (self) {
NSUserDefaultsController *defaultsController = [NSUserDefaultsController sharedUserDefaultsController];
@@ -47,7 +53,6 @@
/* Timer */
[NSEvent removeMonitor:self.eventHandler];
}
- (void)setLockOnSleep:(BOOL)lockOnSleep {

37
MacPass/MPPlugin.h Normal file
View File

@@ -0,0 +1,37 @@
//
// MPPlugin.h
// MacPass
//
// Created by Michael Starke on 11/11/15.
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
//
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@class MPPluginManager;
FOUNDATION_EXPORT NSString *const kMPPluginFileExtension;
@interface MPPlugin : NSObject
@property (copy, readonly) NSString *identifier;
@property (copy, readonly) NSString *name;
@property (copy, readonly) NSString *version;
+ (instancetype)pluginWithBundleURL:(NSURL *)url pluginManager:(MPPluginManager *)manager;
- (instancetype)initWithPluginManager:(MPPluginManager *)manager NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
@protocol MPPluginSettings <NSObject>
@required
@property (strong, readonly) NSViewController *settingsViewController;
@end
NS_ASSUME_NONNULL_END

108
MacPass/MPPlugin.m Normal file
View File

@@ -0,0 +1,108 @@
//
// MPPlugin.m
// MacPass
//
// Created by Michael Starke on 11/11/15.
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
//
#import "MPPlugin.h"
#import "MPPluginManager.h"
NSString *const kMPPluginFileExtension = @"mpplugin";
@implementation MPPlugin
+ (instancetype)pluginWithBundleURL:(NSURL *)url pluginManager:(MPPluginManager *)manager {
if(![self _validURL:url]) {
return nil;
}
NSBundle *pluginBundle = [NSBundle bundleWithURL:url];
if(!pluginBundle) {
return nil;
}
if(![self _validateClass:pluginBundle.principalClass]) {
return nil;
}
return [[pluginBundle.principalClass alloc] initWithPluginManager:manager];
}
- (instancetype)initWithPluginManager:(MPPluginManager *)manager {
self = [super init];
return self;
}
- (NSString *)identifier {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
if(bundle && bundle.bundleIdentifier) {
return bundle.bundleIdentifier;
}
return [NSString stringWithFormat:@"unknown.bundle.identifier"];
}
- (NSString *)name {
NSString *name = [self.identifier componentsSeparatedByString:@"."].lastObject;
return nil == name ? @"Unkown Plugin" : name;
}
- (NSString *)version {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *version;
if(bundle) {
version = bundle.infoDictionary[(NSString *)kCFBundleVersionKey];
if(version) {
return version;
}
}
return @"unknown.version";
}
+ (BOOL)_validURL:(NSURL *)url {
return (NSOrderedSame == [url.pathExtension compare:kMPPluginFileExtension options:NSCaseInsensitiveSearch]);
}
+ (BOOL)_validateClass:(Class)class {
return ([class isSubclassOfClass:[MPPlugin class]]);
}
/* Code by Jedda Wignall<jedda@jedda.me> http://jedda.me/2012/03/verifying-plugin-bundles-using-code-signing/ */
+ (BOOL)_validSignature:(NSURL *)url {
if(!url.path) {
return NO;
}
NSTask * task = [[NSTask alloc] init];
NSPipe * pipe = [NSPipe pipe];
NSArray* args = @[ @"--verify",
/*[NSString stringWithFormat:@"-R=anchor = \"%@\"", [[NSBundle mainBundle] pathForResource:@"BlargsoftCodeCA" ofType:@"cer"]],*/
url.path ];
task.launchPath = @"/usr/bin/codesign";
task.standardOutput = pipe;
task.standardError = pipe;
task.arguments = args;
[task launch];
[task waitUntilExit];
if(task.terminationStatus == 0) {
return YES;
}
NSString * taskString = [[NSString alloc] initWithData:pipe.fileHandleForReading.readDataToEndOfFile encoding:NSASCIIStringEncoding];
if ([taskString rangeOfString:@"modified"].length > 0 || [taskString rangeOfString:@"a sealed resource is missing or invalid"].length > 0) {
// The plugin has been modified or resources removed since being signed. You probably don't want to load this.
NSLog(@"Plugin modified - not loaded"); // log a real error here
}
else if ([taskString rangeOfString:@"failed to satisfy"].length > 0) {
// The plugin is missing resources since being signed. Don't load.
// throw an error
NSLog(@"Plugin not signed by correct CA - not loaded"); // log a real error here
}
else if ([taskString rangeOfString:@"not signed at all"].length > 0) {
// The plugin was not code signed at all. Don't load.
NSLog(@"Plugin not signed at all - don't load."); // log a real error here
}
else {
// Some other codesign error
}
return NO;
}
@end

View File

@@ -9,14 +9,22 @@
#import <Foundation/Foundation.h>
@class KPKNode;
@class MPPlugin;
@interface MPPluginManager : NSObject
@property (readonly, copy) NSArray <MPPlugin __kindof*> *plugins;
typedef BOOL (^NodeMatchBlock)(KPKNode *aNode);
+ (instancetype)sharedManager;
- (instancetype)init NS_UNAVAILABLE;
- (NSArray *)filteredEntriesUsingBlock:(NodeMatchBlock) matchBlock;
- (NSArray *)filteredGroupsUsingBlock:(NodeMatchBlock) matchBlock;
- (void)loadPlugins;
- (void)installPluginAtURL:(NSURL *)url;
@end

View File

@@ -9,9 +9,17 @@
#import "MPPluginManager.h"
#import "MPDocument.h"
#import "MPPlugin.h"
#import "NSApplication+MPAdditions.h"
#import "KeePassKit/KeePassKit.h"
@interface MPPluginManager ()
@property (strong) NSMutableArray<MPPlugin __kindof *> *mutablePlugins;
@end
@implementation MPPluginManager
+ (instancetype)sharedManager {
@@ -29,9 +37,16 @@
- (instancetype)_init {
self = [super init];
if(self) {
_mutablePlugins = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray<MPPlugin *> *)plugins {
return [self.mutablePlugins copy];
}
- (NSArray *)filteredEntriesUsingBlock:(NodeMatchBlock)matchBlock {
NSArray *currentDocuments = [[NSDocumentController sharedDocumentController] documents];
NSMutableArray *entries = [[NSMutableArray alloc] initWithCapacity:200];
@@ -48,8 +63,18 @@
return nil;
}
- (void)_loadPlugins {
- (void)loadPlugins {
NSURL *dir = [NSApp applicationSupportDirectoryURL:YES];
NSError *error;
NSArray *contentURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:dir includingPropertiesForKeys:@[] options:NSDirectoryEnumerationSkipsHiddenFiles error:&error];
if(!contentURLs) {
NSLog(@"Error while trying to locate Plugins: %@", error.localizedDescription);
}
for(NSURL *pluginURL in contentURLs) {
MPPlugin *plugin = [MPPlugin pluginWithBundleURL:pluginURL pluginManager:self];
if(plugin) {
[self.mutablePlugins addObject:plugin];
}
}
}
@end

View File

@@ -7,12 +7,15 @@
//
#import "MPPluginSettingsController.h"
#import "MPPluginManager.h"
#import "MPPlugin.h"
NSString *const _kMPPluginTableNameColumn = @"Name";
NSString *const _kMPPluginTableLoadedColumn = @"Loaded";
@interface MPPluginSettingsController () <NSTableViewDataSource>
@interface MPPluginSettingsController () <NSTableViewDataSource, NSTableViewDelegate>
@property (weak) IBOutlet NSTableView *pluginTableView;
@property (weak) IBOutlet NSView *settingsView;
@end
@@ -36,26 +39,52 @@ NSString *const _kMPPluginTableLoadedColumn = @"Loaded";
- (void)didLoadView {
self.pluginTableView.tableColumns[0].identifier = _kMPPluginTableNameColumn;
self.pluginTableView.tableColumns[1].identifier = _kMPPluginTableLoadedColumn;
self.pluginTableView.tableColumns[0].title = NSLocalizedString(@"PLUGIN_TABLE_NAME_HEADER", "");
self.pluginTableView.tableColumns[1].title = NSLocalizedString(@"PLUGIN_TABLE_LOAD_HEADER", "");
//self.pluginTableView.delegate = self;
self.pluginTableView.delegate = self;
self.pluginTableView.dataSource = self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 2;
return [MPPluginManager sharedManager].plugins.count;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if([tableColumn.identifier isEqualToString:_kMPPluginTableLoadedColumn]) {
return @YES;
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if(![tableColumn.identifier isEqualToString:_kMPPluginTableNameColumn]) {
return nil;
}
else if([tableColumn.identifier isEqualToString:_kMPPluginTableNameColumn]) {
return @"DummyPlugin";
}
return nil;
MPPlugin *plugin = [self pluginForRow:row];
NSTableCellView *view = [tableView makeViewWithIdentifier:@"NameCell" owner:nil];
view.textField.stringValue = plugin.name;
return view;
}
- (void)showSettingsForPlugin:(MPPlugin *)plugin {
/* move old one regardless */
[self.settingsView.subviews.firstObject removeFromSuperview];
if([plugin conformsToProtocol:@protocol(MPPluginSettings)]) {
NSAssert([plugin respondsToSelector:@selector(settingsViewController)], @"Required getter for settings on plugins");
NSViewController *viewController = ((id<MPPluginSettings>)plugin).settingsViewController;
[self.settingsView addSubview:viewController.view];
NSDictionary *dict = @{ @"view" : viewController.view,
@"table" : self.pluginTableView.enclosingScrollView };
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view]-0-|" options:0 metrics:nil views:dict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view]-0-|" options:0 metrics:nil views:dict]];
}
}
- (MPPlugin *)pluginForRow:(NSInteger)row {
NSArray<MPPlugin __kindof *> *plugins = [MPPluginManager sharedManager].plugins;
if(0 > row || row >= plugins.count) {
return nil;
}
return plugins[row];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
NSTableView *table = notification.object;
[self showSettingsForPlugin:[self pluginForRow:table.selectedRow]];
}
@end

View File

@@ -1,30 +0,0 @@
//
// MPServerRequestHandler.h
// MacPass
//
// Created by Michael Starke on 17.06.13.
// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* Protocol for request handling of KeePassHttp request
*/
@protocol MPServerRequestHandling <NSObject>
@required
/**
* A unique identifier for the request handler
*
* @return NSString representing the identifier
*/
- (NSString *)identifier;
/**
* Formulate a response to the request passed in as Dictionary
*
* @param data An NSDictionary containing the parsed JSON request
*/
- (void)respondTo:(NSDictionary *)data;
@end

View File

@@ -20,11 +20,6 @@ APPKIT_EXTERN NSString *const kMPSettingsKeyReopenLastDatabaseOnLaunch;
/* URL handling */
APPKIT_EXTERN NSString *const kMPSettingsKeyBrowserBundleId;
/* Server Settings */
APPKIT_EXTERN NSString *const kMPSettingsKeyHttpPort;
APPKIT_EXTERN NSString *const kMPSettingsKeyEnableHttpServer;
APPKIT_EXTERN NSString *const kMPSettingsKeyShowMenuItem;
/* Autolock */
APPKIT_EXTERN NSString *const kMPSettingsKeyLockOnSleep;
APPKIT_EXTERN NSString *const kMPSettingsKeyIdleLockTimeOut;
@@ -65,6 +60,9 @@ APPKIT_EXTERN NSString *const kMPSettingsKeyEnableQuicklookPreview;
APPKIT_EXTERN NSString *const kMPSettingsKeyDoubleClickURLAction;
APPKIT_EXTERN NSString *const kMPSettingsKeyDoubleClickTitleAction;
/* Plugins */
APPKIT_EXTERN NSString *const kMPSettingsKeyLoadUnsecurePlugins; // If set to YES this will load all plugins regardless of their codesignature status
typedef NS_ENUM(NSUInteger, MPDoubleClickURLAction) {
MPDoubleClickURLActionCopy,
MPDoubleClickURLActionOpen,

View File

@@ -16,9 +16,6 @@ NSString *const kMPSettingsKeyClearPasteboardOnQuit = @"ClearC
NSString *const kMPSettingsKeyBrowserBundleId = @"BrowserBundleId";
NSString *const kMPSettingsKeyOpenEmptyDatabaseOnLaunch = @"OpenEmptyDatabaseOnLaunch";
NSString *const kMPSettingsKeyReopenLastDatabaseOnLaunch = @"ReopenLastDatabaseOnLaunch";
NSString *const kMPSettingsKeyHttpPort = @"HttpPort";
NSString *const kMPSettingsKeyEnableHttpServer = @"EnableHttpServer";
NSString *const kMPSettingsKeyShowMenuItem = @"ShowMenuItem";
NSString *const kMPSettingsKeyLockOnSleep = @"LockOnSleep";
NSString *const kMPSettingsKeyIdleLockTimeOut = @"IdleLockTimeOut";
NSString *const kMPSettingsKeyShowInspector = @"ShowInspector";
@@ -60,12 +57,18 @@ NSString *const kMPSettingsKeyPasswordDefaultsForEntry = @"Passwo
NSString *const kMPSettingsKeyDoubleClickURLAction = @"DoubleClickURLAction";
NSString *const kMPSettingsKeyDoubleClickTitleAction = @"DoubleClickTitleAction";
NSString *const kMPSettingsKeyLoadUnsecurePlugins = @"MPLoadUnsecurePlugins";
/* Deprecated */
NSString *const kMPDeprecatedSettingsKeyRememberKeyFilesForDatabases = @"kMPSettingsKeyRememberKeyFilesForDatabases";
NSString *const kMPDeprecatedSettingsKeyLastDatabasePath = @"MPLastDatabasePath";
NSString *const kMPDeprecatedSettingsKeyDocumentsAutotypeFixNoteWasShown = @"DocumentsAutotypeFixNoteWasShown";
NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"DoubleClickURLToLaunch";
NSString *const kMPDeprecatedSettingsKeyEntrySearchFilterMode = @"EntrySearchFilterMode";
NSString *const kMPDeprecatedSettingsKeyHttpPort = @"HttpPort";
NSString *const kMPDeprecatedSettingsKeyEnableHttpServer = @"EnableHttpServer";
NSString *const kMPDeprecatedSettingsKeyShowMenuItem = @"ShowMenuItem";
@implementation MPSettingsHelper
@@ -94,9 +97,6 @@ NSString *const kMPDeprecatedSettingsKeyEntrySearchFilterMode = @"En
kMPSettingsKeyClearPasteboardOnQuit: @YES,
kMPSettingsKeyOpenEmptyDatabaseOnLaunch: @NO,
kMPSettingsKeyReopenLastDatabaseOnLaunch: @YES,
kMPSettingsKeyHttpPort: @19455,
kMPSettingsKeyEnableHttpServer: @NO,
kMPSettingsKeyShowMenuItem: @YES,
kMPSettingsKeyLockOnSleep: @YES,
kMPSettingsKeyIdleLockTimeOut: @0, // 5 minutes
kMPSettingsKeyLegacyHideNotes: @NO,
@@ -121,7 +121,8 @@ NSString *const kMPDeprecatedSettingsKeyEntrySearchFilterMode = @"En
kMPSettingsKeyPasswordUseCustomString: @NO,
kMPSettingsKeyPasswordCustomString: @"",
kMPSettingsKeyDoubleClickURLAction: @(MPDoubleClickURLActionCopy),
kMPSettingsKeyDoubleClickTitleAction: @(MPDoubleClickTitleActionInspect)
kMPSettingsKeyDoubleClickTitleAction: @(MPDoubleClickTitleActionInspect),
kMPSettingsKeyLoadUnsecurePlugins: @NO
};
});
return standardDefaults;
@@ -135,7 +136,12 @@ NSString *const kMPDeprecatedSettingsKeyEntrySearchFilterMode = @"En
kMPDeprecatedSettingsKeyLastDatabasePath,
kMPDeprecatedSettingsKeyDocumentsAutotypeFixNoteWasShown,
kMPDeprecatedSettingsKeyDoubleClickURLToLaunch,
kMPDeprecatedSettingsKeyEntrySearchFilterMode];
kMPDeprecatedSettingsKeyEntrySearchFilterMode,
/* Moved to KeePassHttp Plugin */
kMPDeprecatedSettingsKeyHttpPort,
kMPDeprecatedSettingsKeyEnableHttpServer,
kMPDeprecatedSettingsKeyShowMenuItem
];
});
return deprecatedSettings;
}

View File

@@ -124,6 +124,25 @@
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>com.apple.package</string>
</array>
<key>UTTypeDescription</key>
<string>MacPass Plugin</string>
<key>UTTypeIdentifier</key>
<string>com.hicknhack.macpass.plugin</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpplugin</string>
<string>mpPlugin</string>
<string>mpPlugIn</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,22 @@
//
// NSApplication+MPAdditions.h
// MacPass
//
// Created by Michael Starke on 10/11/15.
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
//
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSApplication (MPAdditions)
@property (copy, readonly) NSString *applicationName;
@property (copy, readonly, nullable) NSURL *applicationSupportDirectoryURL;
- (NSURL * _Nullable)applicationSupportDirectoryURL:(BOOL)create;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,41 @@
//
// NSApplication+MPAdditions.m
// MacPass
//
// Created by Michael Starke on 10/11/15.
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
//
#import "NSApplication+MPAdditions.h"
@implementation NSApplication (MPAdditions)
- (NSString *)applicationName {
return [[NSBundle mainBundle].infoDictionary[@"CFBundleName"] copy];
}
- (NSURL *)applicationSupportDirectoryURL {
return [self applicationSupportDirectoryURL:NO];
}
- (NSURL *)applicationSupportDirectoryURL:(BOOL)create {
NSError *error;
NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&error];
if(url) {
url = [url URLByAppendingPathComponent:self.applicationName isDirectory:YES];
if(create) {
[[NSFileManager defaultManager] createDirectoryAtURL:url
withIntermediateDirectories:YES
attributes:nil
error:&error];
}
return url;
}
return nil;
}
@end

View File

@@ -1,36 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9059"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9060"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPPluginSettingsController">
<connections>
<outlet property="pluginTableView" destination="Jyk-V9-KXR" id="qnk-aD-CkH"/>
<outlet property="settingsView" destination="RyZ-1L-3WB" id="MZn-vo-eTf"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<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="501" height="190"/>
<rect key="frame" x="0.0" y="0.0" width="501" height="240"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="RyZ-1L-3WB">
<rect key="frame" x="178" y="20" width="303" height="200"/>
<animations/>
</customView>
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wl5-yF-mBn">
<rect key="frame" x="20" y="20" width="461" height="150"/>
<rect key="frame" x="20" y="20" width="150" height="200"/>
<clipView key="contentView" id="0PQ-0m-LPv">
<rect key="frame" x="1" y="23" width="459" height="126"/>
<rect key="frame" x="1" y="1" width="148" height="198"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" headerView="bJb-fm-Cos" id="Jyk-V9-KXR">
<rect key="frame" x="0.0" y="0.0" width="459" height="126"/>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="Jyk-V9-KXR">
<rect key="frame" x="0.0" y="0.0" width="148" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="116" minWidth="40" maxWidth="1000" id="1A6-Dn-d1p">
<tableColumn width="145" minWidth="40" maxWidth="1000" id="1A6-Dn-d1p">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -42,18 +47,27 @@
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="337" minWidth="40" maxWidth="1000" id="byN-fd-cNd">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<buttonCell key="dataCell" type="check" title="CheckCell" bezelStyle="regularSquare" imagePosition="left" inset="2" id="ikg-5n-oR5">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="NameCell" id="ksr-nD-o30">
<rect key="frame" x="1" y="1" width="145" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lcQ-hj-Tcp">
<rect key="frame" x="0.0" y="0.0" width="100" height="17"/>
<animations/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="uLo-aP-Zyb">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<animations/>
<connections>
<outlet property="textField" destination="lcQ-hj-Tcp" id="n9m-kx-gWy"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
</tableView>
@@ -62,7 +76,8 @@
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="150" id="ah8-R5-Kek"/>
<constraint firstAttribute="width" constant="150" id="7Wq-9K-BgW"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="200" id="Zhj-a1-fc5"/>
</constraints>
<animations/>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="C0h-Cx-fXX">
@@ -75,21 +90,19 @@
<autoresizingMask key="autoresizingMask"/>
<animations/>
</scroller>
<tableHeaderView key="headerView" id="bJb-fm-Cos">
<rect key="frame" x="0.0" y="0.0" width="459" height="23"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</tableHeaderView>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="RyZ-1L-3WB" firstAttribute="leading" secondItem="Jyk-V9-KXR" secondAttribute="trailing" constant="9" id="0fX-KU-NhZ"/>
<constraint firstAttribute="trailing" secondItem="RyZ-1L-3WB" secondAttribute="trailing" constant="20" id="3UR-Be-L2K"/>
<constraint firstAttribute="bottom" secondItem="wl5-yF-mBn" secondAttribute="bottom" constant="20" id="7Wl-rl-S6F"/>
<constraint firstItem="wl5-yF-mBn" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="HFD-Sm-3aB"/>
<constraint firstAttribute="trailing" secondItem="wl5-yF-mBn" secondAttribute="trailing" constant="20" id="LFi-lL-sF8"/>
<constraint firstAttribute="bottom" secondItem="wl5-yF-mBn" secondAttribute="bottom" constant="20" id="YcG-jd-aNi"/>
<constraint firstItem="RyZ-1L-3WB" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="Y0h-Pg-tZ3"/>
<constraint firstItem="wl5-yF-mBn" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" id="eqA-Pl-0j5"/>
<constraint firstAttribute="bottom" secondItem="RyZ-1L-3WB" secondAttribute="bottom" constant="20" id="rkB-Iz-nN5"/>
</constraints>
<animations/>
<point key="canvasLocation" x="382.5" y="420.5"/>
<point key="canvasLocation" x="-52.5" y="175"/>
</customView>
</objects>
</document>