diff --git a/External/Pearl b/External/Pearl
index 3cca78c6..c4cd94ab 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit 3cca78c6ebc874ef3168e674dbb7391f9a6dec52
+Subproject commit c4cd94ab7743a06a0173e3a93e6ea10ab7c7d7f5
diff --git a/OnePassword.sketch/Data b/OnePassword.sketch/Data
new file mode 100644
index 00000000..d7da2f6a
Binary files /dev/null and b/OnePassword.sketch/Data differ
diff --git a/OnePassword.sketch/QuickLook/Preview.pdf b/OnePassword.sketch/QuickLook/Preview.pdf
new file mode 100644
index 00000000..f57c2ed9
Binary files /dev/null and b/OnePassword.sketch/QuickLook/Preview.pdf differ
diff --git a/OnePassword.sketch/QuickLook/Thumbnail.jpg b/OnePassword.sketch/QuickLook/Thumbnail.jpg
new file mode 100644
index 00000000..99f26d5c
Binary files /dev/null and b/OnePassword.sketch/QuickLook/Thumbnail.jpg differ
diff --git a/OnePassword.xcodeproj/project.pbxproj b/OnePassword.xcodeproj/project.pbxproj
index 9b6498a6..e434602a 100644
--- a/OnePassword.xcodeproj/project.pbxproj
+++ b/OnePassword.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
DA007F5514B25EE100251337 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA007F5414B25EE100251337 /* ciphers.plist */; };
DA007F5614B26EFA00251337 /* Pearl.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAC77CD41482AAD600BCF976 /* Pearl.strings */; };
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; };
+ DA0A848C14C4DFCB0090EA8E /* OPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0A848B14C4DFCB0090EA8E /* OPElementGeneratedEntity.m */; };
DA0B951114C2D69E001D4EB1 /* help.html in Resources */ = {isa = PBXBuildFile; fileRef = DA0B951014C2D69E001D4EB1 /* help.html */; };
DA0B966914C37487001D4EB1 /* addressbook-person@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0B951314C37486001D4EB1 /* addressbook-person@2x.png */; };
DA0B966A14C37487001D4EB1 /* addressbook.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0B951414C37486001D4EB1 /* addressbook.png */; };
@@ -372,7 +373,8 @@
DA5BFA67147E415C00F98B1E /* OPMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5BFA66147E415C00F98B1E /* OPMainViewController.m */; };
DA5DB7A614BE4B19002DD256 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5DB7A514BE4B19002DD256 /* Default.png */; };
DA5DB7A814BE4B4B002DD256 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5DB7A714BE4B4B002DD256 /* Default@2x.png */; };
- DABF7DA514BE54E4007F3557 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = DABF7DA414BE54E4007F3557 /* Default.png */; };
+ DA7659AF14C5E22E00769249 /* Square-bottom.png in Resources */ = {isa = PBXBuildFile; fileRef = DA7659AD14C5E22E00769249 /* Square-bottom.png */; };
+ DA7659B014C5E22E00769249 /* Square-top.png in Resources */ = {isa = PBXBuildFile; fileRef = DA7659AE14C5E22E00769249 /* Square-top.png */; };
DAC6325E1486805C0075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC63277148680700075AEA5 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
@@ -452,6 +454,7 @@
DAC781321482AAD800BCF976 /* WebViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC780EF1482AAD700BCF976 /* WebViewController.h */; };
DAC781331482AAD800BCF976 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC780F01482AAD700BCF976 /* WebViewController.m */; };
DAC781361482E67300BCF976 /* OPRecentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC781351482E67300BCF976 /* OPRecentViewController.m */; };
+ DADC3C4D14C62B350091CB4D /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DADC3C4C14C62B350091CB4D /* Settings.bundle */; };
DAE2C648148247E500BA6B10 /* OPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE2C646148247E500BA6B10 /* OPTypeViewController.m */; };
DAE998D214C1D2A0002D7C22 /* Content-Backdrop.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */; };
DAE998D314C1D2A0002D7C22 /* Content-Backdrop@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987A14C1D2A0002D7C22 /* Content-Backdrop@2x.png */; };
@@ -616,6 +619,8 @@
DA007F5114B24DCD00251337 /* OPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPConfig.m; sourceTree = ""; };
DA007F5414B25EE100251337 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = ""; };
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
+ DA0A848A14C4DFCB0090EA8E /* OPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPElementGeneratedEntity.h; sourceTree = ""; };
+ DA0A848B14C4DFCB0090EA8E /* OPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPElementGeneratedEntity.m; sourceTree = ""; };
DA0B951014C2D69E001D4EB1 /* help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = help.html; path = Resources/help.html; sourceTree = ""; };
DA0B951314C37486001D4EB1 /* addressbook-person@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "addressbook-person@2x.png"; sourceTree = ""; };
DA0B951414C37486001D4EB1 /* addressbook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = addressbook.png; sourceTree = ""; };
@@ -987,7 +992,8 @@
DA5BFA66147E415C00F98B1E /* OPMainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OPMainViewController.m; sourceTree = ""; };
DA5DB7A514BE4B19002DD256 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = ../Default.png; sourceTree = ""; };
DA5DB7A714BE4B4B002DD256 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "../Default@2x.png"; sourceTree = ""; };
- DABF7DA414BE54E4007F3557 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = SOURCE_ROOT; };
+ DA7659AD14C5E22E00769249 /* Square-bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Square-bottom.png"; sourceTree = ""; };
+ DA7659AE14C5E22E00769249 /* Square-top.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Square-top.png"; sourceTree = ""; };
DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libuicolor-utilities.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DAC6326C148680650075AEA5 /* libjrswizzle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjrswizzle.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAC632791486809A0075AEA5 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JRSwizzle.h; path = External/Pearl/External/jrswizzle/JRSwizzle.h; sourceTree = SOURCE_ROOT; };
@@ -2070,6 +2076,7 @@
DAC780F01482AAD700BCF976 /* WebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = ""; };
DAC781341482E67300BCF976 /* OPRecentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPRecentViewController.h; sourceTree = ""; };
DAC781351482E67300BCF976 /* OPRecentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPRecentViewController.m; sourceTree = ""; };
+ DADC3C4C14C62B350091CB4D /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; };
DAE2C645148247E500BA6B10 /* OPTypeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPTypeViewController.h; sourceTree = ""; };
DAE2C646148247E500BA6B10 /* OPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPTypeViewController.m; sourceTree = ""; };
DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Content-Backdrop.png"; path = "Resources/Content-Backdrop.png"; sourceTree = ""; };
@@ -2658,6 +2665,7 @@
DA34DA1514B1BEA100F721C1 /* OPTypes.m */,
DA007F5014B24DCC00251337 /* OPConfig.h */,
DA007F5114B24DCD00251337 /* OPConfig.m */,
+ DADC3C4C14C62B350091CB4D /* Settings.bundle */,
);
path = OnePassword;
sourceTree = "";
@@ -2704,6 +2712,8 @@
children = (
DA34DA0F14B1BC7E00F721C1 /* OPElementEntity.h */,
DA34DA1014B1BC7E00F721C1 /* OPElementEntity.m */,
+ DA0A848A14C4DFCB0090EA8E /* OPElementGeneratedEntity.h */,
+ DA0A848B14C4DFCB0090EA8E /* OPElementGeneratedEntity.m */,
DA34DA0B14B1BC7D00F721C1 /* OPElementStoredEntity.h */,
DA34DA0C14B1BC7D00F721C1 /* OPElementStoredEntity.m */,
);
@@ -2789,7 +2799,6 @@
DAC77CD31482AAD600BCF976 /* Resources */ = {
isa = PBXGroup;
children = (
- DABF7DA414BE54E4007F3557 /* Default.png */,
DAC77CD41482AAD600BCF976 /* Pearl.strings */,
);
path = Resources;
@@ -3878,6 +3887,8 @@
DAE9987B14C1D2A0002D7C22 /* Dividers */ = {
isa = PBXGroup;
children = (
+ DA7659AD14C5E22E00769249 /* Square-bottom.png */,
+ DA7659AE14C5E22E00769249 /* Square-top.png */,
DAE9987C14C1D2A0002D7C22 /* Bold_Lines.png */,
DAE9987D14C1D2A0002D7C22 /* Box.png */,
DAE9987E14C1D2A0002D7C22 /* Dashed_Divider.png */,
@@ -4192,7 +4203,6 @@
DA007F5614B26EFA00251337 /* Pearl.strings in Resources */,
DA5DB7A614BE4B19002DD256 /* Default.png in Resources */,
DA5DB7A814BE4B4B002DD256 /* Default@2x.png in Resources */,
- DABF7DA514BE54E4007F3557 /* Default.png in Resources */,
DAE998D214C1D2A0002D7C22 /* Content-Backdrop.png in Resources */,
DAE998D314C1D2A0002D7C22 /* Content-Backdrop@2x.png in Resources */,
DAE998D414C1D2A0002D7C22 /* Bold_Lines.png in Resources */,
@@ -4668,6 +4678,9 @@
DA0B97BC14C37487001D4EB1 /* volume-mute@2x.png in Resources */,
DA0B97BD14C37487001D4EB1 /* wrench.png in Resources */,
DA0B97BE14C37487001D4EB1 /* wrench@2x.png in Resources */,
+ DA7659AF14C5E22E00769249 /* Square-bottom.png in Resources */,
+ DA7659B014C5E22E00769249 /* Square-top.png in Resources */,
+ DADC3C4D14C62B350091CB4D /* Settings.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4690,6 +4703,7 @@
DA007F5214B24DCD00251337 /* OPConfig.m in Sources */,
DA55B29E14B38272001131B7 /* OPContentViewController.m in Sources */,
DA55B2A214B4EB47001131B7 /* OPSearchDelegate.m in Sources */,
+ DA0A848C14C4DFCB0090EA8E /* OPElementGeneratedEntity.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/OnePassword/OPAppDelegate.h b/OnePassword/OPAppDelegate.h
index 54819b7b..97224499 100644
--- a/OnePassword/OPAppDelegate.h
+++ b/OnePassword/OPAppDelegate.h
@@ -16,6 +16,7 @@
@property (strong, nonatomic) NSString *keyPhrase;
+ (OPAppDelegate *)get;
++ (NSManagedObjectContext *)managedObjectContext;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
diff --git a/OnePassword/OPAppDelegate.m b/OnePassword/OPAppDelegate.m
index 00fd8e0c..48a6cad6 100644
--- a/OnePassword/OPAppDelegate.m
+++ b/OnePassword/OPAppDelegate.m
@@ -10,6 +10,13 @@
#import "OPMainViewController.h"
+@interface OPAppDelegate ()
+
++ (NSDictionary *)keyPhraseQuery;
++ (NSDictionary *)keyPhraseHashQuery;
+
+@end
+
@implementation OPAppDelegate
@synthesize managedObjectContext = __managedObjectContext;
@@ -22,26 +29,63 @@
[Logger get].autoprintLevel = LogLevelDebug;
}
-- (void)applicationWillResignActive:(UIApplication *)application {
++ (NSDictionary *)keyPhraseQuery {
- if (![[OPConfig get].rememberKeyPhrase boolValue])
- self.keyPhrase = nil;
+ static NSDictionary *OPKeyPhraseQuery = nil;
+ if (!OPKeyPhraseQuery)
+ OPKeyPhraseQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
+ attributes:[NSDictionary dictionaryWithObject:@"MasterKeyPhrase"
+ forKey:(__bridge id)kSecAttrService]
+ matches:nil];
+
+ return OPKeyPhraseQuery;
+}
+
++ (NSDictionary *)keyPhraseHashQuery {
+
+ static NSDictionary *OPKeyPhraseHashQuery = nil;
+ if (!OPKeyPhraseHashQuery)
+ OPKeyPhraseHashQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
+ attributes:[NSDictionary dictionaryWithObject:@"MasterKeyPhraseHash"
+ forKey:(__bridge id)kSecAttrService]
+ matches:nil];
+
+ return OPKeyPhraseHashQuery;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
- if (!self.keyPhrase)
+ if ([[OPConfig get].storeKeyPhrase boolValue]) {
+ // Key phrase is stored in keychain. Load it.
+ dbg(@"Loading master key phrase from key chain.");
+ NSData *keyPhraseData = [KeyChain dataOfItemForQuery:[OPAppDelegate keyPhraseQuery]];
+ dbg(@" -> Master key phrase %@.", keyPhraseData? @"found": @"NOT found");
+
+ self.keyPhrase = keyPhraseData? [[NSString alloc] initWithBytes:keyPhraseData.bytes length:keyPhraseData.length
+ encoding:NSUTF8StringEncoding]: nil;
+ } else {
+ // Key phrase should not be stored in keychain. Delete it.
+ dbg(@"Deleting master key phrase from key chain.");
+ [KeyChain deleteItemForQuery:[OPAppDelegate keyPhraseQuery]];
+ }
+
+ if (!self.keyPhrase) {
+ // Key phrase is not known. Ask user to set/specify it.
+ dbg(@"Key phrase not known. Will ask user.");
+
dispatch_async(dispatch_get_main_queue(), ^{
- NSString *keyPhraseHash = [OPConfig get].keyPhraseHash;
+ NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[OPAppDelegate keyPhraseHashQuery]];
+ dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
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)
+ if (!buttonIndex)
exit(0);
if (![answer length]) {
+ // User didn't enter a key phrase.
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:@"No master password entered."
tappedButtonBlock:
@@ -50,9 +94,13 @@
} cancelTitle:@"Quit" otherTitles:nil];
}
- NSString *answerHash = [[answer hashWith:PearlDigestSHA1] encodeHex];
- if (keyPhraseHash) {
- if (![keyPhraseHash isEqualToString:answerHash]) {
+ NSData *answerHash = [answer hashWith:PearlDigestSHA512];
+ if (keyPhraseHash)
+ // A key phrase hash is known -> a key phrase is set.
+ // Make sure the user's entered key phrase matches it.
+ if (![keyPhraseHash isEqual:answerHash]) {
+ dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
+
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:@"Incorrect master password."
tappedButtonBlock:
@@ -62,16 +110,23 @@
return;
}
- } else
- [OPConfig get].keyPhraseHash = answerHash;
self.keyPhrase = answer;
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
keyPhraseAlert.alertField.autocapitalizationType = UITextAutocapitalizationTypeNone;
keyPhraseAlert.alertField.autocorrectionType = UITextAutocorrectionTypeNo;
+ keyPhraseAlert.alertField.enablesReturnKeyAutomatically = YES;
+ keyPhraseAlert.alertField.returnKeyType = UIReturnKeyGo;
keyPhraseAlert.alertField.secureTextEntry = YES;
[keyPhraseAlert showAlert];
});
+ }
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+
+ if (![[OPConfig get].rememberKeyPhrase boolValue])
+ self.keyPhrase = nil;
}
- (void)applicationWillTerminate:(UIApplication *)application
@@ -85,6 +140,11 @@
return (OPAppDelegate *)[super get];
}
++ (NSManagedObjectContext *)managedObjectContext{
+
+ return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
+}
+
- (void)saveContext
{
NSError *error = nil;
@@ -99,6 +159,25 @@
}
}
+- (void)setKeyPhrase:(NSString *)keyPhrase {
+
+ _keyPhrase = keyPhrase;
+
+ if (keyPhrase) {
+ NSData *keyPhraseHash = [keyPhrase hashWith:PearlDigestSHA512];
+ dbg(@"Updating master key phrase hash to: %@.", keyPhraseHash);
+ [KeyChain addOrUpdateItemForQuery:[OPAppDelegate keyPhraseHashQuery]
+ withAttributes:[NSDictionary dictionaryWithObject:keyPhraseHash
+ forKey:(__bridge id)kSecValueData]];
+ if ([[OPConfig get].storeKeyPhrase boolValue]) {
+ dbg(@"Storing master key phrase in key chain.");
+ [KeyChain addOrUpdateItemForQuery:[OPAppDelegate keyPhraseQuery]
+ withAttributes:[NSDictionary dictionaryWithObject:[keyPhrase dataUsingEncoding:NSUTF8StringEncoding]
+ forKey:(__bridge id)kSecValueData]];
+ }
+ }
+}
+
#pragma mark - Core Data stack
/**
diff --git a/OnePassword/OPConfig.h b/OnePassword/OPConfig.h
index a36bda3e..4efc68c2 100644
--- a/OnePassword/OPConfig.h
+++ b/OnePassword/OPConfig.h
@@ -9,8 +9,9 @@
@interface OPConfig : Config
@property (nonatomic, retain) NSNumber *dataStoreError;
-@property (nonatomic, retain) NSString *keyPhraseHash;
+@property (nonatomic, retain) NSNumber *storeKeyPhrase;
@property (nonatomic, retain) NSNumber *rememberKeyPhrase;
+@property (nonatomic, retain) NSNumber *helpHidden;
+ (OPConfig *)get;
diff --git a/OnePassword/OPConfig.m b/OnePassword/OPConfig.m
index fe1b47dd..c3710f85 100644
--- a/OnePassword/OPConfig.m
+++ b/OnePassword/OPConfig.m
@@ -10,18 +10,19 @@
@implementation OPConfig
-@dynamic dataStoreError, keyPhraseHash, rememberKeyPhrase;
+@dynamic dataStoreError, storeKeyPhrase, rememberKeyPhrase, helpHidden;
--(id) init {
+- (id)init {
if(!(self = [super init]))
return self;
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)),
+ [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKeyPhrase)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(rememberKeyPhrase)),
-
+ [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
nil]];
return self;
diff --git a/OnePassword/OPElementEntity.m b/OnePassword/OPElementEntity.m
index 52355361..64e35364 100644
--- a/OnePassword/OPElementEntity.m
+++ b/OnePassword/OPElementEntity.m
@@ -7,7 +7,6 @@
//
#import "OPElementEntity.h"
-#import "OPAppDelegate.h"
@implementation OPElementEntity
@@ -27,14 +26,7 @@
- (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];
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Content implementation missing." userInfo:nil];
}
- (NSString *)contentDescription {
diff --git a/OnePassword/OPElementGeneratedEntity.h b/OnePassword/OPElementGeneratedEntity.h
new file mode 100644
index 00000000..d4652ffa
--- /dev/null
+++ b/OnePassword/OPElementGeneratedEntity.h
@@ -0,0 +1,18 @@
+//
+// OPElementGeneratedEntity.h
+// OnePassword
+//
+// Created by Maarten Billemont on 16/01/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+#import
+#import "OPElementEntity.h"
+
+
+@interface OPElementGeneratedEntity : OPElementEntity
+
+@property (nonatomic) int16_t counter;
+
+@end
diff --git a/OnePassword/OPElementGeneratedEntity.m b/OnePassword/OPElementGeneratedEntity.m
new file mode 100644
index 00000000..4380d50d
--- /dev/null
+++ b/OnePassword/OPElementGeneratedEntity.m
@@ -0,0 +1,29 @@
+//
+// OPElementGeneratedEntity.m
+// OnePassword
+//
+// Created by Maarten Billemont on 16/01/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import "OPElementGeneratedEntity.h"
+#import "OPAppDelegate.h"
+
+
+@implementation OPElementGeneratedEntity
+
+@dynamic counter;
+
+- (id)content {
+
+ if (![self.name length])
+ return nil;
+
+ if (self.type & OPElementTypeCalculated)
+ return OPCalculateContent(self.type, self.name, [OPAppDelegate get].keyPhrase, self.counter);
+
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
+ reason:[NSString stringWithFormat:@"Unsupported type: %d", self.type] userInfo:nil];
+}
+
+@end
diff --git a/OnePassword/OPMainViewController.h b/OnePassword/OPMainViewController.h
index e5c94d4f..02acca25 100644
--- a/OnePassword/OPMainViewController.h
+++ b/OnePassword/OPMainViewController.h
@@ -20,9 +20,14 @@
@property (weak, nonatomic) IBOutlet UILabel *siteName;
@property (weak, nonatomic) IBOutlet UILabel *passwordCounter;
@property (weak, nonatomic) IBOutlet UIButton *passwordIncrementer;
+@property (weak, nonatomic) IBOutlet UIButton *passwordEdit;
+@property (weak, nonatomic) IBOutlet UIView *contentContainer;
+@property (weak, nonatomic) IBOutlet UIView *helpContainer;
-- (IBAction)didChangeContentType:(UISegmentedControl *)sender;
-- (IBAction)didTriggerContent;
-- (IBAction)didIncrementPasswordCounter;
+- (IBAction)copyContent;
+- (IBAction)incrementPasswordCounter;
+- (IBAction)editPassword;
+- (IBAction)toggleHelp;
+- (void)toggleHelp:(BOOL)hidden;
@end
diff --git a/OnePassword/OPMainViewController.m b/OnePassword/OPMainViewController.m
index fb4c7c2f..01371b6d 100644
--- a/OnePassword/OPMainViewController.m
+++ b/OnePassword/OPMainViewController.m
@@ -9,6 +9,8 @@
#import "OPMainViewController.h"
#import "OPAppDelegate.h"
#import "OPContentViewController.h"
+#import "OPElementGeneratedEntity.h"
+#import "OPElementStoredEntity.h"
#import
@@ -28,6 +30,9 @@
@synthesize siteName = _siteName;
@synthesize passwordCounter = _passwordCounter;
@synthesize passwordIncrementer = _passwordIncrementer;
+@synthesize passwordEdit = _passwordEdit;
+@synthesize contentContainer = _contentContainer;
+@synthesize helpContainer = _helpContainer;
@synthesize contentField = _contentField;
#pragma mark - View lifecycle
@@ -49,6 +54,7 @@
[super viewWillAppear:animated];
+ [self toggleHelp:[[OPConfig get].helpHidden boolValue]];
[self updateAnimated:NO];
}
@@ -56,7 +62,7 @@
// Because IB's edit button doesn't auto-toggle self.editable like editButtonItem does.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
-
+
[super viewDidLoad];
}
@@ -69,6 +75,9 @@
[self setSiteName:nil];
[self setPasswordCounter:nil];
[self setPasswordIncrementer:nil];
+ [self setPasswordEdit:nil];
+ [self setContentContainer:nil];
+ [self setHelpContainer:nil];
[super viewDidUnload];
}
@@ -80,7 +89,7 @@
}
- (void)updateAnimated:(BOOL)animated {
-
+
[[OPAppDelegate get] saveContext];
if (animated)
@@ -92,28 +101,27 @@
}
- (void)updateWasAnimated:(BOOL)animated {
-
+
NSUInteger chapter = self.activeElement? 2: 1;
[self.helpView loadRequest:
- [NSURLRequest requestWithURL:
- [NSURL URLWithString:[NSString stringWithFormat:@"#%d", chapter] relativeToURL:
- [[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]];
-
+ [NSURLRequest requestWithURL:
+ [NSURL URLWithString:[NSString stringWithFormat:@"#%d", chapter] relativeToURL:
+ [[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]];
+
[self.navigationItem setRightBarButtonItem:self.activeElement.type & OPElementTypeStored? self.editButtonItem: nil animated:animated];
-
- self.searchDisplayController.searchBar.placeholder = self.activeElement.name;
+
self.siteName.text = self.activeElement.name;
- self.passwordCounter.alpha = self.activeElement.type & OPElementTypeCalculated? 1: 0;
- self.passwordIncrementer.alpha = self.activeElement.type & OPElementTypeCalculated? 1: 0;
-
+ self.passwordCounter.alpha = self.activeElement.type & OPElementTypeCalculated? 0.5f: 0;
+ self.passwordIncrementer.alpha = self.activeElement.type & OPElementTypeCalculated? 0.5f: 0;
+ self.passwordEdit.alpha = self.activeElement.type & OPElementTypeStored? 0.5f: 0;
+
[self.typeButton setTitle:NSStringFromOPElementType(self.activeElement.type)
forState:UIControlStateNormal];
-
- self.contentField.enabled = self.editing && self.activeElement.type & OPElementTypeStored;
- self.contentField.clearButtonMode = self.contentField.enabled? UITextFieldViewModeAlways: UITextFieldViewModeNever;
- self.contentField.text = @"...";
-
+ self.typeButton.alpha = NSStringFromOPElementType(self.activeElement.type).length? 1: 0;
+
+ self.contentField.enabled = NO;
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *contentDescription = self.activeElement.contentDescription;
dispatch_async(dispatch_get_main_queue(), ^{
@@ -124,34 +132,103 @@
#pragma mark - Protocols
-- (IBAction)didChangeContentType:(UISegmentedControl *)sender {
-
- [self updateAnimated:YES];
-}
-
-- (IBAction)didTriggerContent:(id)sender {
+- (IBAction)copyContent {
[[UIPasteboard generalPasteboard] setValue:self.activeElement.content
forPasteboardType:self.activeElement.contentUTI];
}
-- (IBAction)didIncrementPasswordCounter {
+- (IBAction)incrementPasswordCounter {
+
+ if ([self.activeElement isKindOfClass:[OPElementGeneratedEntity class]])
+ [AlertViewController showAlertWithTitle:@"Change Password"
+ message:l(@"Setting a new password for %@.\n"
+ @"Don't forget to update your password on the site as well!", self.activeElement.name)
+ tappedButtonBlock:^(NSInteger buttonIndex) {
+ if (!buttonIndex)
+ return;
+
+ // Update password counter.
+ if ([self.activeElement isKindOfClass:[OPElementGeneratedEntity class]]) {
+ ++((OPElementGeneratedEntity *) self.activeElement).counter;
+ [self updateAnimated:YES];
+ }
+ } cancelTitle:[PearlStrings get].commonButtonAbort otherTitles:[PearlStrings get].commonButtonThanks, nil];
+}
+
+- (IBAction)editPassword {
+
+ if (self.activeElement.type & OPElementTypeStored) {
+ self.contentField.enabled = YES;
+ [self.contentField becomeFirstResponder];
+ }
+}
+
+- (IBAction)toggleHelp {
+
+ [UIView animateWithDuration:0.3f animations:^{
+ if (self.helpContainer.frame.origin.y < 400)
+ [self toggleHelp:YES];
+ else
+ [self toggleHelp:NO];
+ }];
+}
+
+- (void)toggleHelp:(BOOL)hidden {
+
+ if (hidden) {
+ self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 373);
+ //self.helpContainer.frame = CGRectSetHeight(self.helpContainer.frame, 0);
+ self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 414);
+ [OPConfig get].helpHidden = [NSNumber numberWithBool:YES];
+ } else {
+ self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 155);
+ //self.helpContainer.frame = CGRectSetHeight(self.helpContainer.frame, 219);
+ self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 196);
+ [OPConfig get].helpHidden = [NSNumber numberWithBool:NO];
+ }
}
- (void)didSelectType:(OPElementType)type {
- self.activeElement.type = type;
- [self updateAnimated:YES];
+ [AlertViewController showAlertWithTitle:@"Change Password Type"
+ message:l(@"Changing the type of %@'s password.\n"
+ @"Don't forget to update your password on the site as well!", self.activeElement.name)
+ tappedButtonBlock:^(NSInteger buttonIndex) {
+ if (!buttonIndex)
+ return;
+
+ // Update password type.
+ if (ClassForOPElementType(type) != ClassForOPElementType(self.activeElement.type)) {
+ // Type requires a different class of element. Recreate the element.
+ OPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(ClassForOPElementType(type))
+ inManagedObjectContext:[OPAppDelegate managedObjectContext]];
+
+ newElement.name = self.activeElement.name;
+ newElement.uses = self.activeElement.uses;
+ newElement.lastUsed = self.activeElement.lastUsed;
+ newElement.contentUTI = self.activeElement.contentUTI;
+ newElement.contentType = self.activeElement.contentType;
+
+ [[OPAppDelegate managedObjectContext] deleteObject:self.activeElement];
+ self.activeElement = newElement;
+ }
+ self.activeElement.type = type;
+
+ // Redraw.
+ [self updateAnimated:YES];
+ } cancelTitle:[PearlStrings get].commonButtonAbort otherTitles:[PearlStrings get].commonButtonThanks, nil];
}
- (void)didSelectElement:(OPElementEntity *)element {
self.activeElement = element;
[self.activeElement use];
- [self updateAnimated:YES];
-
- self.searchDisplayController.searchBar.text = @"";
+
[self.searchDisplayController setActive:NO animated:YES];
+ self.searchDisplayController.searchBar.text = self.activeElement.name;
+
+ [self updateAnimated:YES];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
@@ -159,4 +236,32 @@
[self updateAnimated:YES];
}
+- (BOOL)textFieldShouldReturn:(UITextField *)textField {
+
+ if (textField == self.contentField)
+ [self.contentField resignFirstResponder];
+
+ return YES;
+}
+
+- (void)textFieldDidEndEditing:(UITextField *)textField {
+
+ if (textField == self.contentField) {
+ self.contentField.enabled = NO;
+ [AlertViewController showAlertWithTitle:@"Change Password"
+ message:l(@"Setting a new password for %@.\n"
+ @"Don't forget to update your password on the site as well!", self.activeElement.name)
+ tappedButtonBlock:^(NSInteger buttonIndex) {
+ if (buttonIndex) {
+ // Update password content.
+ if ([self.activeElement isKindOfClass:[OPElementStoredEntity class]])
+ ((OPElementStoredEntity *) self.activeElement).contentObject = self.contentField.text;
+ }
+
+ // Redraw.
+ [self updateAnimated:YES];
+ } cancelTitle:[PearlStrings get].commonButtonAbort otherTitles:[PearlStrings get].commonButtonThanks, nil];
+ }
+}
+
@end
diff --git a/OnePassword/OPSearchDelegate.m b/OnePassword/OPSearchDelegate.m
index 45374f55..46d4afbf 100644
--- a/OnePassword/OPSearchDelegate.m
+++ b/OnePassword/OPSearchDelegate.m
@@ -8,10 +8,10 @@
#import "OPSearchDelegate.h"
#import "OPAppDelegate.h"
+#import "OPElementGeneratedEntity.h"
@interface OPSearchDelegate (Private)
-- (NSManagedObjectContext *)managedObjectContext;
- (void)update;
@end
@@ -21,41 +21,47 @@
@synthesize delegate;
@synthesize searchDisplayController;
-- (NSManagedObjectContext *)managedObjectContext {
-
- return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
+- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
+
+ self.searchDisplayController.searchBar.text = @"";
+ self.searchDisplayController.searchBar.prompt = @"Enter the site's domain name (eg. apple.com):";
+ [self update];
}
-- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
+- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
+
+ self.searchDisplayController.searchBar.prompt = nil;
+}
- [self.searchDisplayController.searchResultsTableView setEditing:self.searchDisplayController.searchContentsController.editing animated:NO];
- [self update];
+- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
+
+ [tableView setEditing:self.searchDisplayController.searchContentsController.editing animated:NO];
}
- (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]]];
+ [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
[fetchRequest setPredicate:
- [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", text]];
-
+ [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", text]];
+
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
- managedObjectContext:[self managedObjectContext]
+ managedObjectContext:[OPAppDelegate managedObjectContext]
sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
-
+
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
@@ -151,7 +157,7 @@
cell.detailTextLabel.text = @"New";
} else {
OPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
-
+
cell.textLabel.text = element.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", element.uses];
}
@@ -162,8 +168,8 @@
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 = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([OPElementGeneratedEntity class])
+ inManagedObjectContext:[OPAppDelegate managedObjectContext]];
element.name = self.searchDisplayController.searchBar.text;
} else
element = [self.fetchedResultsController objectAtIndexPath:indexPath];
@@ -190,13 +196,13 @@
}
- (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];
+
+ [[OPAppDelegate managedObjectContext] deleteObject:element];
}
}
diff --git a/OnePassword/OPTypes.h b/OnePassword/OPTypes.h
index fba3949e..7ae7963a 100644
--- a/OnePassword/OPTypes.h
+++ b/OnePassword/OPTypes.h
@@ -31,4 +31,5 @@ typedef enum {
} OPElementType;
NSString *NSStringFromOPElementType(OPElementType type);
-NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase);
\ No newline at end of file
+Class ClassForOPElementType(OPElementType type);
+NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase, int counter);
\ No newline at end of file
diff --git a/OnePassword/OPTypes.m b/OnePassword/OPTypes.m
index 4217d2e9..f4c76d4e 100644
--- a/OnePassword/OPTypes.m
+++ b/OnePassword/OPTypes.m
@@ -7,6 +7,8 @@
//
#import "OPTypes.h"
+#import "OPElementGeneratedEntity.h"
+#import "OPElementStoredEntity.h"
NSString *NSStringFromOPElementType(OPElementType type) {
@@ -41,8 +43,40 @@ NSString *NSStringFromOPElementType(OPElementType type) {
}
}
+Class ClassForOPElementType(OPElementType type) {
+
+ if (!type)
+ return nil;
+
+ switch (type) {
+ case OPElementTypeCalculatedLong:
+ return [OPElementGeneratedEntity class];
+
+ case OPElementTypeCalculatedMedium:
+ return [OPElementGeneratedEntity class];
+
+ case OPElementTypeCalculatedShort:
+ return [OPElementGeneratedEntity class];
+
+ case OPElementTypeCalculatedBasic:
+ return [OPElementGeneratedEntity class];
+
+ case OPElementTypeCalculatedPIN:
+ return [OPElementGeneratedEntity class];
+
+ case OPElementTypeStoredPersonal:
+ return [OPElementStoredEntity class];
+
+ case OPElementTypeStoredDevicePrivate:
+ return [OPElementStoredEntity class];
+
+ default:
+ [NSException raise:NSInternalInconsistencyException format:@"Type not supported: %d", type];
+ }
+}
+
static NSDictionary *OPTypes_ciphers = nil;
-NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase) {
+NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPhrase, int counter) {
assert(type & OPElementTypeCalculated);
@@ -52,7 +86,7 @@ NSString *OPCalculateContent(OPElementType type, NSString *name, NSString *keyPh
// 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];
+ NSData *keyHash = [[NSString stringWithFormat:@"%@-%@-%d", name, keyPhrase, counter] hashWith:PearlDigestMD4];
const char *keyBytes = keyHash.bytes;
// Determine the cipher from the first hash byte.
diff --git a/OnePassword/OnePassword.xcdatamodeld/OnePassword.xcdatamodel/contents b/OnePassword/OnePassword.xcdatamodeld/OnePassword.xcdatamodel/contents
index ef43b69d..0a99d651 100644
--- a/OnePassword/OnePassword.xcdatamodeld/OnePassword.xcdatamodel/contents
+++ b/OnePassword/OnePassword.xcdatamodeld/OnePassword.xcdatamodel/contents
@@ -1,6 +1,6 @@
-
+
@@ -8,12 +8,16 @@
+
+
+
-
+
-
+
+
\ No newline at end of file
diff --git a/OnePassword/Resources/Content-Backdrop.png b/OnePassword/Resources/Content-Backdrop.png
index 1024ba1d..82742167 100644
Binary files a/OnePassword/Resources/Content-Backdrop.png and b/OnePassword/Resources/Content-Backdrop.png differ
diff --git a/OnePassword/Resources/Content-Backdrop@2x.png b/OnePassword/Resources/Content-Backdrop@2x.png
index b74f99d0..6901f5ca 100644
Binary files a/OnePassword/Resources/Content-Backdrop@2x.png and b/OnePassword/Resources/Content-Backdrop@2x.png differ
diff --git a/OnePassword/Resources/Dividers/Square-bottom.png b/OnePassword/Resources/Dividers/Square-bottom.png
new file mode 100644
index 00000000..c6d254ca
Binary files /dev/null and b/OnePassword/Resources/Dividers/Square-bottom.png differ
diff --git a/OnePassword/Resources/Dividers/Square-top.png b/OnePassword/Resources/Dividers/Square-top.png
new file mode 100644
index 00000000..a9c9cfed
Binary files /dev/null and b/OnePassword/Resources/Dividers/Square-top.png differ
diff --git a/OnePassword/Resources/Icon-72.png b/OnePassword/Resources/Icon-72.png
index 4277a5ce..f5d2f9b3 100644
Binary files a/OnePassword/Resources/Icon-72.png and b/OnePassword/Resources/Icon-72.png differ
diff --git a/OnePassword/Resources/Icon-Small-50.png b/OnePassword/Resources/Icon-Small-50.png
index 21c1b05d..c865c0ff 100644
Binary files a/OnePassword/Resources/Icon-Small-50.png and b/OnePassword/Resources/Icon-Small-50.png differ
diff --git a/OnePassword/Resources/Icon-Small.png b/OnePassword/Resources/Icon-Small.png
index fc6f76d7..9462256a 100644
Binary files a/OnePassword/Resources/Icon-Small.png and b/OnePassword/Resources/Icon-Small.png differ
diff --git a/OnePassword/Resources/Icon-Small@2x.png b/OnePassword/Resources/Icon-Small@2x.png
index 8732dafc..dbb45f20 100644
Binary files a/OnePassword/Resources/Icon-Small@2x.png and b/OnePassword/Resources/Icon-Small@2x.png differ
diff --git a/OnePassword/Resources/Icon.png b/OnePassword/Resources/Icon.png
index 86fa6bd4..9823eddd 100644
Binary files a/OnePassword/Resources/Icon.png and b/OnePassword/Resources/Icon.png differ
diff --git a/OnePassword/Resources/Icon@2x.png b/OnePassword/Resources/Icon@2x.png
index de102215..7a4dc1b3 100644
Binary files a/OnePassword/Resources/Icon@2x.png and b/OnePassword/Resources/Icon@2x.png differ
diff --git a/OnePassword/Resources/help.html b/OnePassword/Resources/help.html
index e891a9e1..e4085fbf 100644
--- a/OnePassword/Resources/help.html
+++ b/OnePassword/Resources/help.html
@@ -8,17 +8,22 @@
text-shadow: 0 1px black;
font: 16px "Baskerville";
}
- h1 {
+ h1, h2 {
margin-top: 1em;
padding-top: 1em;
-
- font: inherit;
+ font-family: inherit;
font-weight: bold;
}
+ h2 {
+ font-size: inherit;
+ }
- — 1 —
+ One Password
+ by Lyndir
+
+ — 1 —
Find the site that you need a password for by entering it into the search field .
@@ -27,19 +32,20 @@
The counter shows how many times you've generated a password for the site.
- — 2 —
+ — 2 —
- Your site 's password is now displayed.
+ The site 's password is now displayed.
- Below it you can set the password type . Some types auto-generate passwords,
+ Below it you can set the site's password type . Some types auto-generate a password,
others let you choose your own .
If your new password is not deemed valid by the site, try changing the password type.
- To generate a new password for the site, you can increment its password counter .
+ To generate a new password for the site, you can increment the site's password counter .
+ Do this when, for instance, you've had to share the password with somebody else.
diff --git a/OnePassword/Settings.bundle/Root.plist b/OnePassword/Settings.bundle/Root.plist
new file mode 100644
index 00000000..31a60959
--- /dev/null
+++ b/OnePassword/Settings.bundle/Root.plist
@@ -0,0 +1,61 @@
+
+
+
+
+ PreferenceSpecifiers
+
+
+ Type
+ PSTitleValueSpecifier
+ Title
+ Version
+ Key
+ version
+
+
+ Type
+ PSTitleValueSpecifier
+ Title
+ Build
+ Key
+ build
+
+
+ Type
+ PSTitleValueSpecifier
+ Title
+ Copyright
+ Key
+ copyright
+
+
+ Type
+ PSGroupSpecifier
+ Title
+ Security
+
+
+ DefaultValue
+
+ Key
+ storeKeyPhrase
+ Title
+ Store master password
+ Type
+ PSToggleSwitchSpecifier
+
+
+ Type
+ PSToggleSwitchSpecifier
+ Title
+ Remember master password
+ Key
+ rememberKeyPhrase
+ DefaultValue
+
+
+
+ StringsTable
+ Root
+
+
diff --git a/OnePassword/Settings.bundle/en.lproj/Root.strings b/OnePassword/Settings.bundle/en.lproj/Root.strings
new file mode 100644
index 00000000..8cd87b9d
Binary files /dev/null and b/OnePassword/Settings.bundle/en.lproj/Root.strings differ
diff --git a/OnePassword/en.lproj/MainStoryboard_iPhone.storyboard b/OnePassword/en.lproj/MainStoryboard_iPhone.storyboard
index 7505961c..42c351f5 100644
--- a/OnePassword/en.lproj/MainStoryboard_iPhone.storyboard
+++ b/OnePassword/en.lproj/MainStoryboard_iPhone.storyboard
@@ -5,141 +5,182 @@
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -148,55 +189,162 @@
-
- Passwords of this type are encrypted and stored. They are not tied to a certain algorithm but if you loose the storage, you loose the passwords.
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -207,11 +355,11 @@
-
-
+
+
-
+
@@ -224,30 +372,33 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
+
-
+
@@ -255,7 +406,7 @@
-
+
@@ -265,16 +416,19 @@
-
-
-
+
+
+
+
+
+
-
-
+
+
@@ -284,20 +438,21 @@
-
+
-
-
+
+
-
-
+
+
+
@@ -306,28 +461,82 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -359,10 +568,9 @@
-
+
-
@@ -374,12 +582,50 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+