Re-organize the project into a better hierarchy.
This commit is contained in:
121
platform-ios/ObjC/Mac/MPAppsViewController.xib
Normal file
121
platform-ios/ObjC/Mac/MPAppsViewController.xib
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">1070</int>
|
||||
<string key="IBDocument.SystemVersion">11C42</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">1938</string>
|
||||
<string key="IBDocument.AppKitVersion">1138.17</string>
|
||||
<string key="IBDocument.HIToolboxVersion">567.00</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="NS.object.0">1938</string>
|
||||
</object>
|
||||
<array key="IBDocument.IntegratedClassDependencies">
|
||||
<string>NSWindowTemplate</string>
|
||||
<string>NSView</string>
|
||||
<string>NSCustomObject</string>
|
||||
</array>
|
||||
<array key="IBDocument.PluginDependencies">
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</array>
|
||||
<object class="NSMutableDictionary" key="IBDocument.Metadata">
|
||||
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
|
||||
<integer value="1" key="NS.object.0"/>
|
||||
</object>
|
||||
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
|
||||
<object class="NSCustomObject" id="1001">
|
||||
<string key="NSClassName">NSObject</string>
|
||||
</object>
|
||||
<object class="NSCustomObject" id="1003">
|
||||
<string key="NSClassName">FirstResponder</string>
|
||||
</object>
|
||||
<object class="NSCustomObject" id="1004">
|
||||
<string key="NSClassName">NSApplication</string>
|
||||
</object>
|
||||
<object class="NSWindowTemplate" id="1005">
|
||||
<int key="NSWindowStyleMask">15</int>
|
||||
<int key="NSWindowBacking">2</int>
|
||||
<string key="NSWindowRect">{{196, 240}, {480, 270}}</string>
|
||||
<int key="NSWTFlags">544735232</int>
|
||||
<string key="NSWindowTitle">Window</string>
|
||||
<string key="NSWindowClass">NSWindow</string>
|
||||
<nil key="NSViewClass"/>
|
||||
<nil key="NSUserInterfaceItemIdentifier"/>
|
||||
<object class="NSView" key="NSWindowView" id="1006">
|
||||
<reference key="NSNextResponder"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrameSize">{480, 270}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
</object>
|
||||
<string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
|
||||
<string key="NSMaxSize">{10000000000000, 10000000000000}</string>
|
||||
<bool key="NSWindowIsRestorable">YES</bool>
|
||||
</object>
|
||||
</array>
|
||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||
<array class="NSMutableArray" key="connectionRecords"/>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<array key="orderedObjects">
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">0</int>
|
||||
<array key="object" id="0"/>
|
||||
<reference key="children" ref="1000"/>
|
||||
<nil key="parent"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-2</int>
|
||||
<reference key="object" ref="1001"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">File's Owner</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-1</int>
|
||||
<reference key="object" ref="1003"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">First Responder</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-3</int>
|
||||
<reference key="object" ref="1004"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">Application</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">1</int>
|
||||
<reference key="object" ref="1005"/>
|
||||
<array class="NSMutableArray" key="children">
|
||||
<reference ref="1006"/>
|
||||
</array>
|
||||
<reference key="parent" ref="0"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">2</int>
|
||||
<reference key="object" ref="1006"/>
|
||||
<reference key="parent" ref="1005"/>
|
||||
</object>
|
||||
</array>
|
||||
</object>
|
||||
<dictionary class="NSMutableDictionary" key="flattenedProperties">
|
||||
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="1.IBWindowTemplateEditedContentRect">{{357, 418}, {480, 270}}</string>
|
||||
<integer value="1" key="1.NSWindowTemplate.visibleAtLaunch"/>
|
||||
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</dictionary>
|
||||
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
|
||||
<nil key="activeLocalization"/>
|
||||
<dictionary class="NSMutableDictionary" key="localizations"/>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">2</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes"/>
|
||||
<int key="IBDocument.localizationMode">0</int>
|
||||
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||
<bool key="IBDocument.UseAutolayout">YES</bool>
|
||||
</data>
|
||||
</archive>
|
16
platform-ios/ObjC/Mac/MPGradientView.h
Normal file
16
platform-ios/ObjC/Mac/MPGradientView.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by Maarten Billemont on 2016-05-15.
|
||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
IB_DESIGNABLE
|
||||
@interface MPGradientView : NSView
|
||||
|
||||
@property(nonatomic, retain) IBInspectable NSColor *startingColor;
|
||||
@property(nonatomic, retain) IBInspectable NSColor *endingColor;
|
||||
@property(nonatomic, assign) IBInspectable NSInteger angle;
|
||||
@property(nonatomic, assign) IBInspectable CGFloat ratio;
|
||||
|
||||
@end
|
81
platform-ios/ObjC/Mac/MPGradientView.m
Normal file
81
platform-ios/ObjC/Mac/MPGradientView.m
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Created by Maarten Billemont on 2016-05-15.
|
||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGradientView.h"
|
||||
|
||||
@implementation MPGradientView {
|
||||
NSGradient *gradient;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame {
|
||||
|
||||
if (!(self = [super initWithFrame:frame]))
|
||||
return nil;
|
||||
|
||||
[self defaults];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
|
||||
if (!(self = [super initWithCoder:coder]))
|
||||
return nil;
|
||||
|
||||
[self defaults];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)defaults {
|
||||
|
||||
self.startingColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.0];
|
||||
self.endingColor = [NSColor colorWithCalibratedWhite:0.0 alpha:1.0];
|
||||
self.angle = 270;
|
||||
self.ratio = 0.5f;
|
||||
}
|
||||
|
||||
- (void)setStartingColor:(NSColor *)startingColor {
|
||||
|
||||
_startingColor = startingColor;
|
||||
gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setEndingColor:(NSColor *)endingColor {
|
||||
|
||||
_endingColor = endingColor;
|
||||
gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setAngle:(NSInteger)angle {
|
||||
|
||||
_angle = angle;
|
||||
gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setRatio:(CGFloat)ratio {
|
||||
|
||||
_ratio = ratio;
|
||||
gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
|
||||
if (!self.startingColor || !self.endingColor || [self.startingColor isEqual:self.endingColor]) {
|
||||
[(self.startingColor?: self.endingColor) set];
|
||||
NSRectFill( dirtyRect );
|
||||
return;
|
||||
}
|
||||
|
||||
[(gradient?: (gradient = [[NSGradient alloc] initWithColorsAndLocations:
|
||||
self.startingColor, (CGFloat)0.f,
|
||||
[self.startingColor blendedColorWithFraction:0.5f ofColor:self.endingColor], self.ratio,
|
||||
self.endingColor, (CGFloat)1.f, nil]))
|
||||
drawInRect:self.bounds angle:self.angle];
|
||||
}
|
||||
|
||||
@end
|
142
platform-ios/ObjC/Mac/MPInitialWindow.xib
Normal file
142
platform-ios/ObjC/Mac/MPInitialWindow.xib
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="MPInitialWindowController">
|
||||
<connections>
|
||||
<outlet property="openAtLoginButton" destination="508" id="571"/>
|
||||
<outlet property="window" destination="1" id="ngh-EA-JYN"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Welcome to Master Password" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" animationBehavior="default" id="1">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" moveToActiveSpace="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="1000" height="640"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<view key="contentView" id="2" userLabel="Container">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="640"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<customView wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="369">
|
||||
<rect key="frame" x="0.0" y="40" width="1000" height="600"/>
|
||||
<subviews>
|
||||
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="3" userLabel="Image">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="600"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="shot-laptop-leaning-iphone" id="4"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="268">
|
||||
<rect key="frame" x="18" y="502" width="964" height="78"/>
|
||||
<shadow key="shadow" blurRadius="1">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="269">
|
||||
<font key="font" metaFont="system" size="32"/>
|
||||
<string key="title">Access your passwords
|
||||
from anywhere.</string>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="283">
|
||||
<rect key="frame" x="440" y="72" width="121" height="29"/>
|
||||
<shadow key="shadow" blurRadius="1">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Get Mobile" id="284">
|
||||
<font key="font" size="24" name="HelveticaNeue-Light"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="173" userLabel="Store">
|
||||
<rect key="frame" x="342" y="20" width="150" height="44"/>
|
||||
<shadow key="shadow" blurRadius="1">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="appstore" imagePosition="overlaps" alignment="left" inset="2" id="174">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="iphoneAppStore:" target="-2" id="206"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="C4d-ih-ARL" userLabel="Store">
|
||||
<rect key="frame" x="508" y="20" width="150" height="44"/>
|
||||
<shadow key="shadow" blurRadius="1">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="appstore" imagePosition="overlaps" alignment="left" inset="2" id="uzk-gu-gWP">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="androidPlayStore:" target="-2" id="EDO-LD-StH"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3" firstAttribute="bottom" secondItem="369" secondAttribute="bottom" id="378"/>
|
||||
<constraint firstItem="3" firstAttribute="trailing" secondItem="369" secondAttribute="trailing" id="379"/>
|
||||
<constraint firstItem="3" firstAttribute="top" secondItem="369" secondAttribute="top" id="380"/>
|
||||
<constraint firstItem="3" firstAttribute="leading" secondItem="369" secondAttribute="leading" id="381"/>
|
||||
<constraint firstItem="268" firstAttribute="leading" secondItem="369" secondAttribute="leading" constant="20" symbolic="YES" id="385"/>
|
||||
<constraint firstAttribute="trailing" secondItem="268" secondAttribute="trailing" constant="20" symbolic="YES" id="387"/>
|
||||
<constraint firstItem="268" firstAttribute="top" secondItem="369" secondAttribute="top" constant="20" symbolic="YES" id="393"/>
|
||||
<constraint firstAttribute="bottom" secondItem="173" secondAttribute="bottom" constant="20" symbolic="YES" id="395"/>
|
||||
<constraint firstItem="173" firstAttribute="trailing" secondItem="283" secondAttribute="centerX" constant="-8" id="542"/>
|
||||
<constraint firstItem="173" firstAttribute="top" secondItem="283" secondAttribute="bottom" constant="8" symbolic="YES" id="543"/>
|
||||
<constraint firstItem="283" firstAttribute="centerX" secondItem="268" secondAttribute="centerX" id="544"/>
|
||||
<constraint firstItem="C4d-ih-ARL" firstAttribute="leading" secondItem="283" secondAttribute="centerX" constant="8" id="TZ0-Ud-fcb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="C4d-ih-ARL" secondAttribute="bottom" constant="20" symbolic="YES" id="pIA-8D-d57"/>
|
||||
<constraint firstItem="C4d-ih-ARL" firstAttribute="top" secondItem="283" secondAttribute="bottom" constant="8" symbolic="YES" id="tqS-gV-jpd"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
<button horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="508">
|
||||
<rect key="frame" x="770" y="8" width="210" height="25"/>
|
||||
<buttonCell key="cell" type="roundTextured" title="Launch After Computer Restarts" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="510">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="-2" id="574"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="562">
|
||||
<rect key="frame" x="21" y="13" width="743" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="To begin: hold command, control and P (⌘⌃P) or access the ●●●| menu icon next to your clock." id="563">
|
||||
<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>
|
||||
<constraints>
|
||||
<constraint firstItem="369" firstAttribute="top" secondItem="2" secondAttribute="top" id="374"/>
|
||||
<constraint firstItem="369" firstAttribute="leading" secondItem="2" secondAttribute="leading" id="375"/>
|
||||
<constraint firstItem="508" firstAttribute="top" secondItem="369" secondAttribute="bottom" constant="8" symbolic="YES" id="514"/>
|
||||
<constraint firstAttribute="bottom" secondItem="508" secondAttribute="bottom" constant="10" id="545"/>
|
||||
<constraint firstItem="562" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="23" id="565"/>
|
||||
<constraint firstItem="369" firstAttribute="trailing" secondItem="2" secondAttribute="trailing" id="567"/>
|
||||
<constraint firstItem="508" firstAttribute="leading" secondItem="562" secondAttribute="trailing" constant="8" symbolic="YES" id="568"/>
|
||||
<constraint firstItem="562" firstAttribute="baseline" secondItem="508" secondAttribute="baseline" id="569"/>
|
||||
<constraint firstAttribute="trailing" secondItem="508" secondAttribute="trailing" constant="20" symbolic="YES" id="nH9-cd-trF"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="appstore" width="150" height="44"/>
|
||||
<image name="shot-laptop-leaning-iphone" width="1000" height="600"/>
|
||||
</resources>
|
||||
</document>
|
29
platform-ios/ObjC/Mac/MPInitialWindowController.h
Normal file
29
platform-ios/ObjC/Mac/MPInitialWindowController.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPInitialWindowController.h
|
||||
// MPInitialWindowController
|
||||
//
|
||||
// Created by lhunath on 2014-06-29.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface MPInitialWindowController : NSWindowController
|
||||
|
||||
@property(nonatomic, weak) IBOutlet NSButton *openAtLoginButton;
|
||||
|
||||
- (IBAction)iphoneAppStore:(id)sender;
|
||||
- (IBAction)androidPlayStore:(id)sender;
|
||||
- (IBAction)togglePreference:(id)sender;
|
||||
|
||||
@end
|
57
platform-ios/ObjC/Mac/MPInitialWindowController.m
Normal file
57
platform-ios/ObjC/Mac/MPInitialWindowController.m
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPInitialWindowController.h
|
||||
// MPInitialWindowController
|
||||
//
|
||||
// Created by lhunath on 2014-06-29.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPInitialWindowController.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@implementation MPInitialWindowController
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (void)windowDidLoad {
|
||||
|
||||
[super windowDidLoad];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
[MPMacAppDelegate get].initialWindowController = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)iphoneAppStore:(id)sender {
|
||||
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/id510296984"]];
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (IBAction)androidPlayStore:(id)sender {
|
||||
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://masterpasswordapp.com"]];
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (IBAction)togglePreference:(id)sender {
|
||||
|
||||
if (sender == self.openAtLoginButton)
|
||||
[[MPMacAppDelegate get] setLoginItemEnabled:self.openAtLoginButton.state == NSOnState];
|
||||
}
|
||||
|
||||
@end
|
39
platform-ios/ObjC/Mac/MPMacAppDelegate.h
Normal file
39
platform-ios/ObjC/Mac/MPMacAppDelegate.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MPMacAppDelegate.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 04/03/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPPasswordWindowController.h"
|
||||
#import "MPInitialWindowController.h"
|
||||
|
||||
@interface MPMacAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate>
|
||||
|
||||
@property(nonatomic, strong) NSStatusItem *statusView;
|
||||
@property(nonatomic, strong) MPPasswordWindowController *passwordWindowController;
|
||||
@property(nonatomic, strong) MPInitialWindowController *initialWindowController;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *showItem;
|
||||
@property(nonatomic, strong) IBOutlet NSMenu *statusMenu;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *hidePasswordsItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *rememberPasswordItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *openAtLoginItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *showFullScreenItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *savePasswordItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *deleteUserItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *usersItem;
|
||||
|
||||
- (IBAction)showPasswordWindow:(id)sender;
|
||||
- (void)setLoginItemEnabled:(BOOL)enabled;
|
||||
- (IBAction)togglePreference:(id)sender;
|
||||
- (IBAction)newUser:(NSMenuItem *)sender;
|
||||
- (IBAction)lock:(id)sender;
|
||||
- (IBAction)terminate:(id)sender;
|
||||
- (IBAction)showPopup:(id)sender;
|
||||
|
||||
@end
|
676
platform-ios/ObjC/Mac/MPMacAppDelegate.m
Normal file
676
platform-ios/ObjC/Mac/MPMacAppDelegate.m
Normal file
@@ -0,0 +1,676 @@
|
||||
//
|
||||
// MPMacAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 04/03/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPMacAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <ServiceManagement/ServiceManagement.h>
|
||||
|
||||
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
|
||||
|
||||
@implementation MPMacAppDelegate
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wfour-char-constants"
|
||||
static EventHotKeyID MPShowHotKey = { .signature = 'show', .id = 1 };
|
||||
static EventHotKeyID MPLockHotKey = { .signature = 'lock', .id = 1 };
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
+ (void)initialize {
|
||||
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
[MPMacConfig get];
|
||||
|
||||
#ifdef DEBUG
|
||||
[PearlLogger get].printLevel = PearlLogLevelDebug; //Trace;
|
||||
#endif
|
||||
} );
|
||||
}
|
||||
|
||||
static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) {
|
||||
|
||||
// Extract the hotkey ID.
|
||||
EventHotKeyID hotKeyID;
|
||||
GetEventParameter( theEvent, kEventParamDirectObject, typeEventHotKeyID,
|
||||
NULL, sizeof( hotKeyID ), NULL, &hotKeyID );
|
||||
|
||||
// Check which hotkey this was.
|
||||
if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) {
|
||||
[((__bridge MPMacAppDelegate *)userData) showPasswordWindow:nil];
|
||||
return noErr;
|
||||
}
|
||||
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
|
||||
[((__bridge MPMacAppDelegate *)userData) lock:nil];
|
||||
return noErr;
|
||||
}
|
||||
|
||||
return eventNotHandledErr;
|
||||
}
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
|
||||
#ifdef CRASHLYTICS
|
||||
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
|
||||
if ([crashlyticsAPIKey length]) {
|
||||
inf(@"Initializing Crashlytics");
|
||||
#if defined (DEBUG) || defined (ADHOC)
|
||||
[Crashlytics sharedInstance].debugMode = YES;
|
||||
#endif
|
||||
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
|
||||
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
level = PearlLogLevelDebug;
|
||||
|
||||
if (message.level >= level)
|
||||
CLSLog( @"%@", [message messageDescription] );
|
||||
|
||||
return YES;
|
||||
}];
|
||||
CLSLog( @"Crashlytics (%@) initialized for: %@ v%@.", //
|
||||
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setup delegates and listeners.
|
||||
[MPConfig get].delegate = self;
|
||||
__weak id weakSelf = self;
|
||||
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
[weakSelf updateMenuItems];
|
||||
} );
|
||||
} forKeyPath:@"key" options:0 context:nil];
|
||||
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
[weakSelf updateMenuItems];
|
||||
} );
|
||||
} forKeyPath:@"activeUser" options:0 context:nil];
|
||||
|
||||
// Status item.
|
||||
self.statusView = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
|
||||
self.statusView.image = [NSImage imageNamed:@"menu-icon"];
|
||||
self.statusView.image.template = YES;
|
||||
self.statusView.menu = self.statusMenu;
|
||||
self.statusView.target = self;
|
||||
self.statusView.action = @selector( showMenu );
|
||||
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, self.storeCoordinator, nil,
|
||||
^(id self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self updateUsers];
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, self.storeCoordinator, nil,
|
||||
^(id self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self updateUsers];
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, nil,
|
||||
^(MPMacAppDelegate *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
NSString *key = note.object;
|
||||
if (!key || [key isEqualToString:NSStringFromSelector( @
|
||||
selector( hidePasswords ) )])
|
||||
self.hidePasswordsItem.state = [[MPConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
|
||||
if (!key || [key isEqualToString:NSStringFromSelector( @
|
||||
selector( rememberLogin ) )])
|
||||
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
|
||||
} );
|
||||
} );
|
||||
[self updateUsers];
|
||||
|
||||
// Global hotkey.
|
||||
EventHotKeyRef hotKeyRef;
|
||||
EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
|
||||
OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ), hotKeyEvents, (__bridge void *)self, NULL );
|
||||
if (status != noErr)
|
||||
err( @"Error installing application event handler: %i", (int)status );
|
||||
status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
|
||||
if (status != noErr)
|
||||
err( @"Error registering 'show' hotkey: %i", (int)status );
|
||||
status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
|
||||
if (status != noErr)
|
||||
err( @"Error registering 'lock' hotkey: %i", (int)status );
|
||||
|
||||
// Initial display.
|
||||
if ([[MPMacConfig get].firstRun boolValue]) {
|
||||
[(self.initialWindowController = [[MPInitialWindowController alloc] initWithWindowNibName:@"MPInitialWindow"])
|
||||
.window makeKeyAndOrderFront:self];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
|
||||
if (![[MPConfig get].rememberLogin boolValue])
|
||||
[self lock:nil];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
// Save changes in the application's managed object context before the application terminates.
|
||||
|
||||
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!context)
|
||||
return NSTerminateNow;
|
||||
|
||||
if (![context commitEditing])
|
||||
return NSTerminateCancel;
|
||||
|
||||
if (![context hasChanges])
|
||||
return NSTerminateNow;
|
||||
|
||||
[context saveToStore];
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
|
||||
[super setActiveUser:activeUser];
|
||||
|
||||
if (activeUser)
|
||||
[MPMacConfig get].usedUserName = activeUser.name;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self updateUsers];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)setLoginItemEnabled:(BOOL)enabled {
|
||||
|
||||
BOOL loginItemEnabled = [self loginItemEnabled];
|
||||
if (loginItemEnabled != enabled) {
|
||||
if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true)
|
||||
loginItemEnabled = enabled;
|
||||
else
|
||||
wrn( @"Failed to set login item." );
|
||||
}
|
||||
|
||||
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
}
|
||||
|
||||
- (BOOL)loginItemEnabled {
|
||||
|
||||
// The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_-
|
||||
NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
|
||||
|
||||
for (NSDictionary *job in jobs)
|
||||
if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:job[@"Label"]])
|
||||
return [job[@"OnDemand"] boolValue];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier {
|
||||
|
||||
// All features are unlocked for mac versions.
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)selectUser:(NSMenuItem *)item {
|
||||
|
||||
[self signOutAnimated:NO];
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
self.activeUser = [MPUserEntity existingObjectWithID:[item representedObject] inContext:mainContext];
|
||||
}
|
||||
|
||||
- (IBAction)exportSitesSecure:(id)sender {
|
||||
|
||||
[self exportSitesAndRevealPasswords:NO];
|
||||
}
|
||||
|
||||
- (IBAction)exportSitesReveal:(id)sender {
|
||||
|
||||
[self exportSitesAndRevealPasswords:YES];
|
||||
}
|
||||
|
||||
- (IBAction)importSites:(id)sender {
|
||||
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
openPanel.allowsMultipleSelection = NO;
|
||||
openPanel.canChooseDirectories = NO;
|
||||
openPanel.title = @"Master Password";
|
||||
openPanel.message = @"Locate the Master Password export file to import.";
|
||||
openPanel.prompt = @"Import";
|
||||
openPanel.directoryURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
|
||||
openPanel.allowedFileTypes = @[ @"mpsites" ];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
if ([openPanel runModal] == NSFileHandlingPanelCancelButton)
|
||||
return;
|
||||
|
||||
NSURL *url = openPanel.URL;
|
||||
[openPanel close];
|
||||
|
||||
[[NSURLSession sharedSession]
|
||||
dataTaskWithURL:url completionHandler:^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||
if (!importedSitesData)
|
||||
return;
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
PearlMainQueueWait( ^{
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Unlock"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = @"Import File's Master Password";
|
||||
alert.informativeText = strf( @"%@'s export was done using a different master password.\n"
|
||||
@"Enter that master password to unlock the exported data.", userName );
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
masterPassword = ((NSTextField *)alert.accessoryView).stringValue;
|
||||
} );
|
||||
|
||||
return masterPassword;
|
||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
PearlMainQueueWait( ^{
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Import"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = strf( @"Master Password for\n%@", userName );
|
||||
alert.informativeText = strf( @"Imports %lu sites, overwriting %lu.",
|
||||
(unsigned long)importCount, (unsigned long)deleteCount );
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
masterPassword = ((NSTextField *)alert.accessoryView).stringValue;
|
||||
} );
|
||||
|
||||
return masterPassword;
|
||||
}];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
switch (result) {
|
||||
case MPImportResultSuccess: {
|
||||
[self updateUsers];
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
alert.messageText = @"Successfully imported sites.";
|
||||
[alert runModal];
|
||||
break;
|
||||
}
|
||||
case MPImportResultCancelled:
|
||||
break;
|
||||
case MPImportResultInternalError:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Import failed because of an internal error."
|
||||
}]] runModal];
|
||||
break;
|
||||
case MPImportResultMalformedInput:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"The import doesn't look like a Master Password export."
|
||||
}]] runModal];
|
||||
break;
|
||||
case MPImportResultInvalidPassword:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Incorrect master password for the import sites."
|
||||
}]] runModal];
|
||||
break;
|
||||
}
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)togglePreference:(id)sender {
|
||||
|
||||
if (sender == self.hidePasswordsItem)
|
||||
[MPConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
||||
if (sender == self.rememberPasswordItem)
|
||||
[MPConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
|
||||
if (sender == self.openAtLoginItem)
|
||||
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
|
||||
if (sender == self.showFullScreenItem) {
|
||||
[MPMacConfig get].fullScreen = @(self.showFullScreenItem.state != NSOnState);
|
||||
[NSApp updateWindows];
|
||||
}
|
||||
if (sender == self.savePasswordItem) {
|
||||
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
|
||||
if ((activeUser.saveKey = !activeUser.saveKey))
|
||||
[[MPMacAppDelegate get] storeSavedKeyFor:activeUser];
|
||||
else
|
||||
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||
[context saveToStore];
|
||||
}];
|
||||
}
|
||||
|
||||
[MPMacConfig flush];
|
||||
[self updateMenuItems];
|
||||
}
|
||||
|
||||
- (IBAction)newUser:(NSMenuItem *)sender {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert setMessageText:@"New User"];
|
||||
[alert setInformativeText:@"To begin, enter your full name.\n\n"
|
||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||
@"as you would on an official document."];
|
||||
[alert addButtonWithTitle:@"Create User"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert setAccessoryView:nameField];
|
||||
[alert layout];
|
||||
[nameField becomeFirstResponder];
|
||||
if ([alert runModal] != NSAlertFirstButtonReturn)
|
||||
return;
|
||||
|
||||
NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
||||
inManagedObjectContext:moc];
|
||||
newUser.name = name;
|
||||
[moc saveToStore];
|
||||
NSError *error = nil;
|
||||
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
||||
err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
[self setActiveUser:newUser];
|
||||
[self showPasswordWindow:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)deleteUser:(NSMenuItem *)sender {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert setMessageText:@"Delete User"];
|
||||
[alert setInformativeText:strf( @"This will delete %@ and all their sites.", self.activeUserForMainThread.name )];
|
||||
[alert addButtonWithTitle:@"Delete"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
if ([alert runModal] != NSAlertFirstButtonReturn)
|
||||
return;
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[moc deleteObject:[self activeUserInContext:moc]];
|
||||
[self setActiveUser:nil];
|
||||
[moc saveToStore];
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
[self showPasswordWindow:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)lock:(id)sender {
|
||||
|
||||
[self signOutAnimated:YES];
|
||||
}
|
||||
|
||||
- (IBAction)terminate:(id)sender {
|
||||
|
||||
[self.passwordWindowController close];
|
||||
self.passwordWindowController = nil;
|
||||
|
||||
[NSApp terminate:nil];
|
||||
}
|
||||
|
||||
- (IBAction)showPopup:(id)sender {
|
||||
|
||||
[self.statusView popUpStatusItemMenu:self.statusView.menu];
|
||||
}
|
||||
|
||||
- (IBAction)showPasswordWindow:(id)sender {
|
||||
|
||||
prof_new( @"showPasswordWindow" );
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
prof_rewind(@"activateIgnoringOtherApps");
|
||||
|
||||
// If no user, can't activate.
|
||||
if (![self activeUserForMainThread]) {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
alert.messageText = @"No User Selected";
|
||||
alert.informativeText = @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock.";
|
||||
[alert runModal];
|
||||
[self showPopup:nil];
|
||||
prof_finish( @"activeUserForMainThread" );
|
||||
return;
|
||||
}
|
||||
prof_rewind( @"activeUserForMainThread" );
|
||||
|
||||
// Don't show window if we weren't already running (ie. if we haven't been activated before).
|
||||
if (!self.passwordWindowController)
|
||||
self.passwordWindowController = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
|
||||
prof_rewind( @"initWithWindow" );
|
||||
|
||||
[self.passwordWindowController showWindow:self];
|
||||
prof_finish( @"showWindow" );
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)exportSitesAndRevealPasswords:(BOOL)revealPasswords {
|
||||
|
||||
MPUserEntity *mainActiveUser = [self activeUserForMainThread];
|
||||
if (!mainActiveUser) {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
alert.messageText = @"No User Selected";
|
||||
alert.informativeText = @"To export your sites, first select the user whose sites to export.";
|
||||
[alert runModal];
|
||||
[self showPopup:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.key) {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
alert.messageText = @"User Locked";
|
||||
alert.informativeText = @"To export your sites, first unlock your user by opening Master Password.";
|
||||
[alert runModal];
|
||||
[self showPopup:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
||||
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
||||
|
||||
NSSavePanel *savePanel = [NSSavePanel savePanel];
|
||||
savePanel.title = @"Master Password";
|
||||
savePanel.message = @"Pick a location for the export Master Password's sites.";
|
||||
if (revealPasswords)
|
||||
savePanel.message = strf( @"%@\nWARNING: Your passwords will be visible. Make sure to always keep the file in a secure location.",
|
||||
savePanel.message );
|
||||
savePanel.prompt = @"Export";
|
||||
savePanel.directoryURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
|
||||
savePanel.nameFieldStringValue = strf( @"%@ (%@).mpsites", mainActiveUser.name,
|
||||
[exportDateFormatter stringFromDate:[NSDate date]] );
|
||||
savePanel.allowedFileTypes = @[ @"mpsites" ];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
|
||||
return;
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError
|
||||
byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&writeError])
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError)
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:coordinateError] runModal];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)updateUsers {
|
||||
|
||||
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
if (idx > 2)
|
||||
[[self.usersItem submenu] removeItem:obj];
|
||||
}];
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!mainContext) {
|
||||
self.createUserItem.title = @"New User (Not ready)";
|
||||
self.createUserItem.enabled = NO;
|
||||
self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
|
||||
self.deleteUserItem.title = @"Delete User (Not ready)";
|
||||
self.deleteUserItem.enabled = NO;
|
||||
self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded.";
|
||||
[self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MPUserEntity *mainActiveUser = [self activeUserInContext:mainContext];
|
||||
|
||||
self.createUserItem.title = @"New User";
|
||||
self.createUserItem.enabled = YES;
|
||||
self.createUserItem.toolTip = nil;
|
||||
|
||||
self.deleteUserItem.title = mainActiveUser? @"Delete User": @"Delete User (None Selected)";
|
||||
self.deleteUserItem.enabled = mainActiveUser != nil;
|
||||
self.deleteUserItem.toolTip = mainActiveUser? nil: @"First select the user to delete.";
|
||||
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users)
|
||||
err( @"Failed to load users: %@", [error fullDescription] );
|
||||
|
||||
if (![users count]) {
|
||||
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
|
||||
noUsersItem.enabled = NO;
|
||||
noUsersItem.toolTip = @"Begin by creating a user.";
|
||||
}
|
||||
|
||||
self.usersItem.state = NSMixedState;
|
||||
for (MPUserEntity *user in users) {
|
||||
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
|
||||
[userItem setTarget:self];
|
||||
[userItem setRepresentedObject:[user objectID]];
|
||||
[[self.usersItem submenu] addItem:userItem];
|
||||
|
||||
if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
|
||||
[super setActiveUser:mainActiveUser = user];
|
||||
|
||||
if ([mainActiveUser isEqual:user]) {
|
||||
userItem.state = NSOnState;
|
||||
self.usersItem.state = NSOffState;
|
||||
}
|
||||
else
|
||||
userItem.state = NSOffState;
|
||||
}
|
||||
|
||||
[self updateMenuItems];
|
||||
}
|
||||
|
||||
- (void)showMenu {
|
||||
|
||||
[self updateMenuItems];
|
||||
|
||||
[self.statusView popUpStatusItemMenu:self.statusView.menu];
|
||||
}
|
||||
|
||||
- (void)updateMenuItems {
|
||||
|
||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||
// if (!(self.showItem.enabled = ![self.passwordWindowController.window isVisible])) {
|
||||
// self.showItem.title = @"Show (Showing)";
|
||||
// self.showItem.toolTip = @"Master Password is already showing.";
|
||||
// }
|
||||
// else if (!(self.showItem.enabled = (activeUser != nil))) {
|
||||
// self.showItem.title = @"Show (No user)";
|
||||
// self.showItem.toolTip = @"First select the user to show passwords for.";
|
||||
// }
|
||||
// else {
|
||||
// self.showItem.title = @"Show";
|
||||
// self.showItem.toolTip = nil;
|
||||
// }
|
||||
|
||||
if (self.key) {
|
||||
self.lockItem.title = @"Lock";
|
||||
self.lockItem.enabled = YES;
|
||||
self.lockItem.toolTip = nil;
|
||||
}
|
||||
else {
|
||||
self.lockItem.title = @"Lock (Locked)";
|
||||
self.lockItem.enabled = NO;
|
||||
self.lockItem.toolTip = @"Master Password is currently locked.";
|
||||
}
|
||||
|
||||
BOOL loginItemEnabled = [self loginItemEnabled];
|
||||
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState;
|
||||
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
|
||||
|
||||
self.savePasswordItem.state = activeUser.saveKey? NSOnState: NSOffState;
|
||||
if (!activeUser) {
|
||||
self.savePasswordItem.title = @"Save Password (No user)";
|
||||
self.savePasswordItem.enabled = NO;
|
||||
self.savePasswordItem.toolTip = @"First select your user and unlock by showing the Master Password window.";
|
||||
}
|
||||
else if (!self.key) {
|
||||
self.savePasswordItem.title = @"Save Password (Locked)";
|
||||
self.savePasswordItem.enabled = NO;
|
||||
self.savePasswordItem.toolTip = @"First unlock by showing the Master Password window.";
|
||||
}
|
||||
else {
|
||||
self.savePasswordItem.title = @"Save Password";
|
||||
self.savePasswordItem.enabled = YES;
|
||||
self.savePasswordItem.toolTip = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - PearlConfigDelegate
|
||||
|
||||
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )];
|
||||
}
|
||||
|
||||
#pragma mark - Crashlytics
|
||||
|
||||
- (NSDictionary *)crashlyticsInfo {
|
||||
|
||||
static NSDictionary *crashlyticsInfo = nil;
|
||||
if (crashlyticsInfo == nil)
|
||||
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
|
||||
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
|
||||
|
||||
return crashlyticsInfo;
|
||||
}
|
||||
|
||||
- (NSString *)crashlyticsAPIKey {
|
||||
|
||||
NSString *crashlyticsAPIKey = NSNullToNil( [[self crashlyticsInfo] valueForKeyPath:@"API Key"] );
|
||||
if (![crashlyticsAPIKey length])
|
||||
wrn( @"Crashlytics API key not set. Crash logs won't be recorded." );
|
||||
|
||||
return crashlyticsAPIKey;
|
||||
}
|
||||
|
||||
@end
|
9
platform-ios/ObjC/Mac/MPMacApplication.h
Normal file
9
platform-ios/ObjC/Mac/MPMacApplication.h
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Created by Maarten Billemont on 2016-04-30.
|
||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MPMacApplication : NSApplication
|
||||
@end
|
54
platform-ios/ObjC/Mac/MPMacApplication.m
Normal file
54
platform-ios/ObjC/Mac/MPMacApplication.m
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by Maarten Billemont on 2016-04-30.
|
||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPMacApplication.h"
|
||||
|
||||
@interface NSResponder (Editing)
|
||||
|
||||
- (void)undo:(id)sender;
|
||||
- (void)redo:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPMacApplication {
|
||||
}
|
||||
|
||||
- (void)sendEvent:(NSEvent *)event {
|
||||
|
||||
if ([event type] == NSKeyDown) {
|
||||
if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
|
||||
if ([self sendAction:@selector( cut: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
|
||||
if ([self sendAction:@selector( copy: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
|
||||
if ([self sendAction:@selector( paste: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
|
||||
if ([self sendAction:@selector( undo: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
|
||||
if ([self sendAction:@selector( selectAll: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
|
||||
if ([self sendAction:@selector( redo: ) to:nil from:self])
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[super sendEvent:event];
|
||||
}
|
||||
|
||||
@end
|
16
platform-ios/ObjC/Mac/MPMacConfig.h
Normal file
16
platform-ios/ObjC/Mac/MPMacConfig.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MPMacConfig.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPConfig.h"
|
||||
|
||||
@interface MPMacConfig : MPConfig
|
||||
|
||||
@property(nonatomic, retain) NSString *usedUserName;
|
||||
@property(nonatomic, retain) NSNumber *fullScreen;
|
||||
|
||||
@end
|
27
platform-ios/ObjC/Mac/MPMacConfig.m
Normal file
27
platform-ios/ObjC/Mac/MPMacConfig.m
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// MPMacConfig.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@implementation MPMacConfig
|
||||
|
||||
@dynamic usedUserName;
|
||||
@dynamic fullScreen;
|
||||
|
||||
- (id)init {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return self;
|
||||
|
||||
[self.defaults registerDefaults:@{
|
||||
NSStringFromSelector( @selector( appleID ) ) : @"510296984",
|
||||
NSStringFromSelector( @selector( fullScreen ) ) : @YES,
|
||||
}];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
23
platform-ios/ObjC/Mac/MPNoStateButton.h
Normal file
23
platform-ios/ObjC/Mac/MPNoStateButton.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPNoStateButton.h
|
||||
// MPNoStateButton
|
||||
//
|
||||
// Created by lhunath on 14-10-27.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface MPNoStateButtonCell : NSButtonCell
|
||||
@end
|
31
platform-ios/ObjC/Mac/MPNoStateButton.m
Normal file
31
platform-ios/ObjC/Mac/MPNoStateButton.m
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPNoStateButton.h
|
||||
// MPNoStateButton
|
||||
//
|
||||
// Created by lhunath on 14-10-27.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPNoStateButton.h"
|
||||
|
||||
|
||||
@implementation MPNoStateButtonCell {
|
||||
|
||||
}
|
||||
|
||||
- (void)setState:(NSInteger)state {
|
||||
|
||||
[super setState:NSOnState];
|
||||
}
|
||||
|
||||
@end
|
24
platform-ios/ObjC/Mac/MPPasswordWindow.h
Normal file
24
platform-ios/ObjC/Mac/MPPasswordWindow.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindow.h
|
||||
// MPPasswordWindow
|
||||
//
|
||||
// Created by lhunath on 2014-06-19.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface MPPasswordWindow : NSWindow
|
||||
|
||||
@end
|
49
platform-ios/ObjC/Mac/MPPasswordWindow.m
Normal file
49
platform-ios/ObjC/Mac/MPPasswordWindow.m
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindow.h
|
||||
// MPPasswordWindow
|
||||
//
|
||||
// Created by lhunath on 2014-06-19.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPPasswordWindow.h"
|
||||
|
||||
@implementation MPPasswordWindow
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (BOOL)canBecomeKeyWindow {
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)update {
|
||||
|
||||
if ([[MPMacConfig get].fullScreen boolValue]) {
|
||||
[self setLevel:NSScreenSaverWindowLevel];
|
||||
[self setFrame:self.screen.frame display:YES];
|
||||
}
|
||||
else if (self.level != NSNormalWindowLevel) {
|
||||
[self setLevel:NSNormalWindowLevel];
|
||||
[self setFrame:NSMakeRect( 0, 0, 640, 600) display:NO];
|
||||
[self center];
|
||||
}
|
||||
|
||||
[super update];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
@end
|
54
platform-ios/ObjC/Mac/MPPasswordWindowController.h
Normal file
54
platform-ios/ObjC/Mac/MPPasswordWindowController.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindowController.h
|
||||
// MPPasswordWindowController
|
||||
//
|
||||
// Created by lhunath on 2014-06-18.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MPSiteModel.h"
|
||||
#import "MPSitesTableView.h"
|
||||
#import "MPPasswordWindow.h"
|
||||
|
||||
@class MPMacAppDelegate;
|
||||
|
||||
@interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||
|
||||
@property(nonatomic) NSMutableArray *sites;
|
||||
@property(nonatomic) NSString *masterPassword;
|
||||
@property(nonatomic) BOOL showVersionContainer;
|
||||
@property(nonatomic) BOOL alternatePressed;
|
||||
@property(nonatomic) BOOL shiftPressed;
|
||||
@property(nonatomic) BOOL locked;
|
||||
@property(nonatomic) BOOL newUser;
|
||||
|
||||
@property(nonatomic, weak) IBOutlet NSArrayController *sitesController;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *sitePasswordTipField;
|
||||
@property(nonatomic, weak) IBOutlet NSSearchField *siteField;
|
||||
@property(nonatomic, weak) IBOutlet MPSitesTableView *siteTable;
|
||||
@property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
|
||||
|
||||
@property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox;
|
||||
@property(nonatomic, weak) IBOutlet NSMatrix *passwordTypesMatrix;
|
||||
|
||||
@property(nonatomic, strong) IBOutlet NSBox *securityQuestionsBox;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *securityQuestionField;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *securityAnswerField;
|
||||
|
||||
- (BOOL)handleCommand:(SEL)commandSelector;
|
||||
|
||||
@end
|
640
platform-ios/ObjC/Mac/MPPasswordWindowController.m
Normal file
640
platform-ios/ObjC/Mac/MPPasswordWindowController.m
Normal file
@@ -0,0 +1,640 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindowController.h
|
||||
// MPPasswordWindowController
|
||||
//
|
||||
// Created by lhunath on 2014-06-18.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "MPPasswordWindowController.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
|
||||
@interface MPPasswordWindowController()
|
||||
|
||||
@property(nonatomic, strong) CAGradientLayer *siteGradient;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordWindowController
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (void)windowDidLoad {
|
||||
|
||||
prof_new( @"windowDidLoad" );
|
||||
[super windowDidLoad];
|
||||
prof_rewind( @"super" );
|
||||
|
||||
[self replaceFonts:self.window.contentView];
|
||||
prof_rewind( @"replaceFonts" );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
prof_new( @"didBecomeKey" );
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
prof_rewind( @"fadeIn" );
|
||||
[self updateUser];
|
||||
prof_finish( @"updateUser" );
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
NSWindow *sheet = [self.window attachedSheet];
|
||||
if (sheet)
|
||||
[self.window endSheet:sheet];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self.window close];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
[self observeKeyPath:@"sitesController.selection"
|
||||
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
||||
prof_new( @"sitesController.selection" );
|
||||
[_self updateSelection];
|
||||
prof_finish( @"updateSelection" );
|
||||
}];
|
||||
prof_rewind( @"observers" );
|
||||
|
||||
NSSearchFieldCell *siteFieldCell = (NSSearchFieldCell *)self.siteField.cell;
|
||||
siteFieldCell.searchButtonCell = nil;
|
||||
siteFieldCell.cancelButtonCell = nil;
|
||||
|
||||
self.siteGradient = [CAGradientLayer layer];
|
||||
self.siteGradient.colors = @[ (__bridge id)[NSColor whiteColor].CGColor, (__bridge id)[NSColor clearColor].CGColor ];
|
||||
self.siteGradient.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
|
||||
self.siteGradient.frame = self.siteTable.bounds;
|
||||
self.siteTable.superview.superview.layer.mask = self.siteGradient;
|
||||
|
||||
self.siteTable.controller = self;
|
||||
prof_finish( @"ui" );
|
||||
}
|
||||
|
||||
- (void)replaceFonts:(NSView *)view {
|
||||
|
||||
if (view.window.backingScaleFactor == 1)
|
||||
[view enumerateViews:^(NSView *subview, BOOL *stop, BOOL *recurse) {
|
||||
if ([subview respondsToSelector:@selector( setFont: )]) {
|
||||
NSFont *font = [(id)subview font];
|
||||
if ([font.fontName isEqualToString:@"HelveticaNeue-Thin"])
|
||||
[(id)subview setFont:[NSFont fontWithName:@"HelveticaNeue" matrix:font.matrix]];
|
||||
if ([font.fontName isEqualToString:@"HelveticaNeue-Light"])
|
||||
[(id)subview setFont:[NSFont fontWithName:@"HelveticaNeue" matrix:font.matrix]];
|
||||
}
|
||||
} recurse:YES];
|
||||
}
|
||||
|
||||
- (void)flagsChanged:(NSEvent *)theEvent {
|
||||
|
||||
BOOL shiftPressed = (theEvent.modifierFlags & NSShiftKeyMask) != 0;
|
||||
if (shiftPressed != self.shiftPressed)
|
||||
self.shiftPressed = shiftPressed;
|
||||
|
||||
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
|
||||
if (alternatePressed != self.alternatePressed) {
|
||||
self.alternatePressed = alternatePressed;
|
||||
self.showVersionContainer = self.alternatePressed || self.selectedSite.outdated;
|
||||
[self.selectedSite updateContent];
|
||||
|
||||
if (self.locked) {
|
||||
NSTextField *passwordField = self.securePasswordField;
|
||||
if (self.securePasswordField.isHidden)
|
||||
passwordField = self.revealPasswordField;
|
||||
[passwordField becomeFirstResponder];
|
||||
[[passwordField currentEditor] moveToEndOfLine:nil];
|
||||
}
|
||||
}
|
||||
|
||||
[super flagsChanged:theEvent];
|
||||
}
|
||||
|
||||
#pragma mark - NSResponder
|
||||
|
||||
// Handle any unhandled editor command.
|
||||
- (void)doCommandBySelector:(SEL)commandSelector {
|
||||
|
||||
[self handleCommand:commandSelector];
|
||||
}
|
||||
|
||||
#pragma mark - NSTextFieldDelegate
|
||||
|
||||
// Editor command in a text field.
|
||||
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
|
||||
|
||||
if (control == self.siteField) {
|
||||
if ([NSStringFromSelector( commandSelector ) rangeOfString:@"delete"].location == 0)
|
||||
return NO;
|
||||
}
|
||||
if (control == self.securePasswordField || control == self.revealPasswordField) {
|
||||
if (commandSelector == @selector( insertNewline: ))
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self handleCommand:commandSelector];
|
||||
}
|
||||
|
||||
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
|
||||
|
||||
if (control == self.siteField)
|
||||
[fieldEditor replaceCharactersInRange:fieldEditor.selectedRange withString:@""];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (IBAction)doUnlockUser:(id)sender {
|
||||
|
||||
[self.progressView startAnimation:self];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||
NSString *userName = activeUser.name;
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:self.masterPassword];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.masterPassword = nil;
|
||||
[self.progressView stopAnimation:self];
|
||||
if (!success)
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey : strf( @"Incorrect master password for user %@", userName )
|
||||
}]] beginSheetModalForWindow:self.window completionHandler:nil];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doSearchSites:(id)sender {
|
||||
|
||||
[self updateSites];
|
||||
}
|
||||
|
||||
#pragma mark - NSTextViewDelegate
|
||||
|
||||
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
|
||||
|
||||
return [self handleCommand:commandSelector];
|
||||
}
|
||||
|
||||
#pragma mark - NSTableViewDataSource
|
||||
|
||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
|
||||
|
||||
return (NSInteger)[self.sites count];
|
||||
}
|
||||
|
||||
#pragma mark - NSTableViewDelegate
|
||||
|
||||
- (void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
|
||||
|
||||
[self replaceFonts:rowView];
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
|
||||
|
||||
[self.sites insertObject:model atIndex:index];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromSitesAtIndex:(NSUInteger)index {
|
||||
|
||||
[self.sites removeObjectAtIndex:index];
|
||||
}
|
||||
|
||||
- (MPSiteModel *)selectedSite {
|
||||
|
||||
return [self.sitesController.selectedObjects firstObject];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)settings:(id)sender {
|
||||
|
||||
[self.window close];
|
||||
[[MPMacAppDelegate get] showPopup:sender];
|
||||
}
|
||||
|
||||
- (IBAction)deleteSite:(id)sender {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Delete"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Delete Site?"];
|
||||
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.name )];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Delete" button.
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[context deleteObject:[self.selectedSite entityInContext:context]];
|
||||
[context saveToStore];
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)changeLogin:(id)sender {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Save"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Change Login Name"];
|
||||
[alert setInformativeText:strf( @"Your login name for: %@", self.selectedSite.name )];
|
||||
NSTextField *loginField = [NSTextField new];
|
||||
[loginField bind:@"value" toObject:self.selectedSite withKeyPath:@"loginName" options:nil];
|
||||
NSButton *generatedField = [NSButton new];
|
||||
[generatedField setButtonType:NSSwitchButton];
|
||||
[generatedField bind:@"value" toObject:self.selectedSite withKeyPath:@"loginGenerated" options:nil];
|
||||
generatedField.title = @"Generated";
|
||||
NSStackView *stackView = [NSStackView stackViewWithViews:@[ loginField, generatedField ]];
|
||||
stackView.orientation = NSUserInterfaceLayoutOrientationVertical;
|
||||
stackView.frame = NSMakeRect( 0, 0, 200, 44 );
|
||||
[stackView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[loginField(200)]"
|
||||
options:0 metrics:nil
|
||||
views:NSDictionaryOfVariableBindings( loginField, stackView )]];
|
||||
[stackView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[generatedField(200)]"
|
||||
options:0 metrics:nil
|
||||
views:NSDictionaryOfVariableBindings( generatedField, stackView )]];
|
||||
[alert setAccessoryView:stackView];
|
||||
[alert layout];
|
||||
[loginField selectText:self];
|
||||
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Save" button.
|
||||
NSString *loginName = [loginField stringValue];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
|
||||
entity.loginName = !self.selectedSite.loginGenerated && [loginName length]? loginName: nil;
|
||||
[context saveToStore];
|
||||
[self.selectedSite updateContent];
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)resetMasterPassword:(id)sender {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Reset"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Reset My Master Password"];
|
||||
[alert setInformativeText:strf( @"This will allow you to change %@'s master password.\n\n"
|
||||
@"WARNING: All your site passwords will change. Do this only if you've forgotten your "
|
||||
@"master password and are fully prepared to change all your sites' passwords to the new ones.",
|
||||
[MPMacAppDelegate get].activeUserForMainThread.name )];
|
||||
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Reset" button.
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
|
||||
NSString *activeUserName = activeUser.name;
|
||||
activeUser.keyID = nil;
|
||||
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||
[context saveToStore];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
NSAlert *alert_ = [NSAlert new];
|
||||
alert_.messageText = @"Master Password Reset";
|
||||
alert_.informativeText = strf( @"%@'s master password has been reset.\n\nYou can now set a new one by logging in.",
|
||||
activeUserName );
|
||||
[alert_ beginSheetModalForWindow:self.window completionHandler:nil];
|
||||
|
||||
if ([MPMacAppDelegate get].key)
|
||||
[[MPMacAppDelegate get] signOutAnimated:YES];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)changePassword:(id)sender {
|
||||
|
||||
if (!self.selectedSite.stored)
|
||||
return;
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Save"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Change Password"];
|
||||
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.name )];
|
||||
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert setAccessoryView:passwordField];
|
||||
[alert layout];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Save" button.
|
||||
NSString *password = [passwordField stringValue];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
|
||||
[entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
|
||||
[context saveToStore];
|
||||
[self.selectedSite updateContent];
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)changeType:(id)sender {
|
||||
|
||||
MPSiteModel *site = self.selectedSite;
|
||||
NSArray *types = [site.algorithm allTypes];
|
||||
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
|
||||
for (NSUInteger t = 0; t < [types count]; ++t) {
|
||||
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
|
||||
NSString *title = [site.algorithm nameOfType:type];
|
||||
if (type & MPSiteTypeClassGenerated)
|
||||
title = strf( @"%@ – %@", [site.algorithm generatePasswordForSiteNamed:site.name ofType:type withCounter:site.counter
|
||||
usingKey:[MPMacAppDelegate get].key], title );
|
||||
|
||||
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||
cell.tag = type;
|
||||
cell.state = type == site.type? NSOnState: NSOffState;
|
||||
cell.title = title;
|
||||
}
|
||||
|
||||
self.passwordTypesBox.title = strf( @"Choose a password type for %@:", site.name );
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Save"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Change Password Type"];
|
||||
[alert setAccessoryView:self.passwordTypesBox];
|
||||
[alert layout];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Save" button.
|
||||
MPSiteType type = (MPSiteType)[self.passwordTypesMatrix.selectedCell tag];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [[MPMacAppDelegate get] changeSite:[self.selectedSite entityInContext:context]
|
||||
saveInContext:context toType:type];
|
||||
if ([entity isKindOfClass:[MPStoredSiteEntity class]] && ![(MPStoredSiteEntity *)entity contentObject].length)
|
||||
PearlMainQueue( ^{
|
||||
[self changePassword:nil];
|
||||
} );
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)securityQuestions:(id)sender {
|
||||
|
||||
MPSiteModel *site = self.selectedSite;
|
||||
self.securityQuestionsBox.title = strf( @"Answer to security questions for %@:", site.name );
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Copy Answer"];
|
||||
[alert addButtonWithTitle:@"Close"];
|
||||
[alert setMessageText:@"Security Questions"];
|
||||
[alert setAccessoryView:self.securityQuestionsBox];
|
||||
[alert layout];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Copy Answer" button.
|
||||
[self copyContent:self.securityAnswerField.stringValue];
|
||||
[self.window close];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (BOOL)handleCommand:(SEL)commandSelector {
|
||||
|
||||
if (commandSelector == @selector( moveUp: )) {
|
||||
[self.sitesController selectPrevious:self];
|
||||
return YES;
|
||||
}
|
||||
if (commandSelector == @selector( moveDown: )) {
|
||||
[self.sitesController selectNext:self];
|
||||
return YES;
|
||||
}
|
||||
if (commandSelector == @selector( insertNewline: )) {
|
||||
[self useSite];
|
||||
return YES;
|
||||
}
|
||||
if (commandSelector == @selector( cancel: ) || commandSelector == @selector( cancelOperation: )) {
|
||||
[self.window close];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)useSite {
|
||||
|
||||
MPSiteModel *selectedSite = [self selectedSite];
|
||||
if (!selectedSite)
|
||||
return;
|
||||
|
||||
if (selectedSite.transient) {
|
||||
[self createNewSite:selectedSite.name];
|
||||
return;
|
||||
}
|
||||
|
||||
// Performing action while content is available. Copy it.
|
||||
[self copyContent:self.shiftPressed? selectedSite.answer: selectedSite.content];
|
||||
[self.window close];
|
||||
|
||||
NSUserNotification *notification = [NSUserNotification new];
|
||||
notification.title = @"Password Copied";
|
||||
if (selectedSite.loginName.length)
|
||||
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
|
||||
else
|
||||
notification.subtitle = selectedSite.name;
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||
}
|
||||
|
||||
- (void)updateUser {
|
||||
|
||||
[MPMacAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
self.locked = YES;
|
||||
self.newUser = YES;
|
||||
|
||||
self.inputLabel.stringValue = @"";
|
||||
self.siteField.stringValue = @"";
|
||||
|
||||
MPUserEntity *mainActiveUser = [[MPMacAppDelegate get] activeUserInContext:mainContext];
|
||||
if (mainActiveUser) {
|
||||
self.newUser = mainActiveUser.keyID == nil;
|
||||
|
||||
if ([MPMacAppDelegate get].key) {
|
||||
self.inputLabel.stringValue = strf( @"%@'s password for:", mainActiveUser.name );
|
||||
self.locked = NO;
|
||||
[self.siteField becomeFirstResponder];
|
||||
}
|
||||
else {
|
||||
self.inputLabel.stringValue = strf( @"Enter %@'s master password:", mainActiveUser.name );
|
||||
NSTextField *passwordField = self.securePasswordField;
|
||||
if (self.securePasswordField.isHidden)
|
||||
passwordField = self.revealPasswordField;
|
||||
[passwordField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
[self updateSites];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateSites {
|
||||
|
||||
NSAssert( [NSOperationQueue currentQueue] == [NSOperationQueue mainQueue], @"updateSites should be called on the main queue." );
|
||||
if (![MPMacAppDelegate get].key) {
|
||||
self.sites = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *queryString = self.siteField.stringValue;
|
||||
NSString *queryPattern;
|
||||
if ([queryString length] < 13)
|
||||
queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||
else
|
||||
// If query is too long, a wildcard per character makes the CoreData fetch take excessively long.
|
||||
queryPattern = strf( @"*%@*", queryString );
|
||||
NSMutableArray *fuzzyGroups = [NSMutableArray new];
|
||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!siteResults) {
|
||||
err( @"While fetching sites for completion: %@", [error fullDescription] );
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL exact = NO;
|
||||
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
||||
for (MPSiteEntity *site in siteResults) {
|
||||
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]];
|
||||
exact |= [site.name isEqualToString:queryString];
|
||||
}
|
||||
if (!exact && [queryString length]) {
|
||||
MPUserEntity *activeUser = [[MPAppDelegate_Shared get] activeUserInContext:context];
|
||||
[newSites addObject:[[MPSiteModel alloc] initWithName:queryString forUser:activeUser]];
|
||||
}
|
||||
|
||||
dbg( @"newSites: %@", newSites );
|
||||
if (![newSites isEqualToArray:self.sites])
|
||||
PearlMainQueue( ^{
|
||||
self.sites = newSites;
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateSelection {
|
||||
|
||||
[self.siteTable scrollRowToVisible:(NSInteger)self.sitesController.selectionIndex];
|
||||
|
||||
NSView *siteScrollView = self.siteTable.superview.superview;
|
||||
NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.sitesController.selectionIndex)];
|
||||
CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y;
|
||||
CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
|
||||
self.siteGradient.colors = @[
|
||||
(__bridge id)[NSColor whiteColor].CGColor,
|
||||
(__bridge id)[NSColor colorWithDeviceWhite:1 alpha:1 - (1 - gradientOpacity) * 4 / 5].CGColor,
|
||||
(__bridge id)[NSColor colorWithDeviceWhite:1 alpha:gradientOpacity].CGColor
|
||||
];
|
||||
|
||||
self.showVersionContainer = self.alternatePressed || self.selectedSite.outdated;
|
||||
[self.sitePasswordTipField setAttributedStringValue:straf( @"Your password for %@:", self.selectedSite.displayedName )];
|
||||
}
|
||||
|
||||
- (void)createNewSite:(NSString *)siteName {
|
||||
|
||||
PearlMainQueue( ^{
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Create"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert setMessageText:@"Create site?"];
|
||||
[alert setInformativeText:strf( @"Do you want to create a new site named:\n\n%@", siteName )];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Create" button.
|
||||
[[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
|
||||
^(MPSiteEntity *site, NSManagedObjectContext *context) {
|
||||
if (site)
|
||||
PearlMainQueue( ^{ [self updateSites]; } );
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)copyContent:(NSString *)content {
|
||||
|
||||
[[NSPasteboard generalPasteboard] declareTypes:@[ NSStringPboardType ] owner:nil];
|
||||
if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) {
|
||||
wrn( @"Couldn't copy password to pasteboard." );
|
||||
return;
|
||||
}
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[[self.selectedSite entityInContext:moc] use];
|
||||
[moc saveToStore];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
1329
platform-ios/ObjC/Mac/MPPasswordWindowController.xib
Normal file
1329
platform-ios/ObjC/Mac/MPPasswordWindowController.xib
Normal file
File diff suppressed because it is too large
Load Diff
53
platform-ios/ObjC/Mac/MPSiteModel.h
Normal file
53
platform-ios/ObjC/Mac/MPSiteModel.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPSiteModel.h
|
||||
// MPSiteModel
|
||||
//
|
||||
// Created by lhunath on 2/11/2014.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPAlgorithm.h"
|
||||
#import "MPUserEntity.h"
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPSiteModel : NSObject
|
||||
|
||||
@property (nonatomic) NSString *name;
|
||||
@property (nonatomic) NSAttributedString *displayedName;
|
||||
@property (nonatomic) MPSiteType type;
|
||||
@property (nonatomic) NSString *typeName;
|
||||
@property (nonatomic) NSString *content;
|
||||
@property (nonatomic) NSString *displayedContent;
|
||||
@property (nonatomic) NSString *question;
|
||||
@property (nonatomic) NSString *answer;
|
||||
@property (nonatomic) NSString *loginName;
|
||||
@property (nonatomic) BOOL loginGenerated;
|
||||
@property (nonatomic) NSNumber *uses;
|
||||
@property (nonatomic) NSUInteger counter;
|
||||
@property (nonatomic) NSDate *lastUsed;
|
||||
@property (nonatomic) id<MPAlgorithm> algorithm;
|
||||
@property (nonatomic) MPAlgorithmVersion algorithmVersion;
|
||||
@property (nonatomic, readonly) BOOL outdated;
|
||||
@property (nonatomic, readonly) BOOL generated;
|
||||
@property (nonatomic, readonly) BOOL stored;
|
||||
@property (nonatomic, readonly) BOOL transient;
|
||||
|
||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
|
||||
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
|
||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
||||
|
||||
- (void)updateContent;
|
||||
@end
|
290
platform-ios/ObjC/Mac/MPSiteModel.m
Normal file
290
platform-ios/ObjC/Mac/MPSiteModel.m
Normal file
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPSiteModel.h
|
||||
// MPSiteModel
|
||||
//
|
||||
// Created by lhunath on 2/11/2014.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteModel.h"
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
|
||||
@implementation MPSiteModel {
|
||||
NSManagedObjectID *_entityOID;
|
||||
BOOL _initialized;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
[self setEntity:entity fuzzyGroups:fuzzyGroups];
|
||||
_initialized = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
[self setTransientSiteName:siteName forUser:user];
|
||||
_initialized = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if ([_entityOID isEqual:entity.objectID])
|
||||
return;
|
||||
_entityOID = entity.objectID;
|
||||
|
||||
NSString *siteName = entity.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
|
||||
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) {
|
||||
s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
|
||||
if (s == NSNotFound)
|
||||
break;
|
||||
|
||||
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
|
||||
range:NSMakeRange( s, [fuzzyGroups[f] length] )];
|
||||
}
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
|
||||
|
||||
self.displayedName = attributedSiteName;
|
||||
self.name = siteName;
|
||||
self.algorithm = entity.algorithm;
|
||||
self.lastUsed = entity.lastUsed;
|
||||
self.type = entity.type;
|
||||
self.typeName = entity.typeName;
|
||||
self.uses = entity.uses_;
|
||||
self.counter = [entity isKindOfClass:[MPGeneratedSiteEntity class]]? [(MPGeneratedSiteEntity *)entity counter]: 0;
|
||||
self.loginGenerated = entity.loginGenerated;
|
||||
NSLog( @"%@: loginGenerated: %d", self.name, self.loginGenerated );
|
||||
|
||||
// Find all password types and the index of the current type amongst them.
|
||||
[self updateContent:entity];
|
||||
}
|
||||
|
||||
- (void)setTransientSiteName:(NSString *)siteName forUser:(MPUserEntity *)user {
|
||||
|
||||
_entityOID = nil;
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||
self.displayedName = stra( siteName, @{
|
||||
NSBackgroundColorAttributeName : [NSColor alternateSelectedControlColor],
|
||||
NSParagraphStyleAttributeName : paragraphStyle,
|
||||
} );
|
||||
self.name = siteName;
|
||||
self.algorithm = MPAlgorithmDefault;
|
||||
self.lastUsed = nil;
|
||||
self.type = user.defaultType;
|
||||
self.typeName = [self.algorithm nameOfType:self.type];
|
||||
self.uses = @0;
|
||||
self.counter = 1;
|
||||
|
||||
// Find all password types and the index of the current type amongst them.
|
||||
[self updateContent];
|
||||
}
|
||||
|
||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
if (!_entityOID)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
||||
if (!entity)
|
||||
err( @"Couldn't retrieve active site: %@", [error fullDescription] );
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
- (void)setCounter:(NSUInteger)counter {
|
||||
|
||||
if (counter == _counter)
|
||||
return;
|
||||
_counter = counter;
|
||||
|
||||
if (!_initialized)
|
||||
// This wasn't a change to the entity.
|
||||
return;
|
||||
|
||||
if (_entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||
((MPGeneratedSiteEntity *)entity).counter = counter;
|
||||
[context saveToStore];
|
||||
|
||||
[self updateContent:entity];
|
||||
}
|
||||
}];
|
||||
else
|
||||
[self updateContent];
|
||||
}
|
||||
|
||||
- (void)setLoginGenerated:(BOOL)loginGenerated {
|
||||
|
||||
if (loginGenerated == _loginGenerated)
|
||||
return;
|
||||
_loginGenerated = loginGenerated;
|
||||
|
||||
if (!_initialized)
|
||||
// This wasn't a change to the entity.
|
||||
return;
|
||||
|
||||
if (_entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
entity.loginGenerated = loginGenerated;
|
||||
[context saveToStore];
|
||||
|
||||
[self updateContent:entity];
|
||||
}];
|
||||
else
|
||||
[self updateContent];
|
||||
}
|
||||
|
||||
- (MPAlgorithmVersion)algorithmVersion {
|
||||
|
||||
return self.algorithm.version;
|
||||
}
|
||||
|
||||
- (void)setAlgorithmVersion:(MPAlgorithmVersion)algorithmVersion {
|
||||
|
||||
if (algorithmVersion == self.algorithm.version)
|
||||
return;
|
||||
[self willChangeValueForKey:@"outdated"];
|
||||
self.algorithm = MPAlgorithmForVersion( algorithmVersion )?: self.algorithm;
|
||||
[self didChangeValueForKey:@"outdated"];
|
||||
|
||||
if (_entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
entity.algorithm = self.algorithm;
|
||||
[context saveToStore];
|
||||
|
||||
[self updateContent:entity];
|
||||
}];
|
||||
else
|
||||
[self updateContent];
|
||||
}
|
||||
|
||||
- (void)setQuestion:(NSString *)question {
|
||||
|
||||
if ([question isEqualToString:_question])
|
||||
return;
|
||||
_question = question;
|
||||
|
||||
[self updateContent];
|
||||
}
|
||||
|
||||
- (BOOL)outdated {
|
||||
|
||||
return self.algorithmVersion < MPAlgorithmVersionCurrent;
|
||||
}
|
||||
|
||||
- (BOOL)generated {
|
||||
|
||||
return self.type & MPSiteTypeClassGenerated;
|
||||
}
|
||||
|
||||
- (BOOL)stored {
|
||||
|
||||
return self.type & MPSiteTypeClassStored;
|
||||
}
|
||||
|
||||
- (BOOL)transient {
|
||||
|
||||
return _entityOID == nil;
|
||||
}
|
||||
|
||||
- (void)updateContent {
|
||||
|
||||
if (_entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
|
||||
}];
|
||||
|
||||
else
|
||||
PearlNotMainQueue( ^{
|
||||
[self updatePasswordWithResult:
|
||||
[self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self updateLoginNameWithResult:
|
||||
[self.algorithm generateLoginForSiteNamed:self.name
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self updateAnswerWithResult:
|
||||
[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)updateContent:(MPSiteEntity *)entity {
|
||||
|
||||
[entity resolvePasswordUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||
[self updatePasswordWithResult:result];
|
||||
}];
|
||||
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||
[self updateLoginNameWithResult:result];
|
||||
}];
|
||||
[self updateAnswerWithResult:[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
}
|
||||
|
||||
- (void)updatePasswordWithResult:(NSString *)result {
|
||||
|
||||
static NSRegularExpression *re_anyChar;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *displayResult = result;
|
||||
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
|
||||
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.content = result;
|
||||
self.displayedContent = displayResult;
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)updateLoginNameWithResult:(NSString *)loginName {
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.loginName = loginName;
|
||||
NSLog( @"%@: loginGenerated: %d, loginName: %@", self.name, self.loginGenerated, loginName );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)updateAnswerWithResult:(NSString *)answer {
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.answer = answer;
|
||||
} );
|
||||
}
|
||||
|
||||
@end
|
27
platform-ios/ObjC/Mac/MPSitesTableView.h
Normal file
27
platform-ios/ObjC/Mac/MPSitesTableView.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPSitesTableView.h
|
||||
// MPSitesTableView
|
||||
//
|
||||
// Created by lhunath on 2014-06-30.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class MPPasswordWindowController;
|
||||
|
||||
@interface MPSitesTableView : NSTableView
|
||||
|
||||
@property(nonatomic, weak) MPPasswordWindowController *controller;
|
||||
|
||||
@end
|
34
platform-ios/ObjC/Mac/MPSitesTableView.m
Normal file
34
platform-ios/ObjC/Mac/MPSitesTableView.m
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPSitesTableView.h
|
||||
// MPSitesTableView
|
||||
//
|
||||
// Created by lhunath on 2014-06-30.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSitesTableView.h"
|
||||
#import "MPPasswordWindowController.h"
|
||||
|
||||
@implementation MPSitesTableView
|
||||
|
||||
- (void)doCommandBySelector:(SEL)aSelector {
|
||||
|
||||
[self.controller handleCommand:aSelector];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)theEvent {
|
||||
|
||||
[self interpretKeyEvents:@[ theEvent ]];
|
||||
}
|
||||
|
||||
@end
|
109
platform-ios/ObjC/Mac/MasterPassword-Info.plist
Normal file
109
platform-ios/ObjC/Mac/MasterPassword-Info.plist
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ATSApplicationFontsPath</key>
|
||||
<string>.</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>[auto]</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>[auto]</string>
|
||||
<key>Fabric</key>
|
||||
<dict>
|
||||
<key>APIKey</key>
|
||||
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
|
||||
<key>Kits</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>KitInfo</key>
|
||||
<dict/>
|
||||
<key>KitName</key>
|
||||
<string>Crashlytics</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2011-2014 Lyndir</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>MPMacApplication</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.utf8-plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
<key>UTTypeSize320IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeSize64IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,366 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
DAD9B5D1176299B9001835F9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD9B5D0176299B9001835F9 /* main.m */; };
|
||||
DAD9B5EC1762AAA6001835F9 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EB1762AAA6001835F9 /* AppKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
DA3B84571915577F00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../../External/Mac/Crashlytics.framework; sourceTree = "<group>"; };
|
||||
DAD9B5C1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MasterPassword-Mac-LoginHelper.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAD9B5CC176299B9001835F9 /* MasterPassword-Mac-LoginHelper-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MasterPassword-Mac-LoginHelper-Info.plist"; sourceTree = "<group>"; };
|
||||
DAD9B5D0176299B9001835F9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
DAD9B5EA17629C56001835F9 /* MasterPassword-Mac-LoginHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "MasterPassword-Mac-LoginHelper.entitlements"; sourceTree = "<group>"; };
|
||||
DAD9B5EB1762AAA6001835F9 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
DAD9B5BE176299B9001835F9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAD9B5EC1762AAA6001835F9 /* AppKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
DAD9B5B8176299B9001835F9 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAD9B5CA176299B9001835F9 /* MasterPassword-Mac-LoginHelper */,
|
||||
DAD9B5C3176299B9001835F9 /* Frameworks */,
|
||||
DAD9B5C2176299B9001835F9 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAD9B5C2176299B9001835F9 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAD9B5C1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAD9B5C3176299B9001835F9 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA3B84571915577F00246EEA /* Crashlytics.framework */,
|
||||
DAD9B5EB1762AAA6001835F9 /* AppKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAD9B5CA176299B9001835F9 /* MasterPassword-Mac-LoginHelper */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAD9B5EA17629C56001835F9 /* MasterPassword-Mac-LoginHelper.entitlements */,
|
||||
DAD9B5CB176299B9001835F9 /* Supporting Files */,
|
||||
);
|
||||
path = "MasterPassword-Mac-LoginHelper";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAD9B5CB176299B9001835F9 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAD9B5CC176299B9001835F9 /* MasterPassword-Mac-LoginHelper-Info.plist */,
|
||||
DAD9B5D0176299B9001835F9 /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
DAD9B5C0176299B9001835F9 /* MasterPassword-Mac-LoginHelper */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DAD9B5DE176299B9001835F9 /* Build configuration list for PBXNativeTarget "MasterPassword-Mac-LoginHelper" */;
|
||||
buildPhases = (
|
||||
DAD9B5BD176299B9001835F9 /* Sources */,
|
||||
DAD9B5BE176299B9001835F9 /* Frameworks */,
|
||||
DAD9B5BF176299B9001835F9 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "MasterPassword-Mac-LoginHelper";
|
||||
productName = "MasterPassword-Mac-LoginHelper";
|
||||
productReference = DAD9B5C1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
DAD9B5B9176299B9001835F9 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = MP;
|
||||
LastUpgradeCheck = 0710;
|
||||
ORGANIZATIONNAME = "Maarten Billemont";
|
||||
TargetAttributes = {
|
||||
DAD9B5C0176299B9001835F9 = {
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = DAD9B5BC176299B9001835F9 /* Build configuration list for PBXProject "MasterPassword-Mac-LoginHelper" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = DAD9B5B8176299B9001835F9;
|
||||
productRefGroup = DAD9B5C2176299B9001835F9 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
DAD9B5C0176299B9001835F9 /* MasterPassword-Mac-LoginHelper */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
DAD9B5BF176299B9001835F9 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
DAD9B5BD176299B9001835F9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAD9B5D1176299B9001835F9 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
DA3B845A1916AC5100246EEA /* AppStore-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = "AppStore-Mac";
|
||||
};
|
||||
DA3B845B1916AC5100246EEA /* AppStore-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper.entitlements";
|
||||
CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac.LoginHelper;
|
||||
};
|
||||
name = "AppStore-Mac";
|
||||
};
|
||||
DAD9B5DC176299B9001835F9 /* Debug-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = "Debug-Mac";
|
||||
};
|
||||
DAD9B5DD176299B9001835F9 /* AdHoc-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = "AdHoc-Mac";
|
||||
};
|
||||
DAD9B5DF176299B9001835F9 /* Debug-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac.LoginHelper;
|
||||
};
|
||||
name = "Debug-Mac";
|
||||
};
|
||||
DAD9B5E0176299B9001835F9 /* AdHoc-Mac */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application: Maarten Billemont (HL3Q45LX9N)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MasterPassword-Mac-LoginHelper/MasterPassword-Mac-LoginHelper-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac.LoginHelper;
|
||||
};
|
||||
name = "AdHoc-Mac";
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
DAD9B5BC176299B9001835F9 /* Build configuration list for PBXProject "MasterPassword-Mac-LoginHelper" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DAD9B5DC176299B9001835F9 /* Debug-Mac */,
|
||||
DAD9B5DD176299B9001835F9 /* AdHoc-Mac */,
|
||||
DA3B845A1916AC5100246EEA /* AppStore-Mac */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = "AdHoc-Mac";
|
||||
};
|
||||
DAD9B5DE176299B9001835F9 /* Build configuration list for PBXNativeTarget "MasterPassword-Mac-LoginHelper" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DAD9B5DF176299B9001835F9 /* Debug-Mac */,
|
||||
DAD9B5E0176299B9001835F9 /* AdHoc-Mac */,
|
||||
DA3B845B1916AC5100246EEA /* AppStore-Mac */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = "AdHoc-Mac";
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = DAD9B5B9176299B9001835F9 /* Project object */;
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.Mac</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// main.m
|
||||
// MasterPassword-Mac-LoginHelper
|
||||
//
|
||||
// Created by Maarten Billemont on 2013-06-07.
|
||||
// Copyright (c) 2013 Maarten Billemont. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
NSURL *bundleURL = [[[[[[NSBundle mainBundle] bundleURL]
|
||||
URLByDeletingLastPathComponent] URLByDeletingLastPathComponent]
|
||||
URLByDeletingLastPathComponent] URLByDeletingLastPathComponent];
|
||||
|
||||
NSError *error = nil;
|
||||
NSRunningApplication *application = [[NSWorkspace sharedWorkspace]
|
||||
launchApplicationAtURL:bundleURL options:NSWorkspaceLaunchWithoutActivation
|
||||
configuration:@{} error:&error];
|
||||
|
||||
if (!application || error) {
|
||||
NSLog( @"Error launching main app: %@", [error debugDescription] );
|
||||
return (int)error.code?: 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
2985
platform-ios/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
Normal file
2985
platform-ios/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
7
platform-ios/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
platform-ios/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:MasterPassword-Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "AppStore-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "AppStore-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "AppStore-Mac"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "AppStore-Mac">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "AppStore-Mac"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "CA_DEBUG_TRANSACTIONS"
|
||||
value = "1"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug-Mac"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug-Mac">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "AdHoc-Mac"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA67743A1A474A03004F356A"
|
||||
BuildableName = "mpw-test"
|
||||
BlueprintName = "mpw-test"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA67743A1A474A03004F356A"
|
||||
BuildableName = "mpw-test"
|
||||
BlueprintName = "mpw-test"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug-Mac"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA67743A1A474A03004F356A"
|
||||
BuildableName = "mpw-test"
|
||||
BlueprintName = "mpw-test"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "AdHoc-Mac"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA67743A1A474A03004F356A"
|
||||
BuildableName = "mpw-test"
|
||||
BlueprintName = "mpw-test"
|
||||
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug-Mac">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "AdHoc-Mac"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
107
platform-ios/ObjC/Mac/MasterPassword-Prefix.pch
Normal file
107
platform-ios/ObjC/Mac/MasterPassword-Prefix.pch
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'MasterPassword' target in the 'MasterPassword' project
|
||||
//
|
||||
|
||||
#import <Availability.h>
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
|
||||
#warning "This project uses features only available in Mac OS X 10.8 and later."
|
||||
#endif
|
||||
|
||||
#import "Pearl-Prefix.pch"
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
#ifdef CRASHLYTICS
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
#endif
|
||||
|
||||
#import "MPTypes.h"
|
||||
#import "MPMacConfig.h"
|
||||
|
||||
#else
|
||||
|
||||
#import <libgen.h>
|
||||
#import <CoreFoundation/CFString.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <objc/NSObjCRuntime.h>
|
||||
#import <stdlib.h>
|
||||
|
||||
#define trc(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 0, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
} while (0)
|
||||
|
||||
#define dbg(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 1, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
} while (0)
|
||||
|
||||
#define inf(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 2, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
} while (0)
|
||||
|
||||
#define wrn(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 3, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
} while (0)
|
||||
|
||||
#define err(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 4, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
} while (0)
|
||||
|
||||
#define ftl(format, ...) \
|
||||
do { \
|
||||
char *_msg = NULL; \
|
||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 5, \
|
||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
16
platform-ios/ObjC/Mac/MasterPassword.entitlements
Normal file
16
platform-ios/ObjC/Mac/MasterPassword.entitlements
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.Mac</string>
|
||||
</array>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
1
platform-ios/ObjC/Mac/en.lproj/InfoPlist.strings
Normal file
1
platform-ios/ObjC/Mac/en.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1 @@
|
||||
/* Localized versions of Info.plist keys */
|
226
platform-ios/ObjC/Mac/en.lproj/MainMenu.xib
Normal file
226
platform-ios/ObjC/Mac/en.lproj/MainMenu.xib
Normal file
@@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="494" id="495"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<menu title="AMainMenu" systemMenu="main" id="29"/>
|
||||
<customObject id="494" customClass="MPMacAppDelegate">
|
||||
<connections>
|
||||
<outlet property="createUserItem" destination="757" id="763"/>
|
||||
<outlet property="deleteUserItem" destination="ZgZ-p2-463" id="smU-PF-mKA"/>
|
||||
<outlet property="hidePasswordsItem" destination="9G7-17-PzY" id="qPX-VT-jVx"/>
|
||||
<outlet property="lockItem" destination="720" id="726"/>
|
||||
<outlet property="openAtLoginItem" destination="785" id="788"/>
|
||||
<outlet property="rememberPasswordItem" destination="744" id="750"/>
|
||||
<outlet property="savePasswordItem" destination="747" id="751"/>
|
||||
<outlet property="showFullScreenItem" destination="Rvo-MK-GgF" id="NYi-85-Yuc"/>
|
||||
<outlet property="showItem" destination="719" id="783"/>
|
||||
<outlet property="statusMenu" destination="716" id="731"/>
|
||||
<outlet property="usersItem" destination="755" id="762"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<userDefaultsController representsSharedInstance="YES" id="548"/>
|
||||
<menu autoenablesItems="NO" id="716">
|
||||
<items>
|
||||
<menuItem title="Users" id="755">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Users" id="756">
|
||||
<items>
|
||||
<menuItem title="New User" enabled="NO" toolTip="Creating users is not yet supported. Please use the iOS app with iCloud enabled to create users and sites." id="757">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="newUser:" target="494" id="761"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete User" enabled="NO" toolTip="Creating users is not yet supported. Please use the iOS app with iCloud enabled to create users and sites." id="ZgZ-p2-463">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="deleteUser:" target="494" id="eia-X5-QMc"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="759"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Preferences" id="739">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Preferences" autoenablesItems="NO" id="742">
|
||||
<items>
|
||||
<menuItem title="Open At Login" id="785">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="494" id="787"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Always open Master Password at start-up." enabled="NO" id="786">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Always open Master Password at start-up.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Show Full Screen" id="Rvo-MK-GgF">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="494" id="wfI-yQ-Kla"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Claim the entire screen while looking up passwords." enabled="NO" id="3k1-4V-IZ7">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Always open Master Password at start-up.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Passwords" id="9G7-17-PzY">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="494" id="xgz-pN-csV"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Don't display the password. Hold alt ⌥ to reveal temporarily." enabled="NO" id="HnK-hQ-cM9">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Don't display the password. Hold alt ">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="⌥">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="LucidaGrande"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content=" to reveal temporarily.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Remember Password" id="744">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="494" id="753"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Remember the password while the application is running." enabled="NO" id="Avu-uY-2Rh">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Remember the password while the application is running.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Save Password" id="747">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="togglePreference:" target="494" id="754"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save the password in your keychain so you don't need to enter it again." enabled="NO" id="748">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Save the password in your keychain so you don't need to enter it again.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Export Sites" id="r1P-hr-mh5">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Export Sites" id="fnk-gK-yCi">
|
||||
<items>
|
||||
<menuItem title="Secure Export" id="06i-og-eLt">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Your passwords are not visible." enabled="NO" id="ybY-P3-eao">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Your passwords are not visible.">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Reveal Passwords" id="fMG-TT-bTn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Keep this file secure or delete it when you're done with it!" enabled="NO" id="cQu-oR-SUa">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Keep this file secure or delete it when you're done with it!">
|
||||
<attributes>
|
||||
<font key="NSFont" size="12" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Import Sites..." id="EFK-zt-EvJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="importSites:" target="494" id="CNv-4j-036"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="718"/>
|
||||
<menuItem title="Open" keyEquivalent="p" id="719">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="showPasswordWindow:" target="494" id="782"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Lock" enabled="NO" keyEquivalent="p" id="720">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="lock:" target="494" id="764"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Quit" keyEquivalent="q" id="717">
|
||||
<connections>
|
||||
<action selector="terminate:" target="494" id="784"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</objects>
|
||||
</document>
|
12
platform-ios/ObjC/Mac/main.m
Normal file
12
platform-ios/ObjC/Mac/main.m
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// main.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 04/03/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
return NSApplicationMain( argc, (const char **)argv );
|
||||
}
|
Reference in New Issue
Block a user