diff --git a/MacPass/Base.lproj/IconSelection.xib b/MacPass/Base.lproj/IconSelection.xib index 1dd090a9..4c9743e8 100644 --- a/MacPass/Base.lproj/IconSelection.xib +++ b/MacPass/Base.lproj/IconSelection.xib @@ -1,8 +1,8 @@ - - + + - - + + @@ -26,31 +26,24 @@ - - - - + @@ -125,11 +140,13 @@ DQ + + - + diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index d509949b..97eae9a1 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -151,7 +151,7 @@ FOUNDATION_EXPORT NSString *const MPDocumentGroupKey; - (void)writeXMLToURL:(NSURL *)url; - (void)readXMLfromURL:(NSURL *)url; -- (void)mergeWithContentsFromURL:(NSURL *)url; +- (void)mergeWithContentsFromURL:(NSURL *)url key:(KPKCompositeKey *)key; /* Undoable Intiialization of elements */ - (KPKGroup *)createGroup:(KPKGroup *)parent; diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 0b651306..7deee47a 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -33,6 +33,7 @@ #import "MPTreeDelegate.h" #import "MPTargetNodeResolving.h" #import "MPErrorRecoveryAttempter.h" +#import "MPPasswordInputController.h" #import "KeePassKit/KeePassKit.h" @@ -334,7 +335,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGrou } if(strategy == MPFileChangeStrategyMerge) { - [self mergeWithContentsFromURL:self.fileURL]; + [self mergeWithContentsFromURL:self.fileURL key:self.compositeKey]; } else if(strategy == MPFileChangeStrategyUseOther) { [self revertToContentsOfURL:self.fileURL ofType:self.fileType error:nil]; @@ -357,29 +358,53 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGrou self.encryptedData = nil; } -- (void)mergeWithContentsFromURL:(NSURL *)url { - /* TODO read file to check what format to use */ +- (void)mergeWithContentsFromURL:(NSURL *)url key:(KPKCompositeKey *)key { NSError *error; - KPKTree *otherTree = [[KPKTree alloc] initWithContentsOfUrl:url key:self.compositeKey error:&error]; - if(!otherTree) { - if(error.code == KPKErrorPasswordAndOrKeyfileWrong) { - NSAlert *alert = [[NSAlert alloc] init]; - alert.alertStyle = NSAlertStyleWarning; - alert.informativeText = NSLocalizedString(@"ALERT_MERGE_OTHER_KEY_CHANGED", @""); - [self presentError:error]; - } - else { - [self presentError:error]; - } + KPKTree *otherTree; + + if(key) { + otherTree = [[KPKTree alloc] initWithContentsOfUrl:url key:key error:&error]; } - else { + + if(otherTree) { [self.tree syncronizeWithTree:otherTree options:KPKSynchronizationSynchronizeOption]; + /* the key might have changed so update ours! */ + //self.compositeKey = key; NSUserNotification *notification = [[NSUserNotification alloc] init]; notification.title = NSApp.applicationName; notification.informativeText = NSLocalizedString(@"AUTO_MERGE_NOTIFICATION_TEXT", @""); notification.deliveryDate = NSDate.date; [NSUserNotificationCenter.defaultUserNotificationCenter scheduleNotification:notification]; - + } + else { + if(!key || error.code == KPKErrorPasswordAndOrKeyfileWrong) { + NSWindow *sheet = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) + styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + MPPasswordInputController *passwordInputController = [[MPPasswordInputController alloc] init]; + __weak MPDocument *welf = self; + [passwordInputController requestPasswordWithMessage:NSLocalizedString(@"EXTERN_CHANGE_OF_MASTERKEY", @"The master key was changed by an extrenal programm!") + cancelLabel:NSLocalizedString(@"ABORT_MERGE_KEEP_MINE", @"Button label to abort a merge on a file with changed master key!") + completionHandler:^BOOL(NSString *password, NSURL *keyURL, BOOL didCancel, NSError *__autoreleasing *error) { + [welf.windowForSheet endSheet:sheet returnCode:(didCancel ? NSModalResponseCancel : NSModalResponseOK)]; + if(!didCancel) { + KPKCompositeKey *compositeKey = [[KPKCompositeKey alloc] initWithPassword:password key:keyURL]; + [welf mergeWithContentsFromURL:url key:compositeKey]; + } + // just return yes regardless since we will display the sheet again if needed! + return YES; + }]; + sheet.contentViewController = passwordInputController; + [self.windowForSheet beginSheet:sheet completionHandler:^(NSModalResponse returnCode) { /* nothing to do, rest is done in other handler! */ }]; + } + else { + /* unable to merge */ + NSAlert *alert = [NSAlert alertWithError:error]; + [alert beginSheetModalForWindow:self.windowForSheet completionHandler:^(NSModalResponse returnCode) { + // do nothing; + }]; + } } } diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 908bd3ea..aecd7192 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -305,7 +305,7 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword); //openPanel.allowedFileTypes = @[(id)kUTTypeXML]; [openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) { if(result == NSFileHandlingPanelOKButton) { - [document mergeWithContentsFromURL:openPanel.URL]; + [document mergeWithContentsFromURL:openPanel.URL key:document.compositeKey]; } }]; } @@ -324,7 +324,10 @@ typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword); } [self _setContentViewController:self.passwordInputController]; __weak MPDocumentWindowController *welf = self; - [self.passwordInputController requestPasswordWithCompletionHandler:^BOOL(NSString *password, NSURL *keyURL, NSError *__autoreleasing *error) { + [self.passwordInputController requestPasswordWithCompletionHandler:^BOOL(NSString *password, NSURL *keyURL, BOOL cancel, NSError *__autoreleasing *error) { + if(cancel) { + return NO; + } return [((MPDocument *)welf.document) unlockWithPassword:password keyFileURL:keyURL error:error]; }]; } diff --git a/MacPass/MPPasswordInputController.h b/MacPass/MPPasswordInputController.h index 10f4cb0e..b89425c0 100644 --- a/MacPass/MPPasswordInputController.h +++ b/MacPass/MPPasswordInputController.h @@ -12,8 +12,10 @@ @interface MPPasswordInputController : MPViewController -typedef BOOL (^passwordInputCompletionBlock)(NSString *password, NSURL *keyURL, NSError *__autoreleasing*error); +typedef BOOL (^passwordInputCompletionBlock)(NSString *password, NSURL *keyURL, BOOL didCancel, NSError *__autoreleasing*error); - (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler; +- (void)requestPasswordWithMessage:(NSString *)message cancelLabel:(NSString *)cancelLabel completionHandler:(passwordInputCompletionBlock)completionHandler; + @end diff --git a/MacPass/MPPasswordInputController.m b/MacPass/MPPasswordInputController.m index 9960f83f..4285860c 100644 --- a/MacPass/MPPasswordInputController.m +++ b/MacPass/MPPasswordInputController.m @@ -26,6 +26,11 @@ @property (weak) IBOutlet NSTextField *errorInfoTextField; @property (weak) IBOutlet NSButton *togglePasswordButton; @property (weak) IBOutlet NSButton *enablePasswordCheckBox; +@property (weak) IBOutlet NSButton *unlockButton; +@property (weak) IBOutlet NSButton *cancelButton; + +@property (copy) NSString *message; +@property (copy) NSString *cancelLabel; @property (assign) BOOL showPassword; @property (nonatomic, assign) BOOL enablePassword; @@ -66,11 +71,17 @@ return self.passwordTextField; } -- (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler { +- (void)requestPasswordWithMessage:(NSString *)message cancelLabel:(NSString *)cancelLabel completionHandler:(passwordInputCompletionBlock)completionHandler { self.completionHandler = completionHandler; + self.message = message; + self.cancelLabel = cancelLabel; [self _reset]; } +- (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler { + [self requestPasswordWithMessage:nil cancelLabel:nil completionHandler:completionHandler]; +} + #pragma mark Properties - (void)setEnablePassword:(BOOL)enablePassword { if(_enablePassword != enablePassword) { @@ -87,14 +98,24 @@ #pragma mark - #pragma mark Private - (IBAction)_submit:(id)sender { - if(self.completionHandler) { - /* No password is different than an empty password */ - NSError *error = nil; - NSString *password = self.enablePassword ? self.passwordTextField.stringValue : nil; - if(!self.completionHandler(password, self.keyPathControl.URL, &error)) { - [self _showError:error]; - [self.view.window shakeWindow:nil]; - } + + if(!self.completionHandler) { + return; + } + + /* No password is different than an empty password */ + NSError *error = nil; + NSString *password = self.enablePassword ? self.passwordTextField.stringValue : nil; + + BOOL cancel = (sender == self.cancelButton); + BOOL result = self.completionHandler(password, self.keyPathControl.URL, cancel, &error); + if(cancel || result) { + return; + } + [self _showError:error]; + /* do not shake if we are a sheet */ + if(!self.view.window.isSheet) { + [self.view.window shakeWindow:nil]; } } @@ -112,8 +133,15 @@ self.showPassword = NO; self.enablePassword = YES; self.passwordTextField.stringValue = @""; - self.errorInfoTextField.hidden = YES; - self.errorImageView.hidden = YES; + self.errorInfoTextField.hidden = (nil == self.message); + if(self.message) { + self.errorInfoTextField.stringValue = self.message; + } + self.errorImageView.hidden = (nil == self.message);; + self.cancelButton.hidden = (nil == self.cancelLabel); + if(self.cancelLabel) { + self.cancelButton.stringValue = self.cancelLabel; + } [self resetKeyFile:self]; }