2
0

Cloud and initial window improvements.

[ADDED]     Advanced option to mark self as corrupt.
[ADDED]     Tell user initial cloud sync can take a moment.
[IMPROVED]  Initial window now visible on full-screen spaces.
[FIXED]     User name label.
This commit is contained in:
Maarten Billemont 2014-02-22 18:27:14 -05:00
parent 3fa9843855
commit 658d710847
17 changed files with 163 additions and 97 deletions

View File

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>Crashlytics</string> <string>Crashlytics</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.crashlytics.sdk.mac</string> <string>com.crashlytics.ios</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
@ -15,16 +15,16 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.1.2</string> <string>2.1.6</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>macosx</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>9</string> <string>21</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>macosx</string> <string>iphoneos</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.6</string> <string>4.0</string>
</dict> </dict>
</plist> </plist>

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 4a3c68dda957adba490aae9b56df018e935d6c84 Subproject commit 081c2dec20b3638694a5ad20cd2fddccdb298447

@ -1 +1 @@
Subproject commit a5ee62e1cca559e27aba92ac65ffc4da44827dfe Subproject commit ef9aa1c29ed6e1729be4d6a3dd65e2c8a289bf4c

View File

@ -127,7 +127,7 @@
return @"Device Private Password"; return @"Device Private Password";
} }
Throw(@"Type not supported: %lu", type); Throw(@"Type not supported: %lu", (long)type);
} }
- (NSString *)shortNameOfType:(MPElementType)type { - (NSString *)shortNameOfType:(MPElementType)type {
@ -161,7 +161,7 @@
return @"Device"; return @"Device";
} }
Throw(@"Type not supported: %lu", type); Throw(@"Type not supported: %lu", (long)type);
} }
- (NSString *)classNameOfType:(MPElementType)type { - (NSString *)classNameOfType:(MPElementType)type {
@ -200,7 +200,7 @@
return [MPElementStoredEntity class]; return [MPElementStoredEntity class];
} }
Throw(@"Type not supported: %lu", type); Throw(@"Type not supported: %lu", (long)type);
} }
- (MPElementType)nextType:(MPElementType)type { - (MPElementType)nextType:(MPElementType)type {
@ -227,7 +227,7 @@
return MPElementTypeStoredPersonal; return MPElementTypeStoredPersonal;
} }
Throw(@"Type not supported: %lu", type); Throw(@"Type not supported: %lu", (long)type);
} }
- (MPElementType)previousType:(MPElementType)type { - (MPElementType)previousType:(MPElementType)type {
@ -300,13 +300,13 @@
case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPElementTypeGeneratedShort:
case MPElementTypeGeneratedPIN: { case MPElementTypeGeneratedPIN: {
NSAssert(NO, @"Cannot save content to element with generated type %lu.", element.type); NSAssert(NO, @"Cannot save content to element with generated type %lu.", (long)element.type);
break; break;
} }
case MPElementTypeStoredPersonal: { case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES]; encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
@ -315,7 +315,7 @@
} }
case MPElementTypeStoredDevicePrivate: { case MPElementTypeStoredDevicePrivate: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES]; encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
@ -360,7 +360,7 @@
case MPElementTypeGeneratedShort: case MPElementTypeGeneratedShort:
case MPElementTypeGeneratedPIN: { case MPElementTypeGeneratedPIN: {
NSAssert([element isKindOfClass:[MPElementGeneratedEntity class]], NSAssert([element isKindOfClass:[MPElementGeneratedEntity class]],
@"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.", element.type, [element class]); @"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.", (long)element.type, [element class]);
NSString *name = element.name; NSString *name = element.name;
MPElementType type = element.type; MPElementType type = element.type;
@ -382,7 +382,7 @@
case MPElementTypeStoredPersonal: { case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject; NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject;
@ -394,7 +394,7 @@
} }
case MPElementTypeStoredDevicePrivate: { case MPElementTypeStoredDevicePrivate: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name]; NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery]; NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery];
@ -423,7 +423,7 @@
case MPElementTypeStoredPersonal: { case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
if ([importKey.keyID isEqualToData:elementKey.keyID]) if ([importKey.keyID isEqualToData:elementKey.keyID])
((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64]; ((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64];
@ -481,7 +481,7 @@
case MPElementTypeStoredPersonal: { case MPElementTypeStoredPersonal: {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], NSAssert([element isKindOfClass:[MPElementStoredEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", element.type, [element class]); @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, [element class]);
result = [((MPElementStoredEntity *)element).contentObject encodeBase64]; result = [((MPElementStoredEntity *)element).contentObject encodeBase64];
break; break;
} }

View File

@ -75,6 +75,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if (password) if (password)
NSAssert(![NSThread isMainThread], @"Computing key must not happen from the main thread."); NSAssert(![NSThread isMainThread], @"Computing key must not happen from the main thread.");
if (!user)
return NO;
MPKey *tryKey = nil; MPKey *tryKey = nil;

View File

@ -701,7 +701,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n", [export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses, [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
[PearlString( @"%lu:%lu", type, (unsigned long)version ) UTF8String], [name UTF8String], content [PearlString( @"%lu:%lu", (long)type, (unsigned long)version ) UTF8String], [name UTF8String], content
? content: @""]; ? content: @""];
} }

View File

@ -54,7 +54,7 @@
if (!aType || aType == (MPElementType)NSNotFound) if (!aType || aType == (MPElementType)NSNotFound)
aType = MPElementTypeGeneratedLong; aType = MPElementTypeGeneratedLong;
if (![self isKindOfClass:[self.algorithm classOfType:aType]]) if (![self isKindOfClass:[self.algorithm classOfType:aType]])
Throw(@"This object's class does not support the type: %lu", aType); Throw(@"This object's class does not support the type: %lu", (long)aType);
self.type_ = @(aType); self.type_ = @(aType);
} }
@ -128,7 +128,7 @@
- (NSString *)debugDescription { - (NSString *)debugDescription {
return PearlString( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}", return PearlString( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
NSStringFromClass( [self class] ), self.name, self.user.name, self.type, (long)self.uses, self.lastUsed, (long)self.version, NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed, (long)self.version,
self.loginName, self.requiresExplicitMigration ); self.loginName, self.requiresExplicitMigration );
} }

View File

@ -16,6 +16,7 @@
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import <Foundation/Foundation.h>
#import "MPElementCollectionView.h" #import "MPElementCollectionView.h"
#import "MPElementModel.h" #import "MPElementModel.h"
#import "MPMacAppDelegate.h" #import "MPMacAppDelegate.h"
@ -36,17 +37,24 @@
if (!(self = [super initWithCoder:coder])) if (!(self = [super initWithCoder:coder]))
return nil; return nil;
__weak MPElementCollectionView *wSelf = self; [self addObserver:self forKeyPath:@"representedObject" options:0 context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
wSelf.counterHidden = !(MPElementTypeClassGenerated & wSelf.representedObject.type);
wSelf.updateContentHidden = !(MPElementTypeClassStored & wSelf.representedObject.type);
} );
} forKeyPath:@"representedObject" options:0 context:nil];
return self; return self;
} }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.counterHidden = !(MPElementTypeClassGenerated & self.representedObject.type);
self.updateContentHidden = !(MPElementTypeClassStored & self.representedObject.type);
}];
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"representedObject"];
}
- (IBAction)toggleType:(id)sender { - (IBAction)toggleType:(id)sender {
id<MPAlgorithm> algorithm = self.representedObject.algorithm; id<MPAlgorithm> algorithm = self.representedObject.algorithm;

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B3116" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B3116" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies> </dependencies>
<objects> <objects>

View File

@ -34,6 +34,7 @@
- (IBAction)newUser:(NSMenuItem *)sender; - (IBAction)newUser:(NSMenuItem *)sender;
- (IBAction)lock:(id)sender; - (IBAction)lock:(id)sender;
- (IBAction)rebuildCloud:(id)sender; - (IBAction)rebuildCloud:(id)sender;
- (IBAction)corruptCloud:(id)sender;
- (IBAction)terminate:(id)sender; - (IBAction)terminate:(id)sender;
- (IBAction)iphoneAppStore:(id)sender; - (IBAction)iphoneAppStore:(id)sender;

View File

@ -14,6 +14,12 @@
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper" #define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
@interface UbiquityStoreManager (Private)
- (void)markCloudStoreCorrupted;
@end
@interface MPMacAppDelegate() @interface MPMacAppDelegate()
@property(nonatomic, strong) NSWindowController *initialWindow; @property(nonatomic, strong) NSWindowController *initialWindow;
@ -129,16 +135,23 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(id)sender { - (IBAction)togglePreference:(id)sender {
if (sender == self.enableCloudButton) if (sender == self.enableCloudButton) {
[self storeManager].cloudEnabled = (self.enableCloudButton.state == NSOnState); if (([self storeManager].cloudEnabled = self.enableCloudButton.state == NSOnState)) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"iCloud Enabled";
alert.informativeText = @"If you already have a user on another iCloud-enabled device, "
@"it may take a moment for that user to sync down to this device.";
[alert runModal];
}
}
if (sender == self.useCloudItem) if (sender == self.useCloudItem)
[self storeManager].cloudEnabled = !(self.useCloudItem.state == NSOnState); [self storeManager].cloudEnabled = self.useCloudItem.state != NSOnState;
if (sender == self.rememberPasswordItem) if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]]; [MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
if (sender == self.openAtLoginButton) if (sender == self.openAtLoginButton)
[self setLoginItemEnabled:(self.openAtLoginButton.state == NSOnState)]; [self setLoginItemEnabled:self.openAtLoginButton.state == NSOnState];
if (sender == self.openAtLoginItem) if (sender == self.openAtLoginItem)
[self setLoginItemEnabled:!(self.openAtLoginItem.state == NSOnState)]; [self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
if (sender == self.savePasswordItem) { if (sender == self.savePasswordItem) {
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context]; MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
@ -194,14 +207,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)rebuildCloud:(id)sender { - (IBAction)rebuildCloud:(id)sender {
if ([[NSAlert alertWithMessageText:@"iCloud Truth Sync" defaultButton:@"Continue" if ([[NSAlert alertWithMessageText:@"iCloud Truth Push" defaultButton:@"Continue"
alternateButton:nil otherButton:@"Cancel" alternateButton:nil otherButton:@"Cancel"
informativeTextWithFormat:@"This action will force all your iCloud enabled devices to revert to this device's version of the truth." informativeTextWithFormat:@"This action will force all your iCloud enabled devices to switch to this device's version of the truth."
@"\n\nThis is only necessary if you notice that your devices aren't syncing properly anymore. " @"\n\nThis is only necessary if you notice that your devices aren't syncing properly anymore. "
"Any data on other devices not available from here will be lost."] runModal] == NSAlertDefaultReturn) "Any data on other devices not available from here will be lost."] runModal] == NSAlertDefaultReturn)
[self.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:NO]; [self.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:NO];
} }
- (IBAction)corruptCloud:(id)sender {
if ([[NSAlert alertWithMessageText:@"iCloud Truth Pull" defaultButton:@"Continue"
alternateButton:nil otherButton:@"Cancel"
informativeTextWithFormat:@"This action will force another iCloud enabled device to push their version of the truth on all."
@"\n\nThis is only necessary if you notice that your devices aren't syncing properly anymore. "
"Any data on this device not available from the other will be lost."] runModal] == NSAlertDefaultReturn)
[self.storeManager markCloudStoreCorrupted];
}
- (IBAction)terminate:(id)sender { - (IBAction)terminate:(id)sender {
[self.passwordWindow close]; [self.passwordWindow close];
@ -301,8 +324,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
// Initial display. // Initial display.
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
if ([[MPMacConfig get].firstRun boolValue]) if ([[MPMacConfig get].firstRun boolValue]) {
[self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self] showWindow:self]; self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self];
[self.initialWindow.window setLevel:NSFloatingWindowLevel];
[self.initialWindow showWindow:self];
}
} }
- (void)setActiveUser:(MPUserEntity *)activeUser { - (void)setActiveUser:(MPUserEntity *)activeUser {

View File

@ -44,7 +44,6 @@
self.backgroundQueue = [NSOperationQueue new]; self.backgroundQueue = [NSOperationQueue new];
self.backgroundQueue.maxConcurrentOperationCount = 1; self.backgroundQueue.maxConcurrentOperationCount = 1;
[self.userLabel setStringValue:PearlString( @"%@'s password for:", [[MPMacAppDelegate get] activeUserForMainThread].name )];
[[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { // [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
// if (![MPAlgorithmDefault migrateUser:[[MPMacAppDelegate get] activeUserInContext:moc]]) // if (![MPAlgorithmDefault migrateUser:[[MPMacAppDelegate get] activeUserInContext:moc]])
@ -54,9 +53,9 @@
// @"their passwords to change. You'll need to update your profile for that site with the new password."]; // @"their passwords to change. You'll need to update your profile for that site with the new password."];
// [moc saveToStore]; // [moc saveToStore];
// }]; // }];
dispatch_async( dispatch_get_main_queue(), ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES];
} ); }];
} forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil]; } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
queue:[NSOperationQueue mainQueue] usingBlock: queue:[NSOperationQueue mainQueue] usingBlock:
@ -77,11 +76,18 @@
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock: queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
self.userLabel.stringValue = @"";
self.siteField.stringValue = @""; self.siteField.stringValue = @"";
self.elements = nil; self.elements = nil;
[self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
self.userLabel.stringValue = PearlString( @"%@'s password for:",
[[MPMacAppDelegate get] activeUserForMainThread].name );
}];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock: queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
@ -107,37 +113,7 @@
} }
if (contextInfo == MPAlertUnlockMP) { if (contextInfo == MPAlertUnlockMP) {
switch (returnCode) { switch (returnCode) {
case NSAlertAlternateReturn: { case NSAlertFirstButtonReturn: {
// "Change" button.
NSInteger returnCode_ = [[NSAlert
alertWithMessageText:@"Changing Master Password" defaultButton:nil
alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil informativeTextWithFormat:
@"This will allow you to log in with a different master password.\n\n"
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."]
runModal];
if (returnCode_ == NSAlertDefaultReturn) {
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
activeUser.keyID = nil;
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPMacAppDelegate get] signOutAnimated:YES];
[context saveToStore];
}];
}
break;
}
case NSAlertOtherReturn: {
// "Cancel" button.
[self close];
break;
}
case NSAlertDefaultReturn: {
// "Unlock" button. // "Unlock" button.
self.contentContainer.alphaValue = 0; self.contentContainer.alphaValue = 0;
self.inProgress = YES; self.inProgress = YES;
@ -150,7 +126,7 @@
usingMasterPassword:password]; usingMasterPassword:password];
self.inProgress = NO; self.inProgress = NO;
dispatch_async( dispatch_get_current_queue(), ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (success) if (success)
self.contentContainer.alphaValue = 1; self.contentContainer.alphaValue = 1;
else { else {
@ -159,11 +135,41 @@
}]] beginSheetModalForWindow:self.window modalDelegate:self }]] beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP]; didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP];
} }
} ); }];
}]; }];
break; break;
} }
case NSAlertSecondButtonReturn: {
// "Change" button.
NSAlert *alert_ = [NSAlert new];
[alert_ addButtonWithTitle:@"Update"];
[alert_ addButtonWithTitle:@"Cancel"];
[alert_ setMessageText:@"Changing Master Password"];
[alert_ setInformativeText:@"This will allow you to log in with a different master password.\n\n"
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."];
if ([alert_ runModal] == NSAlertFirstButtonReturn) {
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
activeUser.keyID = nil;
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPMacAppDelegate get] signOutAnimated:YES];
[context saveToStore];
}];
}
break;
}
case NSAlertThirdButtonReturn: {
// "Cancel" button.
[self close];
break;
}
default: default:
break; break;
} }
@ -304,16 +310,19 @@
[self.siteField setStringValue:@""]; [self.siteField setStringValue:@""];
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked." NSAlert *alert = [NSAlert new];
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel" [alert addButtonWithTitle:@"Unlock"];
informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@", [alert addButtonWithTitle:@"Change"];
userName]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Master Password is locked."];
[alert setInformativeText:PearlString( @"The master password is required to unlock the application for:\n\n%@", userName )];
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert setAccessoryView:passwordField]; [alert setAccessoryView:passwordField];
[alert layout]; [alert layout];
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP]; didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
[passwordField becomeFirstResponder];
}]; }];
}]; }];
@ -413,9 +422,11 @@
- (void)createNewSite:(NSString *)siteName { - (void)createNewSite:(NSString *)siteName {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSAlert *alert = [NSAlert alertWithMessageText:@"Create site?" NSAlert *alert = [NSAlert new];
defaultButton:@"Create" alternateButton:nil otherButton:@"Cancel" [alert addButtonWithTitle:@"Create"];
informativeTextWithFormat:@"Do you want to create a new site named:\n\n%@", siteName]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Create site?"];
[alert setInformativeText:PearlString( @"Do you want to create a new site named:\n\n%@", siteName )];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertCreateSite]; didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertCreateSite];
}]; }];

View File

@ -10,8 +10,8 @@
<string>Master Password</string> <string>Master Password</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>MasterPassword</string> <string>MasterPassword</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword.Mac</string> <string>com.lyndir.lhunath.MasterPassword.Mac</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>

View File

@ -1982,7 +1982,7 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "2A46D0C9-E5F0-4C52-BCC6-96434A0D1C1B";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}"; WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}";
}; };

View File

@ -140,13 +140,30 @@
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Advanced" id="777"> <menu key="submenu" title="Advanced" id="777">
<items> <items>
<menuItem title="iCloud Truth Sync" id="778"> <menuItem title="iCloud Truth Push" id="778">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="rebuildCloud:" target="494" id="780"/> <action selector="rebuildCloud:" target="494" id="780"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Force this device's version of the truth upon all others." enabled="NO" id="779"> <menuItem title="Force our version of the truth upon all other devices." enabled="NO" id="779">
<attributedString key="attributedTitle">
<fragment content="Force this device's version of the truth upon all others.">
<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="iCloud Truth Pull" id="cLQ-kc-cYN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="corruptCloud:" target="494" id="asr-sb-Zkz"/>
</connections>
</menuItem>
<menuItem title="Mark ourselves as corrupt and pull the truth from another." enabled="NO" id="6NL-ki-Jff">
<attributedString key="attributedTitle"> <attributedString key="attributedTitle">
<fragment content="Force this device's version of the truth upon all others."> <fragment content="Force this device's version of the truth upon all others.">
<attributes> <attributes>
@ -164,7 +181,7 @@
</menu> </menu>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="718"/> <menuItem isSeparatorItem="YES" id="718"/>
<menuItem title="Show" keyEquivalent="p" id="719"> <menuItem title="Open" keyEquivalent="p" id="719">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections> <connections>
<action selector="showPasswordWindow:" target="494" id="782"/> <action selector="showPasswordWindow:" target="494" id="782"/>

View File

@ -15,11 +15,11 @@
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3956915634581E737B38C /* PearlNavigationController.m */; }; 93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3956915634581E737B38C /* PearlNavigationController.m */; };
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; }; 93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; };
93D397952F5635C793C24DF1 /* NSError+MPFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+MPFullDescription.m */; }; 93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */; };
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; }; 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; };
93D39B842AB9A5D072810D76 /* NSError+MPFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+MPFullDescription.h */; }; 93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; }; 93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
@ -499,7 +499,7 @@
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; }; 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; };
93D3983278751A530262F64E /* LLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLConfig.h; sourceTree = "<group>"; }; 93D3983278751A530262F64E /* LLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLConfig.h; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; }; 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D398C95847261903D781D3 /* NSError+MPFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+MPFullDescription.h"; sourceTree = "<group>"; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; }; 93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; };
93D39A3CC4D8330831FC8CB4 /* LLToggleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLToggleViewController.h; sourceTree = "<group>"; }; 93D39A3CC4D8330831FC8CB4 /* LLToggleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLToggleViewController.h; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
@ -507,7 +507,7 @@
93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLButtonView.m; sourceTree = "<group>"; }; 93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLButtonView.m; sourceTree = "<group>"; };
93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = "<group>"; }; 93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = "<group>"; };
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
93D39F9106F2CCFB94283188 /* NSError+MPFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+MPFullDescription.m"; sourceTree = "<group>"; }; 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PearlMutableInfo.h"; sourceTree = "<group>"; }; DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PearlMutableInfo.h"; sourceTree = "<group>"; };
DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+PearlMutableInfo.m"; sourceTree = "<group>"; }; DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+PearlMutableInfo.m"; sourceTree = "<group>"; };
@ -2937,8 +2937,8 @@
DAFE45F515039823003ABA7C /* PearlStringUtils.m */, DAFE45F515039823003ABA7C /* PearlStringUtils.m */,
DAFE45F815039823003ABA7C /* README */, DAFE45F815039823003ABA7C /* README */,
DAFE45F915039823003ABA7C /* Resources */, DAFE45F915039823003ABA7C /* Resources */,
93D39F9106F2CCFB94283188 /* NSError+MPFullDescription.m */, 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */,
93D398C95847261903D781D3 /* NSError+MPFullDescription.h */, 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */,
); );
path = Pearl; path = Pearl;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3202,7 +3202,7 @@
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */, 93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */,
DAEB937518AA537D000490CC /* ssl.h in Headers */, DAEB937518AA537D000490CC /* ssl.h in Headers */,
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */, 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */,
93D39B842AB9A5D072810D76 /* NSError+MPFullDescription.h in Headers */, 93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */,
DAEB936218AA537D000490CC /* obj_mac.h in Headers */, DAEB936218AA537D000490CC /* obj_mac.h in Headers */,
DAEB934218AA537D000490CC /* bn.h in Headers */, DAEB934218AA537D000490CC /* bn.h in Headers */,
); );
@ -3854,7 +3854,7 @@
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */, 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */,
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */, 93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */,
93D397952F5635C793C24DF1 /* NSError+MPFullDescription.m in Sources */, 93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };