diff --git a/KeePassKit b/KeePassKit index f17f8765..81be5c49 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit f17f876537d88e9c063b1d6829714214987ccc0b +Subproject commit 81be5c4921a278b3e51eea0f591595d96b0e5f05 diff --git a/MacPass/MPConstants.h b/MacPass/MPConstants.h index d3134db3..a20c094b 100644 --- a/MacPass/MPConstants.h +++ b/MacPass/MPConstants.h @@ -13,5 +13,8 @@ FOUNDATION_EXPORT NSString *const MPPasteBoardType; FOUNDATION_EXPORT NSString *const MPErrorDomain; +FOUNDATION_EXPORT NSString *const MPLegacyDocumentUTI; +FOUNDATION_EXPORT NSString *const MPXMLDocumentUTI; + #endif diff --git a/MacPass/MPConstants.m b/MacPass/MPConstants.m index 37cad268..ba724976 100644 --- a/MacPass/MPConstants.m +++ b/MacPass/MPConstants.m @@ -8,5 +8,7 @@ #import "MPConstants.h" -NSString *const MPPasteBoardType = @"com.hicknhack.macpass.pasteboard"; -NSString *const MPErrorDomain = @"com.hicknhack.macpass.error"; +NSString *const MPPasteBoardType = @"com.hicknhack.macpass.pasteboard"; +NSString *const MPErrorDomain = @"com.hicknhack.macpass.error"; +NSString *const MPLegacyDocumentUTI = @"com.hicknhack.macpass.kdb"; +NSString *const MPXMLDocumentUTI = @"com.hicknhack.macpass.kdbx"; \ No newline at end of file diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index 85b00e48..fdc0537b 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -7,8 +7,7 @@ // #import -#import "MPDatabaseVersion.h" - +#import "KPKVersion.h" APPKIT_EXTERN NSString *const MPDocumentDidAddGroupNotification; APPKIT_EXTERN NSString *const MPDocumentDidRevertNotifiation; @@ -32,9 +31,7 @@ APPKIT_EXTERN NSString *const MPDocumnetDidChangeCurrentEntryNotification; /* true, if password and/or keyfile are set */ @property (assign, readonly) BOOL hasPasswordOrKey; -/* true, if lock screen is present (no phyiscal locking) */ -@property (assign, nonatomic) BOOL locked; -@property (assign, readonly) BOOL decrypted; +@property (nonatomic, readonly, assign) BOOL encrypted; @property (strong, readonly, nonatomic) KPKTree *tree; @property (weak, readonly, nonatomic) KPKGroup *root; @@ -55,7 +52,7 @@ APPKIT_EXTERN NSString *const MPDocumnetDidChangeCurrentEntryNotification; @property (nonatomic, weak) id selectedItem; -- (id)initWithVersion:(MPDatabaseVersion)version; ++ (KPKVersion)versionForFileType:(NSString *)fileType; #pragma mark Lock/Decrypt - (void)lockDatabase:(id)sender; diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 1cb04d5d..c87608ff 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -13,6 +13,7 @@ #import "MPActionHelper.h" #import "MPSettingsHelper.h" #import "MPNotifications.h" +#import "MPConstants.h" #import "MPSavePanelAccessoryViewController.h" #import "DDXMLNode.h" @@ -48,7 +49,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { @property (weak, nonatomic) KPKGroup *root; @property (assign, nonatomic) BOOL hasPasswordOrKey; -@property (assign) BOOL decrypted; @property (assign) BOOL readOnly; @property (strong) NSURL *lockFileURL; @@ -62,23 +62,28 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { @implementation MPDocument ++ (KPKVersion)versionForFileType:(NSString *)fileType { + if( NSOrderedSame == [fileType compare:MPLegacyDocumentUTI options:NSCaseInsensitiveSearch]) { + return KPKLegacyVersion; + } + if( NSOrderedSame == [fileType compare:MPXMLDocumentUTI options:NSCaseInsensitiveSearch]) { + return KPKXmlVersion; + } + return KPKUnknownVersion; +} + + (BOOL)autosavesInPlace { return NO; } - (id)init { - return [self initWithVersion:MPDatabaseVersion4]; -} -#pragma mark NSDocument essentials -- (id)initWithVersion:(MPDatabaseVersion)version { self = [super init]; if(self) { _encryptedData = nil; _didLockFile = NO; - _decrypted = YES; _hasPasswordOrKey = NO; - _locked = NO; _readOnly = NO; + [self setFileType:MPXMLDocumentUTI]; self.tree = [KPKTree templateTree]; } return self; @@ -99,11 +104,15 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { } - (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError { - /* - Move this to data:ofType: method with KeePassKit - */ - KPKPassword *password = nil; - NSData *treeData = [self.tree encryptWithPassword:password forVersion:KPKXmlVersion error:outError]; + KPKPassword *password = [[KPKPassword alloc] initWithPassword:self.password key:self.key]; + KPKVersion version = [[self class] versionForFileType:(NSString *)typeName]; + if(version == KPKUnknownVersion) { + if(outError != NULL) { + *outError = [NSError errorWithDomain:MPErrorDomain code:0 userInfo:nil]; + } + return NO; + } + NSData *treeData = [self.tree encryptWithPassword:password forVersion:version error:outError]; if([treeData writeToURL:url options:NSDataWritingAtomic error:outError]) { NSLog(@"%@", [*outError localizedDescription]); return NO; @@ -128,7 +137,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { */ self.tree = nil; _encryptedData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:outError]; - self.decrypted = NO; return YES; } @@ -142,7 +150,7 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { } - (BOOL)isEntireFileLoaded { - return _decrypted; + return YES; } - (void)close { @@ -183,14 +191,11 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { self.key = keyFileURL; self.password = [password length] > 0 ? password : nil; + NSError *error; self.tree = [[KPKTree alloc] initWithData:_encryptedData password:passwordData error:&error]; - if(self.tree) { - self.decrypted = YES; - return YES; - } - self.decrypted = NO; - return NO; + + return (self.tree != nil); } - (void)lockDatabase:(id)sender { @@ -199,10 +204,18 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { /* Locking needs to be lossless hence just use the XML format */ _encryptedData = [self.tree encryptWithPassword:password forVersion:KPKXmlVersion error:&error]; self.tree = nil; - self.locked = YES; } -#pragma mark Custom Setter +#pragma mark Properties + +- (BOOL)encrypted { + return (self.tree == nil); +} + +- (KPKGroup *)root { + return self.tree.root; +} + - (void)setPassword:(NSString *)password { if(![_password isEqualToString:password]) { _password = [password copy]; @@ -237,8 +250,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { [[NSNotificationCenter defaultCenter] postNotificationName:MPCurrentItemChangedNotification object:self]; } } - -#pragma mark Data Accesors - (void)setTree:(KPKTree *)tree { if(_tree != tree) { _tree = tree; @@ -246,9 +257,7 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { } } -- (KPKGroup *)root { - return self.tree.root; -} +#pragma mark Data Accesors - (KPKEntry *)findEntry:(NSUUID *)uuid { return [self.root entryForUUID:uuid]; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 4fd1feba..0d3fd371 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -22,6 +22,7 @@ #import "MPDocumentWindowDelegate.h" #import "MPContextToolbarButton.h" +#import "KPKTree.h" @interface MPDocumentWindowController () { @private @@ -68,12 +69,12 @@ [super windowDidLoad]; /* Drag and Drop of URLS is working, but the current - und/Redo system cannot guarantee that the undomanager is found + und/Redo system cannot guarantee that the undomanager is found when no window is active, thus this needs to be addresed when switching to KeePassKit - [[self window] setDelegate:self.documentWindowDelegate]; - [[self window] registerForDraggedTypes:@[NSURLPboardType]]; - */ + [[self window] setDelegate:self.documentWindowDelegate]; + [[self window] registerForDraggedTypes:@[NSURLPboardType]]; + */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didRevertDocument:) name:MPDocumentDidRevertNotifiation object:[self document]]; @@ -107,7 +108,7 @@ } MPDocument *document = [self document]; - if(!document.decrypted) { + if(document.encrypted) { [self showPasswordInput]; } else { @@ -157,6 +158,23 @@ #pragma mark Actions - (void)saveDocument:(id)sender { MPDocument *document = [self document]; + NSString *fileType = [document fileType]; + /* we did open as legacy */ + if([fileType isEqualToString:MPLegacyDocumentUTI]) { + if(document.tree.minimumVersion != KPKLegacyVersion) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:NSLocalizedString(@"WARNING_ON_LOSSY_SAVE", "")]; + [alert setInformativeText:NSLocalizedString(@"WARNING_ON_LOSSY_SAVE_DESCRIPTION", "Informative Text displayed when saving woudl yield data loss")]; + [alert addButtonWithTitle:NSLocalizedString(@"SAVE", "Save lossy")]; + [alert addButtonWithTitle:NSLocalizedString(@"CANCEL", "Cancel")]; + + [[alert buttons][1] setKeyEquivalent:[NSString stringWithFormat:@"%c", 0x1b]]; + + [alert beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; + return; + } + } if(!document.hasPasswordOrKey) { // warning if no password ist set! } @@ -180,7 +198,7 @@ SEL itemAction = [menuItem action]; if(itemAction == @selector(showDatabaseSettings:) || itemAction == @selector(editPassword:)) { - return document.decrypted && !document.isLocked; + return !document.encrypted; } BOOL enabled = YES; @@ -188,13 +206,13 @@ enabled &= (nil != document.selectedItem) && (document.selectedItem != document.trash); } - enabled &= !( !document.decrypted || document.isLocked || document.isReadOnly ); + enabled &= !( !document.encrypted || document.isReadOnly ); return enabled; } - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem { MPDocument *document = [self document]; - if(!document.decrypted || document.isLocked || document.isReadOnly) { + if(document.encrypted || document.isReadOnly) { return NO; } MPActionType actionType = [MPActionHelper typeForAction:[theItem action]]; @@ -222,7 +240,7 @@ - (BOOL)validateAction:(SEL)action forItem:(id)item { MPDocument *document = [self document]; - if(!document.decrypted || document.isLocked || document.isReadOnly) { + if(document.encrypted || document.isReadOnly) { return NO; } MPActionType actionType = [MPActionHelper typeForAction:action]; @@ -281,11 +299,11 @@ if(!document.hasPasswordOrKey) { return; // Document needs a password/keyfile to be lockable } - if(document.isLocked) { + if(document.encrypted) { return; // Document already locked } - document.locked = YES; [self showPasswordInput]; + [document lockDatabase:sender]; } - (void)createGroup:(id)sender { @@ -372,9 +390,6 @@ } [contentView layoutSubtreeIfNeeded]; - MPDocument *document = [self document]; - document.locked = NO; - [_entryViewController updateResponderChain]; [_inspectorViewController updateResponderChain]; [_outlineViewController updateResponderChain]; diff --git a/MacPass/MPDocumentWindowDelegate.m b/MacPass/MPDocumentWindowDelegate.m index aa6a2001..53bf2a26 100644 --- a/MacPass/MPDocumentWindowDelegate.m +++ b/MacPass/MPDocumentWindowDelegate.m @@ -16,7 +16,7 @@ - (NSDragOperation)draggingEntered:(id)sender { MPDocument *document = [[[sender draggingDestinationWindow] windowController] document]; - if(document.isLocked || !document.decrypted) { + if(document.encrypted) { return NSDragOperationNone; } diff --git a/MacPass/MPPasswordInputController.m b/MacPass/MPPasswordInputController.m index 5ff8ac9c..e94fc9b3 100644 --- a/MacPass/MPPasswordInputController.m +++ b/MacPass/MPPasswordInputController.m @@ -60,19 +60,8 @@ id windowController = [[[self view] window] windowController]; MPDocument *document = [windowController document]; if(document) { - BOOL isOk = NO; - if(document.decrypted) { - // TODO: Fix unlocking to actually test - BOOL noPassword = !document.password && [[self.passwordTextField stringValue] length] == 0; - BOOL passwordOk = [document.password isEqualToString:[self.passwordTextField stringValue]]; - BOOL noKey = document.key == [self.keyPathControl URL]; - BOOL keyOk = [document.key isEqualTo:[self.keyPathControl URL]]; - isOk = (noPassword || passwordOk) && (noKey || keyOk); - } - else { - isOk = [document unlockWithPassword:[self.passwordTextField stringValue] keyFileURL:[self.keyPathControl URL]]; - } - if(!isOk) { + if(![document unlockWithPassword:[self.passwordTextField stringValue] + keyFileURL:[self.keyPathControl URL]]) { [self _showError]; } else { diff --git a/MacPass/MPSavePanelAccessoryViewController.h b/MacPass/MPSavePanelAccessoryViewController.h index 61a9331b..1b9e5515 100644 --- a/MacPass/MPSavePanelAccessoryViewController.h +++ b/MacPass/MPSavePanelAccessoryViewController.h @@ -7,12 +7,15 @@ // #import "MPViewController.h" +#import "KPKVersion.h" + @class MPDocument; @interface MPSavePanelAccessoryViewController : MPViewController @property (nonatomic, assign) NSSavePanel *savePanel; @property (nonatomic, assign) MPDocument *document; +@property (nonatomic, assign, readonly) KPKVersion selectedVersion; @property (nonatomic, weak) IBOutlet NSPopUpButton *fileTypePopupButton; @property (nonatomic, weak) IBOutlet NSTextField *infoTextField; diff --git a/MacPass/MPSavePanelAccessoryViewController.m b/MacPass/MPSavePanelAccessoryViewController.m index 228f45d6..55776326 100644 --- a/MacPass/MPSavePanelAccessoryViewController.m +++ b/MacPass/MPSavePanelAccessoryViewController.m @@ -8,12 +8,13 @@ #import "MPSavePanelAccessoryViewController.h" #import "MPDocument.h" +#import "MPConstants.h" #import "KPKUTIs.h" #import "KPKTree.h" @interface MPSavePanelAccessoryViewController () - +@property (readwrite, assign) KPKVersion selectedVersion; @end @implementation MPSavePanelAccessoryViewController @@ -45,7 +46,13 @@ - (IBAction)setFileType:(id)sender { NSString *uti = [[self.fileTypePopupButton selectedItem] representedObject]; - BOOL showInfoText = (self.document.tree.minimumVersion == KPKLegacyVersion && [uti isEqualToString:@"com.hicknhack.macpass.kdb"]); + if([uti isEqualToString:MPLegacyDocumentUTI]) { + self.selectedVersion = KPKLegacyVersion; + } + else if([uti isEqualToString:MPLegacyDocumentUTI]) { + self.selectedVersion = KPKXmlVersion; + } + BOOL showInfoText = (self.document.tree.minimumVersion == KPKLegacyVersion && [uti isEqualToString:MPLegacyDocumentUTI]); [self.infoTextField setHidden:!showInfoText]; [self.savePanel setAllowedFileTypes:@[uti]]; }