2
0

Initial working password application with types and history.

This commit is contained in:
Maarten Billemont
2012-01-05 01:44:15 +01:00
parent d7a448dfe0
commit cab35df79b
62 changed files with 1394 additions and 862 deletions

View File

@@ -13,6 +13,9 @@
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (strong, nonatomic) NSString *keyPhrase;
+ (OPAppDelegate *)get;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

View File

@@ -15,9 +15,57 @@
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize keyPhrase = _keyPhrase;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
+ (void)initialize {
[Logger get].autoprintLevel = LogLevelDebug;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *keyPhraseHash = [OPConfig get].keyPhraseHash;
AlertViewController *keyPhraseAlert = [[AlertViewController alloc] initQuestionWithTitle:@"One Password"
message:keyPhraseHash? @"Unlock with your master password:": @"Choose your master password:"
tappedButtonBlock:
^(NSInteger buttonIndex, NSString *answer) {
if (buttonIndex == 0)
exit(0);
if (![answer length]) {
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:@"No master password entered."
tappedButtonBlock:
^(NSInteger buttonIndex) {
exit(0);
} cancelTitle:@"Quit" otherTitles:nil];
}
NSString *answerHash = [[answer hashWith:PearlDigestSHA1] encodeHex];
if (keyPhraseHash) {
if (![keyPhraseHash isEqualToString:answerHash]) {
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:@"Incorrect master password."
tappedButtonBlock:
^(NSInteger buttonIndex) {
exit(0);
} cancelTitle:@"Quit" otherTitles:nil];
return;
}
} else
[OPConfig get].keyPhraseHash = answerHash;
self.keyPhrase = answer;
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
keyPhraseAlert.alertField.autocapitalizationType = UITextAutocapitalizationTypeNone;
keyPhraseAlert.alertField.autocorrectionType = UITextAutocorrectionTypeNo;
keyPhraseAlert.alertField.secureTextEntry = YES;
[keyPhraseAlert showAlert];
});
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@@ -58,6 +106,11 @@
[self saveContext];
}
+ (OPAppDelegate *)get {
return (OPAppDelegate *)[super get];
}
- (void)saveContext
{
NSError *error = nil;
@@ -106,6 +159,7 @@
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"OnePassword" withExtension:@"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
@@ -124,7 +178,12 @@
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
options:[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption,
(id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption,
nil]
error:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
@@ -149,9 +208,14 @@
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
err(@"Unresolved error %@, %@", error, [error userInfo]);
#if DEBUG
wrn(@"Deleted datastore: %@", storeURL);
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
#endif
@throw [NSException exceptionWithName:error.domain reason:error.localizedDescription
userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]];
}
return __persistentStoreCoordinator;
}

16
OnePassword/OPConfig.h Normal file
View File

@@ -0,0 +1,16 @@
//
// OPConfig.h
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
@interface OPConfig : Config
@property (nonatomic, retain) NSNumber *dataStoreError;
@property (nonatomic, retain) NSString *keyPhraseHash;
+ (OPConfig *)get;
@end

34
OnePassword/OPConfig.m Normal file
View File

@@ -0,0 +1,34 @@
//
// OPConfig.m
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPConfig.h"
@implementation OPConfig
@dynamic dataStoreError, keyPhraseHash;
-(id) init {
if(!(self = [super init]))
return self;
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)),
nil]];
return self;
}
+ (OPConfig *)get {
return (OPConfig *)[super get];
}
@end

View File

@@ -0,0 +1,16 @@
//
// OPContentViewController.h
// OnePassword
//
// Created by Maarten Billemont on 03/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "OPElementEntity.h"
@interface OPContentViewController : UIViewController
@property (nonatomic, weak) OPElementEntity *activeElement;
@end

View File

@@ -0,0 +1,22 @@
//
// OPContentViewController.m
// OnePassword
//
// Created by Maarten Billemont on 03/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPContentViewController.h"
@implementation OPContentViewController
@synthesize activeElement = _activeElement;
#pragma mark - View lifecycle
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end

View File

@@ -0,0 +1,26 @@
//
// OPElementEntity.h
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface OPElementEntity : NSManagedObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic) int16_t type;
@property (nonatomic) int16_t uses;
@property (nonatomic) NSTimeInterval lastUsed;
@property (nonatomic, retain) NSString *contentUTI;
@property (nonatomic) int16_t contentType;
- (void)use;
- (id)content;
- (NSString *)contentDescription;
@end

View File

@@ -0,0 +1,45 @@
//
// OPElementEntity.m
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPElementEntity.h"
#import "OPAppDelegate.h"
@implementation OPElementEntity
@dynamic name;
@dynamic type;
@dynamic uses;
@dynamic lastUsed;
@dynamic contentUTI;
@dynamic contentType;
- (void)use {
++self.uses;
self.lastUsed = [[NSDate date] timeIntervalSinceReferenceDate];
}
- (id)content {
if (![self.name length])
return nil;
if (self.type & OPElementTypeCalculated)
return OPCalculateContent(self.type, self.name, [OPAppDelegate get].keyPhrase);
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"Unsupported type: %d", self.type] userInfo:nil];
}
- (NSString *)contentDescription {
return [[self content] description];
}
@end

View File

@@ -0,0 +1,18 @@
//
// OPElementStoredEntity.h
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "OPElementEntity.h"
@interface OPElementStoredEntity : OPElementEntity
@property (nonatomic, retain) id contentObject;
@end

View File

@@ -0,0 +1,23 @@
//
// OPElementStoredEntity.m
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPElementStoredEntity.h"
@implementation OPElementStoredEntity
@dynamic contentObject;
- (id)content {
assert(self.type & OPElementTypeStored);
return self.contentObject;
}
@end

View File

@@ -6,8 +6,19 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
@interface OPMainViewController : UITableViewController
#import "OPTypeViewController.h"
#import "OPElementEntity.h"
#import "OPSearchDelegate.h"
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@interface OPMainViewController : UITableViewController <OPTypeDelegate, UITextFieldDelegate, UISearchBarDelegate, OPSearchResultsDelegate>
@property (strong, nonatomic) OPElementEntity *activeElement;
@property (strong, nonatomic) IBOutlet OPSearchDelegate *searchResultsController;
@property (weak, nonatomic) IBOutlet UITextField *contentField;
@property (weak, nonatomic) IBOutlet UITextView *contentTextView;
@property (weak, nonatomic) IBOutlet UILabel *typeLabel;
@property (weak, nonatomic) IBOutlet UISegmentedControl *contentType;
- (IBAction)didChangeContentType:(UISegmentedControl *)sender;
@end

View File

@@ -7,67 +7,140 @@
//
#import "OPMainViewController.h"
#import "OPAppDelegate.h"
#import "OPContentViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface OPMainViewController (Private)
- (void)updateAnimated:(BOOL)animated;
- (void)updateWasAnimated:(BOOL)animated;
@end
@implementation OPMainViewController
@synthesize managedObjectContext = _managedObjectContext;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
@synthesize activeElement = _activeElement;
@synthesize searchResultsController = _searchResultsController;
@synthesize typeLabel = _typeLabel;
@synthesize contentType = _contentType;
@synthesize contentField = _contentField;
@synthesize contentTextView = _contentTextView;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad || interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showAlternate"]) {
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"OP_Main_ChooseType"])
[[segue destinationViewController] setDelegate:self];
}
if ([[segue identifier] isEqualToString:@"OP_Main_Content"])
((OPContentViewController *)[segue destinationViewController]).activeElement = self.activeElement;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateAnimated:NO];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}
- (void)viewDidLoad {
// Because IB's edit button doesn't auto-toggle self.editable like editButtonItem does.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[super viewDidLoad];
}
- (void)viewDidUnload {
[self setContentField:nil];
[self setTypeLabel:nil];
[self setContentType:nil];
[self setContentTextView:nil];
[self setSearchResultsController:nil];
[super viewDidUnload];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self updateAnimated:animated];
}
- (void)updateAnimated:(BOOL)animated {
[[OPAppDelegate get] saveContext];
if (animated)
[UIView animateWithDuration:0.2 animations:^{
[self updateWasAnimated:YES];
}];
else
[self updateWasAnimated:NO];
}
- (void)updateWasAnimated:(BOOL)animated {
self.typeLabel.text = self.activeElement? NSStringFromOPElementType(self.activeElement.type): @"moo";
self.contentTextView.alpha = self.contentType.selectedSegmentIndex == OPElementContentTypeNote? 1: 0;
self.contentTextView.editable = self.editing && self.activeElement.type & OPElementTypeStored;
self.contentType.alpha = self.editing && self.activeElement.type & OPElementTypeStored? 1: 0;
self.contentType.selectedSegmentIndex = self.activeElement.contentType;
self.contentField.alpha = self.contentType.selectedSegmentIndex == OPElementContentTypePassword? 1: 0;
self.contentField.enabled = self.editing && self.activeElement.type & OPElementTypeStored;
self.contentField.clearButtonMode = self.contentField.enabled? UITextFieldViewModeAlways: UITextFieldViewModeNever;
self.contentField.text = @"...";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *contentDescription = self.activeElement.contentDescription;
if (contentDescription)
[self.activeElement use];
dispatch_async(dispatch_get_main_queue(), ^{
self.contentField.text = contentDescription;
});
});
}
#pragma mark - Protocols
- (IBAction)didChangeContentType:(UISegmentedControl *)sender {
self.activeElement.contentType = self.contentType.selectedSegmentIndex;
[self updateAnimated:YES];
}
- (void)didSelectType:(OPElementType)type {
self.activeElement.type = type;
[self updateAnimated:YES];
}
- (void)didSelectElement:(OPElementEntity *)element {
self.activeElement = element;
[self updateAnimated:YES];
[self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = self.activeElement.name;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self updateAnimated:YES];
}
@end

View File

@@ -11,51 +11,10 @@
@implementation OPRecentViewController
@dynamic tableView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
return YES;
}
@end

View File

@@ -1,15 +0,0 @@
//
// OPSaltedCipherViewController.h
// OnePassword
//
// Created by Maarten Billemont on 30/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface OPSaltedCipherViewController : UITableViewController
@property(nonatomic,retain) IBOutlet UITableView *tableView;
@end

View File

@@ -1,61 +0,0 @@
//
// OPSaltedCipherViewController.m
// OnePassword
//
// Created by Maarten Billemont on 30/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "OPSaltedCipherViewController.h"
@implementation OPSaltedCipherViewController
@dynamic tableView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end

View File

@@ -0,0 +1,27 @@
//
// OPSearchDelegate.h
// OnePassword
//
// Created by Maarten Billemont on 04/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "OPElementEntity.h"
@protocol OPSearchResultsDelegate <NSObject>
- (void)didSelectElement:(OPElementEntity *)element;
@end
@interface OPSearchDelegate : NSObject <UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate, NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (weak, nonatomic) IBOutlet id<OPSearchResultsDelegate> delegate;
@property (weak, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
@end

View File

@@ -0,0 +1,204 @@
//
// OPSearchDelegate.m
// OnePassword
//
// Created by Maarten Billemont on 04/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPSearchDelegate.h"
#import "OPAppDelegate.h"
@interface OPSearchDelegate (Private)
- (NSManagedObjectContext *)managedObjectContext;
- (void)update;
@end
@implementation OPSearchDelegate
@synthesize fetchedResultsController;
@synthesize delegate;
@synthesize searchDisplayController;
- (NSManagedObjectContext *)managedObjectContext {
return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[self.searchDisplayController.searchResultsTableView setEditing:self.searchDisplayController.searchContentsController.editing animated:NO];
[self update];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self update];
return YES;
}
- (void)update {
NSString *text = self.searchDisplayController.searchBar.text;
if (!text)
text = @"";
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([OPElementEntity class])];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", text]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.searchDisplayController.searchResultsTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section + 1];
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:newIndexPath.section + 1];
switch(type) {
case NSFetchedResultsChangeInsert:
[self.searchDisplayController.searchResultsTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[self.searchDisplayController.searchResultsTableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[self.searchDisplayController.searchResultsTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.searchDisplayController.searchResultsTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
++sectionIndex;
switch(type) {
case NSFetchedResultsChangeInsert:
[self.searchDisplayController.searchResultsTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.searchDisplayController.searchResultsTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.searchDisplayController.searchResultsTableView endUpdates];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count] + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (--section == -1)
return 1;
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"OPElementSearch"];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"OPElementSearch"];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section - 1];
if (indexPath.section == -1) {
cell.textLabel.text = self.searchDisplayController.searchBar.text;
cell.detailTextLabel.text = @"New";
} else {
OPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = element.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", element.uses];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
OPElementEntity *element;
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section - 1];
if (indexPath.section == -1) {
element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([OPElementEntity class])
inManagedObjectContext:[self managedObjectContext]];
element.name = self.searchDisplayController.searchBar.text;
} else
element = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.delegate didSelectElement:element];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (--section == -1)
return @"";
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self.fetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section - 1];
if (editingStyle == UITableViewCellEditingStyleDelete) {
OPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
[[self managedObjectContext] deleteObject:element];
}
}
@end

View File

@@ -8,6 +8,14 @@
#import <UIKit/UIKit.h>
@interface OPTypeViewController : UITableViewController
@protocol OPTypeDelegate <NSObject>
- (void)didSelectType:(OPElementType)type;
@end
@interface OPTypeViewController : UITableViewController
@property (nonatomic, weak) id<OPTypeDelegate> delegate;
@end

View File

@@ -9,43 +9,71 @@
#import "OPTypeViewController.h"
@implementation OPTypeViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
@synthesize delegate;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
assert(self.navigationController.topViewController == self);
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
OPElementType type;
switch (indexPath.section) {
case 0: {
// Calculated
switch (indexPath.row) {
case 0:
type = OPElementTypeCalculatedLong;
break;
case 1:
type = OPElementTypeCalculatedMedium;
break;
case 2:
type = OPElementTypeCalculatedShort;
break;
case 3:
type = OPElementTypeCalculatedBasic;
break;
case 4:
type = OPElementTypeCalculatedPIN;
break;
default:
[NSException raise:NSInternalInconsistencyException
format:@"Unsupported row: %d, when selecting calculated element type.", indexPath.row];
}
break;
}
case 1: {
// Stored
switch (indexPath.row) {
case 0:
type = OPElementTypeStoredPersonal;
break;
case 1:
type = OPElementTypeStoredDevicePrivate;
break;
default:
[NSException raise:NSInternalInconsistencyException
format:@"Unsupported row: %d, when selecting stored element type.", indexPath.row];
}
break;
}
default:
[NSException raise:NSInternalInconsistencyException
format:@"Unsupported section: %d, when selecting element type.", indexPath.section];
}
[delegate didSelectType:type];
[self.navigationController popViewControllerAnimated:YES];
}
@end

34
OnePassword/OPTypes.h Normal file
View File

@@ -0,0 +1,34 @@
//
// OPTypes.h
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef enum {
OPElementContentTypePassword,
OPElementContentTypeNote,
OPElementContentTypePicture,
} OPElementContentType;
typedef enum {
OPElementTypeCalculated = 2 << 7,
OPElementTypeStored = 2 << 8,
} OPElementTypeClass;
typedef enum {
OPElementTypeCalculatedLong = OPElementTypeCalculated | 0x01,
OPElementTypeCalculatedMedium = OPElementTypeCalculated | 0x02,
OPElementTypeCalculatedShort = OPElementTypeCalculated | 0x03,
OPElementTypeCalculatedBasic = OPElementTypeCalculated | 0x04,
OPElementTypeCalculatedPIN = OPElementTypeCalculated | 0x05,
OPElementTypeStoredPersonal = OPElementTypeStored | 0x01,
OPElementTypeStoredDevicePrivate = OPElementTypeStored | 0x02,
} OPElementType;
NSString *NSStringFromOPElementType(OPElementType type);
NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase);

72
OnePassword/OPTypes.m Normal file
View File

@@ -0,0 +1,72 @@
//
// OPTypes.m
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "OPTypes.h"
NSString *NSStringFromOPElementType(OPElementType type) {
switch (type) {
case OPElementTypeCalculatedLong:
return @"Long";
case OPElementTypeCalculatedMedium:
return @"Medium";
case OPElementTypeCalculatedShort:
return @"Short";
case OPElementTypeCalculatedBasic:
return @"Basic";
case OPElementTypeCalculatedPIN:
return @"PIN";
case OPElementTypeStoredPersonal:
return @"Personal";
case OPElementTypeStoredDevicePrivate:
return @"Device Private";
default:
[NSException raise:NSInternalInconsistencyException format:@"Type not supported: %d", type];
}
}
static NSDictionary *OPTypes_ciphers = nil;
NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase) {
assert(type & OPElementTypeCalculated);
if (OPTypes_ciphers == nil)
OPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers"
withExtension:@"plist"]];
// Determine the hash whose bytes will be used for calculating a password: md4(name-keyPhrase)
assert(name && keyPhrase);
NSData *keyHash = [[NSString stringWithFormat:@"%@-%@", name, keyPhrase] hashWith:PearlDigestMD4];
const char *keyBytes = keyHash.bytes;
// Determine the cipher from the first hash byte.
assert([keyHash length]);
NSArray *typeCiphers = [[OPTypes_ciphers valueForKey:@"OPElementTypeCalculated"] valueForKey:NSStringFromOPElementType(type)];
NSString *cipher = [typeCiphers objectAtIndex:keyBytes[0] % [typeCiphers count]];
// Encode the content, character by character, using subsequent hash bytes and the cipher.
assert([keyHash length] >= [cipher length] + 1);
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
for (NSUInteger c = 0; c < [cipher length]; ++c) {
const char keyByte = keyBytes[c + 1];
NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)];
NSString *cipherClassCharacters = [[OPTypes_ciphers valueForKey:@"OPCharacterClasses"] valueForKey:cipherClass];
[content appendString:[cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)]];
}
return content;
}

View File

@@ -26,6 +26,8 @@
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationExitsOnSuspend</key>
<true/>
<key>UIMainStoryboardFile</key>
<string>MainStoryboard_iPhone</string>
<key>UIMainStoryboardFile~ipad</key>

View File

@@ -14,4 +14,13 @@
#import <CoreData/CoreData.h>
#endif
#import "Pearl-Prefix.pch"
#define PEARL
#define PEARL_CRYPTO
#define PEARL_UIKIT
#import "Pearl.h"
#import "Pearl-Crypto.h"
#import "Pearl-UIKit.h"
#import "OPTypes.h"
#import "OPConfig.h"

View File

@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="Test1.xcdatamodel" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<elements/>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="872" systemVersion="11C74" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="OPElementEntity" representedClassName="OPElementEntity" syncable="YES">
<attribute name="contentType" optional="YES" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
<attribute name="contentUTI" attributeType="String" defaultValueString="public.plain-text" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" indexed="YES" syncable="YES"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="257" syncable="YES"/>
<attribute name="uses" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
</entity>
<entity name="OPElementStoredEntity" representedClassName="OPElementStoredEntity" parentEntity="OPElementEntity" syncable="YES">
<attribute name="contentObject" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity>
<fetchRequest name="OPSearchElement" entity="OPElementEntity" predicateString="name BEGINSWITH &quot;$query&quot;"/>
<elements>
<element name="OPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
<element name="OPElementStoredEntity" positionX="160" positionY="192" width="128" height="75"/>
</elements>
</model>

55
OnePassword/ciphers.plist Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OPElementTypeCalculated</key>
<dict>
<key>Long</key>
<array>
<string>CvcvCvcvnoCvcv</string>
<string>CvcvnoCvcvCvcv</string>
<string>CvcvCvcvCvcvno</string>
</array>
<key>Medium</key>
<array>
<string>CvcnoCvc</string>
<string>CvcCvcno</string>
</array>
<key>Short</key>
<array>
<string>Cvcn</string>
</array>
<key>Basic</key>
<array>
<string>aaanaaan</string>
<string>aannaaan</string>
<string>aaannaaa</string>
</array>
<key>PIN</key>
<array>
<string>nnnn</string>
</array>
</dict>
<key>OPCharacterClasses</key>
<dict>
<key>V</key>
<string>AEIOU</string>
<key>C</key>
<string>BCDFGHJKLMNPQRSTVWXYZ</string>
<key>v</key>
<string>aeiou</string>
<key>c</key>
<string>bcdfghjklmnpqrstvwxyz</string>
<key>A</key>
<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
<key>a</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
<key>n</key>
<string>0123456789</string>
<key>o</key>
<string>!@#$%^&amp;*()</string>
<key>X</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
</dict>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
OnePassword/divider/Box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,11 @@
{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
\f0\fs24 \cf0 Hello,\
Thank you for purchasing my item. \
To use the dividers, just drag and drop the folder containing the divider you want to use. If you want to expand the divider you can resize it. \
If you want to change the color of a divider you can use the color overlay filter. \
Thanks again, if you need help please contact me via GraphicRiver. }

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because it is too large Load Diff

9
OnePassword/types.c Normal file
View File

@@ -0,0 +1,9 @@
//
// types.c
// OnePassword
//
// Created by Maarten Billemont on 02/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#include <stdio.h>