diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h index d5a56ab9..f812caa1 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.h +++ b/MasterPassword/ObjC/MPAppDelegate_Store.h @@ -27,6 +27,7 @@ typedef enum { - (UbiquityStoreManager *)storeManager; - (void)addElementNamed:(NSString *)siteName completion:(void (^)(MPElementEntity *element))completion; +- (MPElementEntity *)changeElement:(MPElementEntity *)element inContext:(NSManagedObjectContext *)context toType:(MPElementType)type; - (MPImportResult)importSites:(NSString *)importedSitesString askImportPassword:(NSString *(^)(NSString *userName))importPassword askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword; diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index 7c9299bb..1a4a5a10 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -426,6 +426,38 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, }]; } +- (MPElementEntity *)changeElement:(MPElementEntity *)element inContext:(NSManagedObjectContext *)context toType:(MPElementType)type { + + if ([element.algorithm classOfType:type] == element.typeClass) + element.type = type; + + else { + // Type requires a different class of element. Recreate the element. + MPElementEntity *newElement + = [NSEntityDescription insertNewObjectForEntityForName:[element.algorithm classNameOfType:type] + inManagedObjectContext:context]; + newElement.type = type; + newElement.name = element.name; + newElement.user = element.user; + newElement.uses = element.uses; + newElement.lastUsed = element.lastUsed; + newElement.version = element.version; + newElement.loginName = element.loginName; + + [context deleteObject:element]; + [context saveToStore]; + + NSError *error; + if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error]) + err(@"Failed to obtain a permanent object ID after changing object type: %@", error); + + element = newElement; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID]; + return element; +} + - (MPImportResult)importSites:(NSString *)importedSitesString askImportPassword:(NSString *(^)(NSString *userName))importPassword askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword { diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m index 96fdac4f..cdc0f514 100644 --- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m +++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m @@ -395,6 +395,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven otherButton:nil informativeTextWithFormat: @"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."] runModal]; + [self.statusView popUpMenu]; return; } diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h index d80c47bf..ac41e185 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h @@ -8,15 +8,14 @@ #import -@interface MPPasswordWindowController : NSWindowController +@interface MPPasswordWindowController : NSWindowController @property(nonatomic, weak) IBOutlet NSTextField *siteField; @property(nonatomic, weak) IBOutlet NSTextField *contentField; @property(nonatomic, weak) IBOutlet NSTextField *tipField; +@property(nonatomic, weak) IBOutlet NSComboBox *typeField; @property(nonatomic, weak) IBOutlet NSView *contentContainer; @property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView; @property(nonatomic, weak) IBOutlet NSTextField *userLabel; -- (IBAction)reload:(id)sender; - @end diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index 90dc2925..0e7225ba 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -14,6 +14,7 @@ #define MPAlertUnlockMP @"MPAlertUnlockMP" #define MPAlertIncorrectMP @"MPAlertIncorrectMP" #define MPAlertCreateSite @"MPAlertCreateSite" +#define MPAlertChangeType @"MPAlertChangeType" @interface MPPasswordWindowController() @@ -74,6 +75,7 @@ addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:^(NSNotification *note) { _activeElementOID = nil; [self.siteField setStringValue:@""]; + [self.typeField deselectItemAtIndex:[self.typeField indexOfSelectedItem]]; [self trySiteWithAction:NO]; [self ensureLoadedAndUnlockedOrCloseIfLoggedOut:YES]; }]; @@ -155,6 +157,7 @@ self.content = @""; [self.siteField setStringValue:@""]; + [self.typeField deselectItemAtIndex:[self.typeField indexOfSelectedItem]]; [self.tipField setStringValue:@""]; NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked." @@ -173,11 +176,6 @@ return unlocked; } -- (IBAction)reload:(id)sender { - - [[MPMacAppDelegate get].storeManager reloadStore]; -} - - (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { if (contextInfo == MPAlertIncorrectMP) { @@ -267,6 +265,68 @@ break; } } + if (contextInfo == MPAlertChangeType) { + switch (returnCode) { + case NSAlertDefaultReturn: { + MPElementType type = [self selectedType]; + [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPElementEntity *activeElement = [self activeElementInContext:context]; + _activeElementOID = [[MPMacAppDelegate get] changeElement:activeElement inContext:context + toType:type].objectID; + [context saveToStore]; + + dispatch_async( dispatch_get_main_queue(), ^{ + [self trySiteWithAction:NO]; + } ); + }]; + break; + } + default: + break; + } + } +} + +- (MPElementType)selectedType { + + if (self.typeField.indexOfSelectedItem == 0) + return MPElementTypeGeneratedMaximum; + if (self.typeField.indexOfSelectedItem == 1) + return MPElementTypeGeneratedLong; + if (self.typeField.indexOfSelectedItem == 2) + return MPElementTypeGeneratedMedium; + if (self.typeField.indexOfSelectedItem == 3) + return MPElementTypeGeneratedBasic; + if (self.typeField.indexOfSelectedItem == 4) + return MPElementTypeGeneratedShort; + if (self.typeField.indexOfSelectedItem == 5) + return MPElementTypeGeneratedPIN; + if (self.typeField.indexOfSelectedItem == 6) + return MPElementTypeStoredPersonal; + if (self.typeField.indexOfSelectedItem == 7) + return MPElementTypeStoredDevicePrivate; + + wrn(@"Unsupported type selected: %li, assuming Long.", self.typeField.indexOfSelectedItem); + return MPElementTypeGeneratedLong; +} + +- (void)comboBoxSelectionDidChange:(NSNotification *)notification { + + if (notification.object == self.typeField) { + if ([self.typeField indexOfSelectedItem] < 0) + return; + MPElementEntity *activeElement = [self activeElementForThread]; + MPElementType selectedType = [self selectedType]; + if (!activeElement || activeElement.type == selectedType || !(selectedType & MPElementTypeClassGenerated)) + return; + + [[NSAlert alertWithMessageText:@"Change Password Type" defaultButton:@"Change Password" + alternateButton:@"Cancel" otherButton:nil + informativeTextWithFormat:@"Changing the password type for this site will cause the password to change.\n" + @"You will need to update your account with the new password."] + beginSheetModalForWindow:self.window modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:MPAlertChangeType]; + } } - (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words @@ -291,7 +351,6 @@ _activeElementOID = ((NSManagedObject *)[siteResults objectAtIndex:0]).objectID; for (MPElementEntity *element in siteResults) [mutableResults addObject:element.name]; - //[mutableResults addObject:query]; // For when the app should be able to create new sites. } else _activeElementOID = nil; @@ -396,7 +455,9 @@ [self.progressView startAnimation:nil]; [self.backgroundQueue addOperationWithBlock:^{ BOOL actionHandled = NO; - NSString *content = [[self activeElementForThread].content description]; + MPElementEntity *activeElement = [self activeElementForThread]; + NSString *content = [activeElement.content description]; + NSString *typeName = [activeElement typeShortName]; if (!content) content = @""; @@ -415,6 +476,7 @@ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self setContent:content]; [self.progressView stopAnimation:nil]; + [self.typeField selectItemWithObjectValue:typeName]; self.tipField.alphaValue = 1; if (actionHandled) diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib index f871f799..e6bab72f 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib @@ -12,6 +12,8 @@ IBNSLayoutConstraint + NSComboBox + NSComboBoxCell NSCustomObject NSCustomView NSProgressIndicator @@ -56,6 +58,141 @@ 268 + + + 268 + {{288, 131}, {115, 26}} + + + + _NS:9 + YES + + 342884416 + 272630784 + + + LucidaGrande + 13 + 1044 + + + _NS:9 + + YES + 1 + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + controlTextColor + + 3 + MAA + + + 5 + YES + + Maximum + Long + Medium + Basic + Short + PIN + Personal + Device + + + + + 274 + {13, 21} + + + _NS:24 + YES + NO + YES + + + 10 + 10 + 1000 + + 75497472 + 0 + + + LucidaGrande + 12 + 16 + + + 3 + MC4zMzMzMzI5ODU2AA + + + + + 338690112 + 1024 + + + YES + + 6 + System + controlBackgroundColor + + 3 + MC42NjY2NjY2NjY3AA + + + + + 3 + YES + + + + 3 + 2 + + + 6 + System + gridColor + + 3 + MC41AA + + + 19 + tableViewAction: + -765427712 + + + + 1 + 15 + 0 + YES + 0 + 1 + + NO + + NO + 268 @@ -88,10 +225,7 @@ 6 System controlColor - - 3 - MC42NjY2NjY2NjY3AA - + 1 @@ -136,10 +270,10 @@ 268 - {{140, 133}, {200, 22}} + {{80, 133}, {200, 22}} - + _NS:9 YES @@ -155,23 +289,12 @@ _NS:9 1 - - 6 - System - textBackgroundColor - - 3 - MQA - - + 6 System textColor - - 3 - MAA - + NO @@ -316,6 +439,14 @@ 215 + + + typeField + + + + 263 + delegate @@ -332,6 +463,14 @@ 188 + + + delegate + + + + 265 + @@ -443,11 +582,43 @@ 143 + + + 11 + 0 + + 11 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + + + + 5 + 0 + + 6 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + 9 0 - + 9 1 @@ -539,38 +710,6 @@ 29 3 - - - 3 - 0 - - 4 - 1 - - 8 - - 1000 - - 6 - 24 - 3 - - - - 9 - 0 - - 9 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - 3 @@ -587,6 +726,38 @@ 29 3 + + + 5 + 0 + + 5 + 1 + + 80 + + 1000 + + 3 + 9 + 3 + + + + 3 + 0 + + 4 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + 3 @@ -638,8 +809,9 @@ - + + @@ -696,6 +868,7 @@ 182 + 7 @@ -712,15 +885,9 @@ 9 1 - - - 186 - - - 185 @@ -782,11 +949,6 @@ - - 219 - - - 221 @@ -818,10 +980,64 @@ - 239 + 240 + + + + + + 7 + 0 + + 0 + 1 + + 112 + + 1000 + + 3 + 9 + 1 + + + + + + 241 + + + + + 254 + + + + + 256 + + 258 + + + + + 260 + + + + + 261 + + + + + 262 + + + @@ -832,15 +1048,17 @@ - - + + + + com.apple.InterfaceBuilder.CocoaPlugin @@ -857,7 +1075,7 @@ - + @@ -874,7 +1092,6 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -889,7 +1106,6 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -906,7 +1122,28 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + Maximum + Long + Medium + Basic + Short + PIN + Personal + Device + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -914,30 +1151,20 @@ - 239 + 265 MPPasswordWindowController NSWindowController - - reload: - id - - - reload: - - reload: - id - - NSView NSTextField NSProgressIndicator NSTextField NSTextField + NSComboBox NSTextField @@ -961,6 +1188,10 @@ tipField NSTextField + + typeField + NSComboBox + userLabel NSTextField diff --git a/MasterPassword/ObjC/iOS/MPMainViewController.m b/MasterPassword/ObjC/iOS/MPMainViewController.m index 7c65215a..f6c8e882 100644 --- a/MasterPassword/ObjC/iOS/MPMainViewController.m +++ b/MasterPassword/ObjC/iOS/MPMainViewController.m @@ -789,35 +789,8 @@ @"If you continue, the password for this site will change. " @"You will need to update your account's old password to the new one." do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) { - if ([activeElement.algorithm classOfType:type] == activeElement.typeClass) - activeElement.type = type; - - else { - // Type requires a different class of element. Recreate the element. - MPElementEntity *newElement - = [NSEntityDescription insertNewObjectForEntityForName:[activeElement.algorithm classNameOfType:type] - inManagedObjectContext:context]; - newElement.type = type; - newElement.name = activeElement.name; - newElement.user = activeElement.user; - newElement.uses = activeElement.uses; - newElement.lastUsed = activeElement.lastUsed; - newElement.version = activeElement.version; - newElement.loginName = activeElement.loginName; - - [context deleteObject:activeElement]; - [context saveToStore]; - - NSError *error; - if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error]) - err(@"Failed to obtain a permanent object ID after changing object type: %@", error); - - _activeElementOID = newElement.objectID; - activeElement = newElement; - } - - [[NSNotificationCenter defaultCenter] - postNotificationName:MPElementUpdatedNotification object:activeElement.objectID]; + _activeElementOID = [[MPiOSAppDelegate get] changeElement:activeElement inContext:context + toType:type].objectID; return YES; }]; }