2012-03-05 09:53:32 +01:00
|
|
|
//
|
|
|
|
// MPPasswordWindowController.m
|
|
|
|
// MasterPassword-Mac
|
|
|
|
//
|
|
|
|
// Created by Maarten Billemont on 04/03/12.
|
|
|
|
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "MPPasswordWindowController.h"
|
2012-05-07 22:18:01 +02:00
|
|
|
#import "MPAppDelegate_Shared.h"
|
2012-03-06 01:04:19 +01:00
|
|
|
#import "MPElementEntity.h"
|
|
|
|
#import "MPElementGeneratedEntity.h"
|
2012-03-05 09:53:32 +01:00
|
|
|
|
|
|
|
@interface MPPasswordWindowController ()
|
|
|
|
|
2012-03-06 01:04:19 +01:00
|
|
|
@property (nonatomic, strong) NSString *oldSiteName;
|
|
|
|
@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
|
2012-03-05 22:19:05 +01:00
|
|
|
|
2012-03-05 09:53:32 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MPPasswordWindowController
|
2012-03-06 01:04:19 +01:00
|
|
|
@synthesize oldSiteName, siteResults;
|
2012-03-05 09:53:32 +01:00
|
|
|
@synthesize siteField;
|
|
|
|
@synthesize contentField;
|
2012-05-05 00:15:51 +02:00
|
|
|
@synthesize tipField;
|
2012-03-05 09:53:32 +01:00
|
|
|
|
|
|
|
- (void)windowDidLoad {
|
|
|
|
|
2012-03-05 22:19:05 +01:00
|
|
|
[self.contentField setStringValue:@""];
|
2012-05-05 00:15:51 +02:00
|
|
|
[self.tipField setStringValue:@""];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil
|
|
|
|
usingBlock:^(NSNotification *note) {
|
|
|
|
[self.siteField selectText:self];
|
|
|
|
}];
|
2012-03-05 22:19:05 +01:00
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil
|
|
|
|
usingBlock:^(NSNotification *note) {
|
|
|
|
[[NSApplication sharedApplication] hide:self];
|
|
|
|
}];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil
|
|
|
|
usingBlock:^(NSNotification *note) {
|
2012-03-06 01:04:19 +01:00
|
|
|
NSString *newSiteName = [self.siteField stringValue];
|
|
|
|
BOOL shouldComplete = [self.oldSiteName length] < [newSiteName length];
|
|
|
|
self.oldSiteName = newSiteName;
|
2012-05-05 00:15:51 +02:00
|
|
|
|
|
|
|
if ([self trySite])
|
|
|
|
shouldComplete = NO;
|
|
|
|
|
2012-03-06 01:04:19 +01:00
|
|
|
if (shouldComplete)
|
2012-03-05 22:19:05 +01:00
|
|
|
[[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
|
|
|
|
}];
|
|
|
|
|
2012-03-05 09:53:32 +01:00
|
|
|
[super windowDidLoad];
|
2012-03-05 22:19:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
|
2012-03-05 09:53:32 +01:00
|
|
|
|
2012-03-05 22:19:05 +01:00
|
|
|
NSString *query = [[control stringValue] substringWithRange:charRange];
|
2012-05-05 00:15:51 +02:00
|
|
|
if (![query length] || ![MPAppDelegate get].keyHashHex)
|
|
|
|
return nil;
|
2012-03-06 01:04:19 +01:00
|
|
|
|
2012-03-05 22:19:05 +01:00
|
|
|
NSFetchRequest *fetchRequest = [MPAppDelegate.managedObjectModel
|
|
|
|
fetchRequestFromTemplateWithName:@"MPElements"
|
|
|
|
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
query, @"query",
|
2012-03-06 01:04:19 +01:00
|
|
|
[MPAppDelegate get].keyHashHex, @"mpHashHex",
|
2012-03-05 22:19:05 +01:00
|
|
|
nil]];
|
2012-03-06 01:04:19 +01:00
|
|
|
[fetchRequest setSortDescriptors:
|
|
|
|
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
|
|
|
|
|
|
|
|
NSError *error = nil;
|
|
|
|
self.siteResults = [[MPAppDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error];
|
|
|
|
if (error)
|
|
|
|
err(@"Couldn't fetch elements: %@", error);
|
|
|
|
|
|
|
|
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
|
|
|
|
if (self.siteResults)
|
|
|
|
for (MPElementEntity *element in self.siteResults)
|
|
|
|
[mutableResults addObject:element.name];
|
2012-05-05 00:15:51 +02:00
|
|
|
// [mutableResults addObject:query]; // For when the app should be able to create new sites.
|
2012-03-06 01:04:19 +01:00
|
|
|
return mutableResults;
|
2012-03-05 22:19:05 +01:00
|
|
|
}
|
|
|
|
|
2012-05-04 18:54:58 +02:00
|
|
|
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
|
|
|
|
|
2012-05-05 00:15:51 +02:00
|
|
|
if (commandSelector == @selector(cancel:))
|
|
|
|
[self.window close];
|
|
|
|
if (commandSelector == @selector(insertNewline:) && [[self.contentField stringValue] length]) {
|
|
|
|
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
|
|
|
if ([[NSPasteboard generalPasteboard] setString:[self.contentField stringValue] forType:NSPasteboardTypeString]) {
|
|
|
|
self.tipField.alphaValue = 1;
|
|
|
|
[self.tipField setStringValue:@"Copied!"];
|
|
|
|
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC);
|
|
|
|
dispatch_after(popTime, dispatch_get_main_queue(), ^{
|
|
|
|
[NSAnimationContext beginGrouping];
|
|
|
|
[[NSAnimationContext currentContext] setDuration:0.2f];
|
|
|
|
[self.tipField.animator setAlphaValue:0];
|
|
|
|
[NSAnimationContext endGrouping];
|
|
|
|
});
|
|
|
|
return YES;
|
|
|
|
} else
|
|
|
|
wrn(@"Couldn't copy password to pasteboard.");
|
|
|
|
}
|
2012-05-04 18:54:58 +02:00
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2012-03-05 22:19:05 +01:00
|
|
|
- (void)controlTextDidEndEditing:(NSNotification *)obj {
|
|
|
|
|
2012-05-05 00:15:51 +02:00
|
|
|
if (obj.object == self.siteField)
|
|
|
|
[self trySite];
|
2012-03-05 09:53:32 +01:00
|
|
|
}
|
|
|
|
|
2012-05-05 00:15:51 +02:00
|
|
|
- (BOOL)trySite {
|
|
|
|
|
|
|
|
MPElementEntity *result = [self findElement];
|
|
|
|
if (!result) {
|
|
|
|
[self.contentField setStringValue:@""];
|
|
|
|
[self.tipField setStringValue:@""];
|
|
|
|
return NO;
|
2012-05-04 18:54:58 +02:00
|
|
|
}
|
2012-05-05 00:15:51 +02:00
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
2012-05-07 01:15:19 +02:00
|
|
|
NSString *description = [result.content description];
|
2012-05-05 00:15:51 +02:00
|
|
|
[result use];
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[self.contentField setStringValue:description];
|
|
|
|
[self.tipField setStringValue:@"Hit enter to copy the password."];
|
|
|
|
self.tipField.alphaValue = 1;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// For when the app should be able to create new sites.
|
|
|
|
/*
|
|
|
|
else
|
|
|
|
[[MPAppDelegate get].managedObjectContext performBlock:^{
|
|
|
|
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
|
|
|
|
inManagedObjectContext:[MPAppDelegate get].managedObjectContext];
|
|
|
|
assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
|
|
|
|
assert([MPAppDelegate get].keyHashHex);
|
|
|
|
|
|
|
|
element.name = siteName;
|
|
|
|
element.mpHashHex = [MPAppDelegate get].keyHashHex;
|
|
|
|
|
2012-05-07 01:15:19 +02:00
|
|
|
NSString *description = [element.content description];
|
2012-05-05 00:15:51 +02:00
|
|
|
[element use];
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[self.contentField setStringValue:description? description: @""];
|
|
|
|
});
|
|
|
|
}];
|
|
|
|
*/
|
|
|
|
|
|
|
|
return YES;
|
2012-05-04 18:54:58 +02:00
|
|
|
}
|
2012-05-05 00:15:51 +02:00
|
|
|
|
|
|
|
- (MPElementEntity *)findElement {
|
|
|
|
|
|
|
|
for (MPElementEntity *element in self.siteResults)
|
|
|
|
if ([element.name isEqualToString:[self.siteField stringValue]])
|
|
|
|
return element;
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2012-03-05 09:53:32 +01:00
|
|
|
@end
|