2
0

Reformat.

This commit is contained in:
Maarten Billemont 2013-04-20 14:11:19 -04:00
parent c0d57b5561
commit 95d5d8b40c
69 changed files with 1532 additions and 1522 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 737b198043a3acb3a262a38a8b14e265801ce682 Subproject commit affcd43c4e50b99f12da5b3cf342bc6fab6e0298

View File

@ -24,18 +24,17 @@ id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version) {
versionToAlgorithm = [NSMutableDictionary dictionary]; versionToAlgorithm = [NSMutableDictionary dictionary];
id<MPAlgorithm> algorithm = [versionToAlgorithm objectForKey:@(version)]; id<MPAlgorithm> algorithm = [versionToAlgorithm objectForKey:@(version)];
if (!algorithm) if (!algorithm) if ((algorithm = [NSClassFromString( PearlString( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
if ((algorithm = [NSClassFromString(PearlString(@"MPAlgorithmV%lu", (unsigned long)version)) new])) [versionToAlgorithm setObject:algorithm forKey:@(version)];
[versionToAlgorithm setObject:algorithm forKey:@(version)];
return algorithm; return algorithm;
} }
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) { id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
if (PearlCFBundleVersionCompare(bundleVersion, @"1.3") == NSOrderedAscending) if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
// Pre-1.3 // Pre-1.3
return MPAlgorithmForVersion(0); return MPAlgorithmForVersion( 0 );
return MPAlgorithmDefault; return MPAlgorithmDefault;
} }

View File

@ -17,5 +17,5 @@
#import "MPAlgorithm.h" #import "MPAlgorithm.h"
@interface MPAlgorithmV0 : NSObject <MPAlgorithm> @interface MPAlgorithmV0 : NSObject<MPAlgorithm>
@end @end

View File

@ -33,8 +33,8 @@
- (BOOL)migrateUser:(MPUserEntity *)user { - (BOOL)migrateUser:(MPUserEntity *)user {
NSError *error = nil; NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user]; migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
NSArray *migrationElements = [user.managedObjectContext executeFetchRequest:migrationRequest error:&error]; NSArray *migrationElements = [user.managedObjectContext executeFetchRequest:migrationRequest error:&error];
if (!migrationElements) { if (!migrationElements) {
@ -42,7 +42,7 @@
return NO; return NO;
} }
BOOL requiresExplicitMigration = NO; BOOL requiresExplicitMigration = NO;
for (MPElementEntity *migrationElement in migrationElements) for (MPElementEntity *migrationElement in migrationElements)
if (![migrationElement migrateExplicitly:NO]) if (![migrationElement migrateExplicitly:NO])
requiresExplicitMigration = YES; requiresExplicitMigration = YES;
@ -53,7 +53,7 @@
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit { - (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit {
if (element.version != [self version] - 1) if (element.version != [self version] - 1)
// Only migrate from previous version. // Only migrate from previous version.
return NO; return NO;
if (!explicit) { if (!explicit) {
@ -64,21 +64,21 @@
// Apply migration. // Apply migration.
element.requiresExplicitMigration = NO; element.requiresExplicitMigration = NO;
element.version = [self version]; element.version = [self version];
return YES; return YES;
} }
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName { - (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName {
uint32_t nuserNameLength = htonl(userName.length); uint32_t nuserNameLength = htonl(userName.length);
NSDate *start = [NSDate date]; NSDate *start = [NSDate date];
NSData *keyData = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding] NSData *keyData = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
usingSalt:[NSData dataByConcatenatingDatas: usingSalt:[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
[NSData dataWithBytes:&nuserNameLength [NSData dataWithBytes:&nuserNameLength
length:sizeof(nuserNameLength)], length:sizeof(nuserNameLength)],
[userName dataUsingEncoding:NSUTF8StringEncoding], [userName dataUsingEncoding:NSUTF8StringEncoding],
nil] N:MP_N r:MP_r p:MP_p]; nil] N:MP_N r:MP_r p:MP_p];
MPKey *key = [self keyFromKeyData:keyData]; MPKey *key = [self keyFromKeyData:keyData];
trc(@"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex], -[start timeIntervalSinceNow]); trc(@"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex], -[start timeIntervalSinceNow]);
@ -166,13 +166,13 @@
- (NSString *)classNameOfType:(MPElementType)type { - (NSString *)classNameOfType:(MPElementType)type {
return NSStringFromClass([self classOfType:type]); return NSStringFromClass( [self classOfType:type] );
} }
- (Class)classOfType:(MPElementType)type { - (Class)classOfType:(MPElementType)type {
if (!type) if (!type)
Throw(@"No type given."); Throw(@"No type given.");
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPElementTypeGeneratedMaximum:
@ -233,24 +233,24 @@
// Determine the seed whose bytes will be used for calculating a password // Determine the seed whose bytes will be used for calculating a password
uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length); uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length);
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)]; NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)];
trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]); trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]);
NSData *seed = [[NSData dataByConcatenatingDatas: NSData *seed = [[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
nameLengthBytes, nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding], [name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes, counterBytes,
nil] nil]
hmacWith:PearlHashSHA256 key:key.keyData]; hmacWith:PearlHashSHA256 key:key.keyData];
trc(@"seed is: %@", [seed encodeBase64]); trc(@"seed is: %@", [seed encodeBase64]);
const char *seedBytes = seed.bytes; const char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte. // Determine the cipher from the first seed byte.
assert([seed length]); assert([seed length]);
NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]] NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]]
valueForKey:[self nameOfType:type]]; valueForKey:[self nameOfType:type]];
NSString *cipher = [typeCiphers objectAtIndex:htons(seedBytes[0]) % [typeCiphers count]]; NSString *cipher = [typeCiphers objectAtIndex:htons(seedBytes[0]) % [typeCiphers count]];
trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher); trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher);
// Encode the content, character by character, using subsequent seed bytes and the cipher. // Encode the content, character by character, using subsequent seed bytes and the cipher.
@ -258,10 +258,10 @@
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
for (NSUInteger c = 0; c < [cipher length]; ++c) { for (NSUInteger c = 0; c < [cipher length]; ++c) {
uint16_t keyByte = htons(seedBytes[c + 1]); uint16_t keyByte = htons(seedBytes[c + 1]);
NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)]; NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )];
NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass]; NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length],
1)]; 1 )];
trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character); trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character);
[content appendString:character]; [content appendString:character];

View File

@ -17,6 +17,5 @@
#import "MPAlgorithmV0.h" #import "MPAlgorithmV0.h"
@interface MPAlgorithmV1 : MPAlgorithmV0 @interface MPAlgorithmV1 : MPAlgorithmV0
@end @end

View File

@ -18,7 +18,6 @@
#import "MPAlgorithmV1.h" #import "MPAlgorithmV1.h"
#import "MPEntities.h" #import "MPEntities.h"
@implementation MPAlgorithmV1 @implementation MPAlgorithmV1
- (NSUInteger)version { - (NSUInteger)version {
@ -29,7 +28,7 @@
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit { - (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit {
if (element.version != [self version] - 1) if (element.version != [self version] - 1)
// Only migrate from previous version. // Only migrate from previous version.
return NO; return NO;
if (!explicit) { if (!explicit) {
@ -42,7 +41,7 @@
// Apply migration. // Apply migration.
element.requiresExplicitMigration = NO; element.requiresExplicitMigration = NO;
element.version = [self version]; element.version = [self version];
return YES; return YES;
} }
@ -55,24 +54,24 @@
// Determine the seed whose bytes will be used for calculating a password // Determine the seed whose bytes will be used for calculating a password
uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length); uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length);
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)]; NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)];
trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]); trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]);
NSData *seed = [[NSData dataByConcatenatingDatas: NSData *seed = [[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
nameLengthBytes, nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding], [name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes, counterBytes,
nil] nil]
hmacWith:PearlHashSHA256 key:key.keyData]; hmacWith:PearlHashSHA256 key:key.keyData];
trc(@"seed is: %@", [seed encodeBase64]); trc(@"seed is: %@", [seed encodeBase64]);
const unsigned char *seedBytes = seed.bytes; const unsigned char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte. // Determine the cipher from the first seed byte.
assert([seed length]); assert([seed length]);
NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]] NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]]
valueForKey:[self nameOfType:type]]; valueForKey:[self nameOfType:type]];
NSString *cipher = [typeCiphers objectAtIndex:seedBytes[0] % [typeCiphers count]]; NSString *cipher = [typeCiphers objectAtIndex:seedBytes[0] % [typeCiphers count]];
trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher); trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher);
// Encode the content, character by character, using subsequent seed bytes and the cipher. // Encode the content, character by character, using subsequent seed bytes and the cipher.
@ -80,10 +79,10 @@
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
for (NSUInteger c = 0; c < [cipher length]; ++c) { for (NSUInteger c = 0; c < [cipher length]; ++c) {
uint16_t keyByte = seedBytes[c + 1]; uint16_t keyByte = seedBytes[c + 1];
NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)]; NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )];
NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass]; NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length],
1)]; 1 )];
trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character); trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character);
[content appendString:character]; [content appendString:character];
@ -92,5 +91,4 @@
return content; return content;
} }
@end @end

View File

@ -8,7 +8,7 @@
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"
@interface MPAppDelegate_Shared (Key) @interface MPAppDelegate_Shared(Key)
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password; - (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password;
- (void)signOutAnimated:(BOOL)animated; - (void)signOutAnimated:(BOOL)animated;

View File

@ -9,21 +9,21 @@
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@implementation MPAppDelegate_Shared (Key) @implementation MPAppDelegate_Shared(Key)
static NSDictionary *keyQuery(MPUserEntity *user) { static NSDictionary *keyQuery(MPUserEntity *user) {
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{ attributes:@{
(__bridge id)kSecAttrService: @"Saved Master Password", (__bridge id)kSecAttrService : @"Saved Master Password",
(__bridge id)kSecAttrAccount: IfNotNilElse(user.name, @"") (__bridge id)kSecAttrAccount : IfNotNilElse(user.name, @"")
} }
matches:nil]; matches:nil];
} }
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user { - (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery(user)]; NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
if (keyData) if (keyData)
inf(@"Found key in keychain for: %@", user.userID); inf(@"Found key in keychain for: %@", user.userID);
@ -38,16 +38,16 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
- (void)storeSavedKeyFor:(MPUserEntity *)user { - (void)storeSavedKeyFor:(MPUserEntity *)user {
if (user.saveKey) { if (user.saveKey) {
NSData *existingKeyData = [PearlKeyChain dataOfItemForQuery:keyQuery(user)]; NSData *existingKeyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
if (![existingKeyData isEqualToData:self.key.keyData]) { if (![existingKeyData isEqualToData:self.key.keyData]) {
inf(@"Saving key in keychain for: %@", user.userID); inf(@"Saving key in keychain for: %@", user.userID);
[PearlKeyChain addOrUpdateItemForQuery:keyQuery(user) [PearlKeyChain addOrUpdateItemForQuery:keyQuery( user )
withAttributes:@{ withAttributes:@{
(__bridge id)kSecValueData : self.key.keyData, (__bridge id)kSecValueData : self.key.keyData,
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
#endif #endif
}]; }];
} }
@ -56,7 +56,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
- (void)forgetSavedKeyFor:(MPUserEntity *)user { - (void)forgetSavedKeyFor:(MPUserEntity *)user {
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery(user)]; OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
if (result == noErr || result == errSecItemNotFound) { if (result == noErr || result == errSecItemNotFound) {
user.saveKey = NO; user.saveKey = NO;
@ -74,7 +74,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
self.key = nil; self.key = nil;
self.activeUser = nil; self.activeUser = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{@"animated": @(animated)}]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }];
} }
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password { - (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
@ -84,42 +84,38 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// Method 1: When the user has no keyID set, set a new key from the given master password. // Method 1: When the user has no keyID set, set a new key from the given master password.
if (!user.keyID) { if (!user.keyID) {
if ([password length]) if ([password length]) if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) { user.keyID = tryKey.keyID;
user.keyID = tryKey.keyID;
// Migrate existing elements. // Migrate existing elements.
[self migrateElementsForUser:user inContext:user.managedObjectContext toKey:tryKey]; [self migrateElementsForUser:user inContext:user.managedObjectContext toKey:tryKey];
} }
} }
// Method 2: Depending on the user's saveKey, load or remove the key from the keychain. // Method 2: Depending on the user's saveKey, load or remove the key from the keychain.
if (!user.saveKey) if (!user.saveKey)
// Key should not be stored in keychain. Delete it. // Key should not be stored in keychain. Delete it.
[self forgetSavedKeyFor:user]; [self forgetSavedKeyFor:user];
else else if (!tryKey) {
if (!tryKey) { // Key should be saved in keychain. Load it.
// Key should be saved in keychain. Load it. if ((tryKey = [self loadSavedKeyFor:user])) if (![user.keyID isEqual:tryKey.keyID]) {
if ((tryKey = [self loadSavedKeyFor:user])) // Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
if (![user.keyID isEqual:tryKey.keyID]) { inf(@"Saved password doesn't match keyID for: %@", user.userID);
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
inf(@"Saved password doesn't match keyID for: %@", user.userID);
tryKey = nil; tryKey = nil;
[self forgetSavedKeyFor:user]; [self forgetSavedKeyFor:user];
}
} }
}
// Method 3: Check the given master password string. // Method 3: Check the given master password string.
if (!tryKey) { if (!tryKey) {
if ([password length]) if ([password length]) if ((tryKey = [MPAlgorithmDefault keyForPassword:password
if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) ofUserNamed:user.name])) if (![user.keyID isEqual:tryKey.keyID]) {
if (![user.keyID isEqual:tryKey.keyID]) { inf(@"Key derived from password doesn't match keyID for: %@", user.userID);
inf(@"Key derived from password doesn't match keyID for: %@", user.userID);
tryKey = nil; tryKey = nil;
} }
} }
// No more methods left, fail if key still not known. // No more methods left, fail if key still not known.
@ -166,7 +162,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
- (void)migrateElementsForUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey { - (void)migrateElementsForUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
if (![user.elements count]) if (![user.elements count])
// Nothing to migrate. // Nothing to migrate.
return; return;
MPKey *recoverKey = newKey; MPKey *recoverKey = newKey;

View File

@ -15,7 +15,7 @@
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate> @interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
#endif #endif
@property (strong, nonatomic) MPKey *key; @property(strong, nonatomic) MPKey *key;
+ (instancetype)get; + (instancetype)get;

View File

@ -49,7 +49,7 @@
NSError *error; NSError *error;
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error]; MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error];
if (!activeUser) if (!activeUser)
err(@"Failed to retrieve active user: %@", error); err(@"Failed to retrieve active user: %@", error);
return activeUser; return activeUser;
} }

View File

@ -18,7 +18,7 @@ typedef enum {
MPImportResultInternalError, MPImportResultInternalError,
} MPImportResult; } MPImportResult;
@interface MPAppDelegate_Shared (Store)<UbiquityStoreManagerDelegate> @interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>
+ (NSManagedObjectContext *)managedObjectContextForThreadIfReady; + (NSManagedObjectContext *)managedObjectContextForThreadIfReady;
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock; + (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock;

View File

@ -15,8 +15,8 @@
#define STORE_OPTIONS #define STORE_OPTIONS
#endif #endif
@implementation MPAppDelegate_Shared (Store) @implementation MPAppDelegate_Shared(Store)
PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert); PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert);
PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert); PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert);
PearlAssociatedObjectProperty(PearlOverlay*, StoreLoading, storeLoading); PearlAssociatedObjectProperty(PearlOverlay*, StoreLoading, storeLoading);
PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext); PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext);
@ -31,7 +31,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
if ([[NSThread currentThread] isMainThread]) if ([[NSThread currentThread] isMainThread])
return mainManagedObjectContext; return mainManagedObjectContext;
NSManagedObjectContext *threadManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; NSManagedObjectContext
*threadManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
threadManagedObjectContext.parentContext = mainManagedObjectContext; threadManagedObjectContext.parentContext = mainManagedObjectContext;
return threadManagedObjectContext; return threadManagedObjectContext;
@ -46,7 +47,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = mainManagedObjectContext; moc.parentContext = mainManagedObjectContext;
[moc performBlock:^{ [moc performBlock:^{
mocBlock(moc); mocBlock( moc );
}]; }];
return YES; return YES;
@ -61,7 +62,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = mainManagedObjectContext; moc.parentContext = mainManagedObjectContext;
[moc performBlockAndWait:^{ [moc performBlockAndWait:^{
mocBlock(moc); mocBlock( moc );
}]; }];
return YES; return YES;
@ -87,7 +88,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared" containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
additionalStoreOptions:@{STORE_OPTIONS} additionalStoreOptions:@{ STORE_OPTIONS }
delegate:self]; delegate:self];
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
@ -203,7 +204,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
// Migrate cloud store. // Migrate cloud store.
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"]; NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
if (!uuid) if (!uuid)
// No old cloud store to migrate. // No old cloud store to migrate.
return; return;
if (![manager cloudSafeForSeeding]) { if (![manager cloudSafeForSeeding]) {
@ -321,10 +322,10 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
self.privateManagedObjectContext = nil; self.privateManagedObjectContext = nil;
self.mainManagedObjectContext = nil; self.mainManagedObjectContext = nil;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if (![self.storeLoading isVisible]) if (![self.storeLoading isVisible])
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Opening Your Data"]; self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Opening Your Data"];
}); } );
[self migrateStoreForManager:manager isCloud:isCloudStore]; [self migrateStoreForManager:manager isCloud:isCloudStore];
} }
@ -338,7 +339,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
} ); } );
// Create our contexts. // Create our contexts.
NSManagedObjectContext *privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; NSManagedObjectContext
*privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateManagedObjectContext performBlockAndWait:^{ [privateManagedObjectContext performBlockAndWait:^{
privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
privateManagedObjectContext.persistentStoreCoordinator = coordinator; privateManagedObjectContext.persistentStoreCoordinator = coordinator;
@ -433,7 +435,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSAssert(![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread."); NSAssert(![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread.");
NSManagedObjectContext *moc; NSManagedObjectContext *moc;
while (!(moc = [MPAppDelegate_Shared managedObjectContextForThreadIfReady])) while (!(moc = [MPAppDelegate_Shared managedObjectContextForThreadIfReady]))
usleep((useconds_t)(USEC_PER_SEC * 0.2)); usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
// Parse import data. // Parse import data.
inf(@"Importing sites."); inf(@"Importing sites.");
@ -442,11 +444,11 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSString *importBundleVersion = nil, *importUserName = nil; NSString *importBundleVersion = nil, *importUserName = nil;
NSData *importKeyID = nil; NSData *importKeyID = nil;
BOOL headerStarted = NO, headerEnded = NO, clearText = NO; BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableSet *elementsToDelete = [NSMutableSet set]; NSMutableSet *elementsToDelete = [NSMutableSet set];
NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]]; NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
for (NSString *importedSiteLine in importedSiteLines) { for (NSString *importedSiteLine in importedSiteLines) {
if ([importedSiteLine hasPrefix:@"#"]) { if ([importedSiteLine hasPrefix:@"#"]) {
// Comment or header // Comment or header
if (!headerStarted) { if (!headerStarted) {
@ -468,13 +470,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
return MPImportResultMalformedInput; return MPImportResultMalformedInput;
} }
NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0 NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
range:NSMakeRange(0, [importedSiteLine length])] lastObject]; range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]]; NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]]; NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
if ([headerName isEqualToString:@"User Name"]) { if ([headerName isEqualToString:@"User Name"]) {
importUserName = headerValue; importUserName = headerValue;
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName]; userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
NSArray *users = [moc executeFetchRequest:userFetchRequest error:&error]; NSArray *users = [moc executeFetchRequest:userFetchRequest error:&error];
if (!users) { if (!users) {
@ -490,10 +492,10 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
dbg(@"Found user: %@", [user debugDescription]); dbg(@"Found user: %@", [user debugDescription]);
} }
if ([headerName isEqualToString:@"Key ID"]) if ([headerName isEqualToString:@"Key ID"])
importKeyID = [headerValue decodeHex]; importKeyID = [headerValue decodeHex];
if ([headerName isEqualToString:@"Version"]) { if ([headerName isEqualToString:@"Version"]) {
importBundleVersion = headerValue; importBundleVersion = headerValue;
importAlgorithm = MPAlgorithmDefaultForBundleVersion(importBundleVersion); importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
} }
if ([headerName isEqualToString:@"Passwords"]) { if ([headerName isEqualToString:@"Passwords"]) {
if ([headerValue isEqualToString:@"VISIBLE"]) if ([headerValue isEqualToString:@"VISIBLE"])
@ -515,14 +517,14 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
err(@"Invalid site format in line: %@", importedSiteLine); err(@"Invalid site format in line: %@", importedSiteLine);
return MPImportResultMalformedInput; return MPImportResultMalformedInput;
} }
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0 NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
range:NSMakeRange(0, [importedSiteLine length])] lastObject]; range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
NSString *lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]]; NSString *lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]]; NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
NSString *type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]]; NSString *type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
NSString *version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]]; NSString *version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
NSString *name = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]]; NSString *name = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]]; NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
// Find existing site. // Find existing site.
if (user) { if (user) {
@ -531,18 +533,18 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
if (!existingSites) { if (!existingSites) {
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error); err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
return MPImportResultInternalError; return MPImportResultInternalError;
} else }
if (existingSites.count) else if (existingSites.count)
dbg(@"Existing sites: %@", existingSites); dbg(@"Existing sites: %@", existingSites);
[elementsToDelete addObjectsFromArray:existingSites]; [elementsToDelete addObjectsFromArray:existingSites];
[importedSiteElements addObject:@[lastUsed, uses, type, version, name, exportContent]]; [importedSiteElements addObject:@[ lastUsed, uses, type, version, name, exportContent ]];
} }
} }
// Ask for confirmation to import these sites and the master password of the user. // Ask for confirmation to import these sites and the master password of the user.
inf(@"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteElements count], (unsigned long)[elementsToDelete count], [MPUserEntity idFor:importUserName]); inf(@"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteElements count], (unsigned long)[elementsToDelete count], [MPUserEntity idFor:importUserName]);
NSString *userMasterPassword = userPassword(user.name, [importedSiteElements count], [elementsToDelete count]); NSString *userMasterPassword = userPassword( user.name, [importedSiteElements count], [elementsToDelete count] );
if (!userMasterPassword) { if (!userMasterPassword) {
inf(@"Import cancelled."); inf(@"Import cancelled.");
return MPImportResultCancelled; return MPImportResultCancelled;
@ -563,9 +565,9 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
// Make sure there is a user. // Make sure there is a user.
if (!user) { if (!user) {
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc]; inManagedObjectContext:moc];
user.name = importUserName; user.name = importUserName;
user.keyID = importKeyID; user.keyID = importKeyID;
dbg(@"Created User: %@", [user debugDescription]); dbg(@"Created User: %@", [user debugDescription]);
} }
@ -573,27 +575,28 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
// Import new sites. // Import new sites.
for (NSArray *siteElements in importedSiteElements) { for (NSArray *siteElements in importedSiteElements) {
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]]; NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]];
NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue]; NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue];
MPElementType type = (MPElementType)[[siteElements objectAtIndex:2] integerValue]; MPElementType type = (MPElementType)[[siteElements objectAtIndex:2] integerValue];
NSUInteger version = (unsigned)[[siteElements objectAtIndex:3] integerValue]; NSUInteger version = (unsigned)[[siteElements objectAtIndex:3] integerValue];
NSString *name = [siteElements objectAtIndex:4]; NSString *name = [siteElements objectAtIndex:4];
NSString *exportContent = [siteElements objectAtIndex:5]; NSString *exportContent = [siteElements objectAtIndex:5];
// Create new site. // Create new site.
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion(version) classNameOfType:type] MPElementEntity
inManagedObjectContext:moc]; *element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion( version ) classNameOfType:type]
element.name = name; inManagedObjectContext:moc];
element.user = user; element.name = name;
element.type = type; element.user = user;
element.uses = uses; element.type = type;
element.uses = uses;
element.lastUsed = lastUsed; element.lastUsed = lastUsed;
element.version = version; element.version = version;
if ([exportContent length]) { if ([exportContent length]) {
if (clearText) if (clearText)
[element importClearTextContent:exportContent usingKey:userKey]; [element importClearTextContent:exportContent usingKey:userKey];
else { else {
if (!importKey) if (!importKey)
importKey = [importAlgorithm keyForPassword:importPassword(user.name) ofUserNamed:user.name]; importKey = [importAlgorithm keyForPassword:importPassword( user.name ) ofUserNamed:user.name];
if (![importKey.keyID isEqualToData:importKeyID]) if (![importKey.keyID isEqualToData:importKeyID])
return MPImportResultInvalidPassword; return MPImportResultInvalidPassword;
@ -645,25 +648,24 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
// Sites. // Sites.
for (MPElementEntity *element in activeUser.elements) { for (MPElementEntity *element in activeUser.elements) {
NSDate *lastUsed = element.lastUsed; NSDate *lastUsed = element.lastUsed;
NSUInteger uses = element.uses; NSUInteger uses = element.uses;
MPElementType type = element.type; MPElementType type = element.type;
NSUInteger version = element.version; NSUInteger version = element.version;
NSString *name = element.name; NSString *name = element.name;
NSString *content = nil; NSString *content = nil;
// Determine the content to export. // Determine the content to export.
if (!(type & MPElementFeatureDevicePrivate)) { if (!(type & MPElementFeatureDevicePrivate)) {
if (showPasswords) if (showPasswords)
content = element.content; content = element.content;
else else if (type & MPElementFeatureExportContent)
if (type & MPElementFeatureExportContent) content = element.exportContent;
content = element.exportContent;
} }
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n", [export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses, [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
[PearlString(@"%u:%lu", type, (unsigned long)version) UTF8String], [name UTF8String], content [PearlString( @"%u:%lu", type, (unsigned long)version ) UTF8String], [name UTF8String], content
? content: @""]; ? content: @""];
} }
MPCheckpoint( MPCheckpointSitesExported, @{ MPCheckpoint( MPCheckpointSitesExported, @{

View File

@ -10,9 +10,9 @@
@interface MPConfig : PearlConfig @interface MPConfig : PearlConfig
@property (nonatomic, retain) NSNumber *sendInfo; @property(nonatomic, retain) NSNumber *sendInfo;
@property (nonatomic, retain) NSNumber *rememberLogin; @property(nonatomic, retain) NSNumber *rememberLogin;
@property (nonatomic, retain) NSNumber *iCloudDecided; @property(nonatomic, retain) NSNumber *iCloudDecided;
@end @end

View File

@ -9,6 +9,7 @@
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
@implementation MPConfig @implementation MPConfig
@dynamic sendInfo, rememberLogin, iCloudDecided; @dynamic sendInfo, rememberLogin, iCloudDecided;
- (id)init { - (id)init {
@ -16,11 +17,13 @@
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
[self.defaults registerDefaults:@{NSStringFromSelector(@selector(askForReviews)): @YES, [self.defaults registerDefaults:@{
NSStringFromSelector( @selector(askForReviews) ) : @YES,
NSStringFromSelector(@selector(sendInfo)): @NO, NSStringFromSelector( @selector(sendInfo) ) : @NO,
NSStringFromSelector(@selector(rememberLogin)): @NO, NSStringFromSelector( @selector(rememberLogin) ) : @NO,
NSStringFromSelector(@selector(iCloudDecided)): @NO}]; NSStringFromSelector( @selector(iCloudDecided) ) : @NO
}];
self.delegate = [MPAppDelegate get]; self.delegate = [MPAppDelegate get];

View File

@ -13,14 +13,14 @@
@interface MPElementEntity : NSManagedObject @interface MPElementEntity : NSManagedObject
@property (nonatomic, retain) id content; @property(nonatomic, retain) id content;
@property (nonatomic, retain) NSDate * lastUsed; @property(nonatomic, retain) NSDate *lastUsed;
@property (nonatomic, retain) NSString * loginName; @property(nonatomic, retain) NSString *loginName;
@property (nonatomic, retain) NSString * name; @property(nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; @property(nonatomic, retain) NSNumber *requiresExplicitMigration_;
@property (nonatomic, retain) NSNumber * type_; @property(nonatomic, retain) NSNumber *type_;
@property (nonatomic, retain) NSNumber * uses_; @property(nonatomic, retain) NSNumber *uses_;
@property (nonatomic, retain) NSNumber * version_; @property(nonatomic, retain) NSNumber *version_;
@property (nonatomic, retain) MPUserEntity *user; @property(nonatomic, retain) MPUserEntity *user;
@end @end

View File

@ -7,8 +7,6 @@
// //
#import "MPElementEntity.h" #import "MPElementEntity.h"
#import "MPUserEntity.h"
@implementation MPElementEntity @implementation MPElementEntity

View File

@ -10,9 +10,8 @@
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
#import "MPElementEntity.h" #import "MPElementEntity.h"
@interface MPElementGeneratedEntity : MPElementEntity @interface MPElementGeneratedEntity : MPElementEntity
@property (nonatomic, retain) NSNumber * counter_; @property(nonatomic, retain) NSNumber *counter_;
@end @end

View File

@ -8,7 +8,6 @@
#import "MPElementGeneratedEntity.h" #import "MPElementGeneratedEntity.h"
@implementation MPElementGeneratedEntity @implementation MPElementGeneratedEntity
@dynamic counter_; @dynamic counter_;

View File

@ -10,9 +10,8 @@
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
#import "MPElementEntity.h" #import "MPElementEntity.h"
@interface MPElementStoredEntity : MPElementEntity @interface MPElementStoredEntity : MPElementEntity
@property (nonatomic, retain) id contentObject; @property(nonatomic, retain) id contentObject;
@end @end

View File

@ -8,7 +8,6 @@
#import "MPElementStoredEntity.h" #import "MPElementStoredEntity.h"
@implementation MPElementStoredEntity @implementation MPElementStoredEntity
@dynamic contentObject; @dynamic contentObject;

View File

@ -21,17 +21,17 @@
@end @end
@interface MPElementEntity (MP) @interface MPElementEntity(MP)
@property (assign) MPElementType type; @property(assign) MPElementType type;
@property (readonly) NSString *typeName; @property(readonly) NSString *typeName;
@property (readonly) NSString *typeShortName; @property(readonly) NSString *typeShortName;
@property (readonly) NSString *typeClassName; @property(readonly) NSString *typeClassName;
@property (readonly) Class typeClass; @property(readonly) Class typeClass;
@property (assign) NSUInteger uses; @property(assign) NSUInteger uses;
@property (assign) NSUInteger version; @property(assign) NSUInteger version;
@property (assign) BOOL requiresExplicitMigration; @property(assign) BOOL requiresExplicitMigration;
@property (readonly) id<MPAlgorithm> algorithm; @property(readonly) id<MPAlgorithm> algorithm;
- (id)contentUsingKey:(MPKey *)key; - (id)contentUsingKey:(MPKey *)key;
- (void)setContent:(id)content usingKey:(MPKey *)key; - (void)setContent:(id)content usingKey:(MPKey *)key;
@ -44,18 +44,18 @@
@end @end
@interface MPElementGeneratedEntity (MP) @interface MPElementGeneratedEntity(MP)
@property (assign) NSUInteger counter; @property(assign) NSUInteger counter;
@end @end
@interface MPUserEntity (MP) @interface MPUserEntity(MP)
@property (assign) NSUInteger avatar; @property(assign) NSUInteger avatar;
@property (assign) BOOL saveKey; @property(assign) BOOL saveKey;
@property (assign) MPElementType defaultType; @property(assign) MPElementType defaultType;
@property (readonly) NSString *userID; @property(readonly) NSString *userID;
+ (NSString *)idFor:(NSString *)userName; + (NSString *)idFor:(NSString *)userName;

View File

@ -9,7 +9,7 @@
#import "MPEntities.h" #import "MPEntities.h"
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
@implementation NSManagedObjectContext (MP) @implementation NSManagedObjectContext(MP)
- (BOOL)saveToStore { - (BOOL)saveToStore {
@ -24,7 +24,7 @@
@end @end
@implementation MPElementEntity (MP) @implementation MPElementEntity(MP)
- (MPElementType)type { - (MPElementType)type {
@ -101,7 +101,7 @@
- (id<MPAlgorithm>)algorithm { - (id<MPAlgorithm>)algorithm {
return MPAlgorithmForVersion(self.version); return MPAlgorithmForVersion( self.version );
} }
- (NSUInteger)use { - (NSUInteger)use {
@ -146,29 +146,27 @@
} }
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key { - (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key {
} }
- (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key { - (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key {
} }
- (NSString *)description { - (NSString *)description {
return PearlString(@"%@:%@", [self class], [self name]); return PearlString( @"%@:%@", [self class], [self name] );
} }
- (NSString *)debugDescription { - (NSString *)debugDescription {
return PearlString(@"{%@: name=%@, user=%@, type=%d, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}", return PearlString( @"{%@: name=%@, user=%@, type=%d, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
NSStringFromClass([self class]), self.name, self.user.name, self.type, (long)self.uses, self.lastUsed, (long)self.version, NSStringFromClass( [self class] ), self.name, self.user.name, self.type, (long)self.uses, self.lastUsed, (long)self.version,
self.loginName, self.requiresExplicitMigration); self.loginName, self.requiresExplicitMigration );
} }
- (BOOL)migrateExplicitly:(BOOL)explicit { - (BOOL)migrateExplicitly:(BOOL)explicit {
while (self.version < MPAlgorithmDefaultVersion) while (self.version < MPAlgorithmDefaultVersion)
if ([MPAlgorithmForVersion(self.version + 1) migrateElement:self explicit:explicit]) if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit])
inf(@"%@ migration to version: %ld succeeded for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self); inf(@"%@ migration to version: %ld succeeded for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self);
else { else {
wrn(@"%@ migration to version: %ld failed for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self); wrn(@"%@ migration to version: %ld failed for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self);
@ -180,7 +178,7 @@
@end @end
@implementation MPElementGeneratedEntity (MP) @implementation MPElementGeneratedEntity(MP)
- (NSUInteger)counter { - (NSUInteger)counter {
@ -204,16 +202,17 @@
return [self.algorithm generateContentForElement:self usingKey:key]; return [self.algorithm generateContentForElement:self usingKey:key];
} }
@end @end
@implementation MPElementStoredEntity (MP) @implementation MPElementStoredEntity(MP)
+ (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name { + (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name {
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{(__bridge id)kSecAttrService: @"DevicePrivate", attributes:@{
(__bridge id)kSecAttrAccount: name} (__bridge id)kSecAttrService : @"DevicePrivate",
(__bridge id)kSecAttrAccount : name
}
matches:nil]; matches:nil];
} }
@ -251,19 +250,20 @@
assert([key.keyID isEqualToData:self.user.keyID]); assert([key.keyID isEqualToData:self.user.keyID]);
NSData *encryptedContent = [[[content description] dataUsingEncoding:NSUTF8StringEncoding] NSData *encryptedContent = [[[content description] dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES]; encryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
if (self.type & MPElementFeatureDevicePrivate) { if (self.type & MPElementFeatureDevicePrivate) {
[PearlKeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name] [PearlKeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name]
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
encryptedContent, (__bridge id)kSecValueData, encryptedContent, (__bridge id)kSecValueData,
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
(__bridge id)kSecAttrAccessible, (__bridge id)kSecAttrAccessible,
#endif #endif
nil]]; nil]];
self.contentObject = nil; self.contentObject = nil;
} else }
else
self.contentObject = encryptedContent; self.contentObject = encryptedContent;
} }
@ -280,7 +280,7 @@
else { else {
NSString *clearContent = [[NSString alloc] initWithData:[self decryptContent:[protectedContent decodeBase64] NSString *clearContent = [[NSString alloc] initWithData:[self decryptContent:[protectedContent decodeBase64]
usingKey:contentProtectionKey] usingKey:contentProtectionKey]
encoding:NSUTF8StringEncoding]; encoding:NSUTF8StringEncoding];
[self importClearTextContent:clearContent usingKey:key]; [self importClearTextContent:clearContent usingKey:key];
} }
@ -293,7 +293,7 @@
@end @end
@implementation MPUserEntity (MP) @implementation MPUserEntity(MP)
- (NSUInteger)avatar { - (NSUInteger)avatar {

View File

@ -19,12 +19,11 @@
@protocol MPAlgorithm; @protocol MPAlgorithm;
@interface MPKey : NSObject @interface MPKey : NSObject
@property (nonatomic, readonly, strong) id<MPAlgorithm> algorithm; @property(nonatomic, readonly, strong) id<MPAlgorithm> algorithm;
@property (nonatomic, readonly, strong) NSData *keyData; @property(nonatomic, readonly, strong) NSData *keyData;
@property (nonatomic, readonly, strong) NSData *keyID; @property(nonatomic, readonly, strong) NSData *keyID;
- (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm; - (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm;
- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength; - (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength;

View File

@ -17,22 +17,22 @@
#import "MPAlgorithm.h" #import "MPAlgorithm.h"
@interface MPKey()
@interface MPKey () @property(nonatomic, readwrite, strong) id<MPAlgorithm> algorithm;
@property(nonatomic, readwrite, strong) NSData *keyData;
@property (nonatomic, readwrite, strong) id<MPAlgorithm> algorithm; @property(nonatomic, readwrite, strong) NSData *keyID;
@property (nonatomic, readwrite, strong) NSData *keyData;
@property (nonatomic, readwrite, strong) NSData *keyID;
@end @end
@implementation MPKey @implementation MPKey
@synthesize algorithm = _algorithm, keyData = _keyData, keyID = _keyID; @synthesize algorithm = _algorithm, keyData = _keyData, keyID = _keyID;
- (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm { - (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
self.keyData = keyData; self.keyData = keyData;
self.algorithm = algorithm; self.algorithm = algorithm;
@ -43,7 +43,7 @@
- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength { - (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength {
NSData *subKeyData = [self.keyData subdataWithRange:NSMakeRange(0, MIN(subKeyLength, self.keyData.length))]; NSData *subKeyData = [self.keyData subdataWithRange:NSMakeRange( 0, MIN(subKeyLength, self.keyData.length) )];
return [self.algorithm keyFromKeyData:subKeyData]; return [self.algorithm keyFromKeyData:subKeyData];
} }
@ -61,5 +61,4 @@
return [self isEqualToKey:object]; return [self isEqualToKey:object];
} }
@end @end

View File

@ -16,27 +16,27 @@ typedef enum {
typedef enum { typedef enum {
/** Generate the password. */ /** Generate the password. */
MPElementTypeClassGenerated = 1 << 4, MPElementTypeClassGenerated = 1 << 4,
/** Store the password. */ /** Store the password. */
MPElementTypeClassStored = 1 << 5, MPElementTypeClassStored = 1 << 5,
} MPElementTypeClass; } MPElementTypeClass;
typedef enum { typedef enum {
/** Export the key-protected content data. */ /** Export the key-protected content data. */
MPElementFeatureExportContent = 1 << 10, MPElementFeatureExportContent = 1 << 10,
/** Never export content. */ /** Never export content. */
MPElementFeatureDevicePrivate = 1 << 11, MPElementFeatureDevicePrivate = 1 << 11,
} MPElementFeature; } MPElementFeature;
typedef enum { typedef enum {
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate, MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
} MPElementType; } MPElementType;

View File

@ -13,16 +13,16 @@
@interface MPUserEntity : NSManagedObject @interface MPUserEntity : NSManagedObject
@property (nonatomic, retain) NSNumber * avatar_; @property(nonatomic, retain) NSNumber *avatar_;
@property (nonatomic, retain) NSNumber * defaultType_; @property(nonatomic, retain) NSNumber *defaultType_;
@property (nonatomic, retain) NSData * keyID; @property(nonatomic, retain) NSData *keyID;
@property (nonatomic, retain) NSDate * lastUsed; @property(nonatomic, retain) NSDate *lastUsed;
@property (nonatomic, retain) NSString * name; @property(nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSNumber * saveKey_; @property(nonatomic, retain) NSNumber *saveKey_;
@property (nonatomic, retain) NSSet *elements; @property(nonatomic, retain) NSSet *elements;
@end @end
@interface MPUserEntity (CoreDataGeneratedAccessors) @interface MPUserEntity(CoreDataGeneratedAccessors)
- (void)addElementsObject:(MPElementEntity *)value; - (void)addElementsObject:(MPElementEntity *)value;
- (void)removeElementsObject:(MPElementEntity *)value; - (void)removeElementsObject:(MPElementEntity *)value;

View File

@ -7,8 +7,6 @@
// //
#import "MPUserEntity.h" #import "MPUserEntity.h"
#import "MPElementEntity.h"
@implementation MPUserEntity @implementation MPUserEntity

View File

@ -12,16 +12,16 @@
@interface MPAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate> @interface MPAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate>
@property (nonatomic, strong) NSStatusItem *statusItem; @property(nonatomic, strong) NSStatusItem *statusItem;
@property (nonatomic, strong) MPPasswordWindowController *passwordWindow; @property(nonatomic, strong) MPPasswordWindowController *passwordWindow;
@property (nonatomic, weak) IBOutlet NSMenuItem *lockItem; @property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *showItem; @property(nonatomic, weak) IBOutlet NSMenuItem *showItem;
@property (nonatomic, strong) IBOutlet NSMenu *statusMenu; @property(nonatomic, strong) IBOutlet NSMenu *statusMenu;
@property (nonatomic, weak) IBOutlet NSMenuItem *useICloudItem; @property(nonatomic, weak) IBOutlet NSMenuItem *useICloudItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *rememberPasswordItem; @property(nonatomic, weak) IBOutlet NSMenuItem *rememberPasswordItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *savePasswordItem; @property(nonatomic, weak) IBOutlet NSMenuItem *savePasswordItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *createUserItem; @property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *usersItem; @property(nonatomic, weak) IBOutlet NSMenuItem *usersItem;
- (IBAction)activate:(id)sender; - (IBAction)activate:(id)sender;
- (IBAction)togglePreference:(NSMenuItem *)sender; - (IBAction)togglePreference:(NSMenuItem *)sender;

View File

@ -11,8 +11,8 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import <Carbon/Carbon.h> #import <Carbon/Carbon.h>
@implementation MPAppDelegate @implementation MPAppDelegate
@synthesize statusItem; @synthesize statusItem;
@synthesize lockItem; @synthesize lockItem;
@synthesize showItem; @synthesize showItem;
@ -26,28 +26,28 @@
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants" #pragma clang diagnostic ignored "-Wfour-char-constants"
static EventHotKeyID MPShowHotKey = {.signature = 'show', .id = 1}; static EventHotKeyID MPShowHotKey = { .signature = 'show', .id = 1 };
static EventHotKeyID MPLockHotKey = {.signature = 'lock', .id = 1}; static EventHotKeyID MPLockHotKey = { .signature = 'lock', .id = 1 };
#pragma clang diagnostic pop #pragma clang diagnostic pop
+ (void)initialize { + (void)initialize {
static dispatch_once_t initialize; static dispatch_once_t initialize;
dispatch_once(&initialize, ^{ dispatch_once( &initialize, ^{
[MPMacConfig get]; [MPMacConfig get];
#ifdef DEBUG #ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelDebug;//Trace; [PearlLogger get].printLevel = PearlLogLevelDebug;//Trace;
#endif #endif
}); } );
} }
static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) { static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) {
// Extract the hotkey ID. // Extract the hotkey ID.
EventHotKeyID hotKeyID; EventHotKeyID hotKeyID;
GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, GetEventParameter( theEvent, kEventParamDirectObject, typeEventHotKeyID,
NULL, sizeof(hotKeyID), NULL, &hotKeyID); NULL, sizeof(hotKeyID), NULL, &hotKeyID );
// Check which hotkey this was. // Check which hotkey this was.
if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) { if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) {
@ -83,18 +83,18 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.createUserItem.enabled = YES; self.createUserItem.enabled = YES;
self.createUserItem.toolTip = nil; self.createUserItem.toolTip = nil;
NSError *error = nil; NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]]; fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [moc executeFetchRequest:fetchRequest error:&error]; NSArray *users = [moc executeFetchRequest:fetchRequest error:&error];
if (!users) if (!users)
err(@"Failed to load users: %@", error); err(@"Failed to load users: %@", error);
if (![users count]) { if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
noUsersItem.enabled = NO; noUsersItem.enabled = NO;
noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. " noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
@"Then give iCloud some time to sync the new user to your Mac."; @"Then give iCloud some time to sync the new user to your Mac.";
} }
for (MPUserEntity *user in users) { for (MPUserEntity *user in users) {
@ -123,7 +123,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)activate:(id)sender { - (IBAction)activate:(id)sender {
if (!self.activeUser) if (!self.activeUser)
// No user, can't activate. // No user, can't activate.
return; return;
if ([[NSApplication sharedApplication] isActive]) if ([[NSApplication sharedApplication] isActive])
@ -163,8 +163,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification [[NSNotificationCenter defaultCenter]
object:NSStringFromSelector(configKey) userInfo:nil]; postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey ) userInfo:nil];
} }
#pragma mark - NSApplicationDelegate #pragma mark - NSApplicationDelegate
@ -176,17 +176,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
__weak id weakSelf = self; __weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[weakSelf updateMenuItems]; [weakSelf updateMenuItems];
} forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil]; } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[weakSelf updateMenuItems]; [weakSelf updateMenuItems];
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil]; } forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
// Status item. // Status item.
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusItem.image = [NSImage imageNamed:@"menu-icon"]; self.statusItem.image = [NSImage imageNamed:@"menu-icon"];
self.statusItem.highlightMode = YES; self.statusItem.highlightMode = YES;
self.statusItem.target = self; self.statusItem.target = self;
self.statusItem.action = @selector(showMenu); self.statusItem.action = @selector(showMenu);
__weak MPAppDelegate *wSelf = self; __weak MPAppDelegate *wSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
@ -201,33 +201,33 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[MPMacConfig get].usedUserName = activeUser.name; [MPMacConfig get].usedUserName = activeUser.name;
} forKeyPath:@"activeUserObjectID" options:0 context:nil]; } forKeyPath:@"activeUserObjectID" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock: addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState; self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState;
}]; }];
[self updateUsers]; [self updateUsers];
// Global hotkey. // Global hotkey.
EventHotKeyRef hotKeyRef; EventHotKeyRef hotKeyRef;
EventTypeSpec hotKeyEvents[1] = {{.eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed}}; EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
OSStatus status = InstallApplicationEventHandler(NewEventHandlerUPP(MPHotKeyHander), GetEventTypeCount(hotKeyEvents), OSStatus status = InstallApplicationEventHandler(NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ),
hotKeyEvents, hotKeyEvents,
(__bridge void *)self, NULL); (__bridge void *)self, NULL);
if (status != noErr) if (status != noErr)
err(@"Error installing application event handler: %d", status); err(@"Error installing application event handler: %d", status);
status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef); status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr) if (status != noErr)
err(@"Error registering 'show' hotkey: %d", status); err(@"Error registering 'show' hotkey: %d", status);
status = RegisterEventHotKey(35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef); status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr) if (status != noErr)
err(@"Error registering 'lock' hotkey: %d", status); err(@"Error registering 'lock' hotkey: %d", status);
} }
@ -235,50 +235,55 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)updateMenuItems { - (void)updateMenuItems {
if (!(self.showItem.enabled = ![self.passwordWindow.window isVisible])) { if (!(self.showItem.enabled = ![self.passwordWindow.window isVisible])) {
self.showItem.title = @"Show (Showing)"; self.showItem.title = @"Show (Showing)";
self.showItem.toolTip = @"Master Password is already showing."; self.showItem.toolTip = @"Master Password is already showing.";
} else if (!(self.showItem.enabled = (self.activeUser != nil))) { }
self.showItem.title = @"Show (No user)"; else if (!(self.showItem.enabled = (self.activeUser != nil))) {
self.showItem.title = @"Show (No user)";
self.showItem.toolTip = @"First select the user to show passwords for."; self.showItem.toolTip = @"First select the user to show passwords for.";
} else { }
self.showItem.title = @"Show"; else {
self.showItem.title = @"Show";
self.showItem.toolTip = nil; self.showItem.toolTip = nil;
} }
if (self.key) { if (self.key) {
self.lockItem.title = @"Lock"; self.lockItem.title = @"Lock";
self.lockItem.enabled = YES; self.lockItem.enabled = YES;
self.lockItem.toolTip = nil; self.lockItem.toolTip = nil;
} else { }
self.lockItem.title = @"Lock (Locked)"; else {
self.lockItem.title = @"Lock (Locked)";
self.lockItem.enabled = NO; self.lockItem.enabled = NO;
self.lockItem.toolTip = @"Master Password is currently locked."; self.lockItem.toolTip = @"Master Password is currently locked.";
} }
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState; self.savePasswordItem.state = [MPAppDelegate get].activeUser.saveKey? NSOnState: NSOffState;
if (!self.activeUser) { if (!self.activeUser) {
self.savePasswordItem.title = @"Save Password (No user)"; self.savePasswordItem.title = @"Save Password (No user)";
self.savePasswordItem.enabled = NO; self.savePasswordItem.enabled = NO;
self.savePasswordItem.toolTip = @"First select your user and unlock by showing the Master Password window."; self.savePasswordItem.toolTip = @"First select your user and unlock by showing the Master Password window.";
} else if (!self.key) { }
self.savePasswordItem.title = @"Save Password (Locked)"; else if (!self.key) {
self.savePasswordItem.title = @"Save Password (Locked)";
self.savePasswordItem.enabled = NO; self.savePasswordItem.enabled = NO;
self.savePasswordItem.toolTip = @"First unlock by showing the Master Password window."; self.savePasswordItem.toolTip = @"First unlock by showing the Master Password window.";
} else { }
self.savePasswordItem.title = @"Save Password"; else {
self.savePasswordItem.title = @"Save Password";
self.savePasswordItem.enabled = YES; self.savePasswordItem.enabled = YES;
self.savePasswordItem.toolTip = nil; self.savePasswordItem.toolTip = nil;
} }
self.useICloudItem.state = [[MPMacConfig get].iCloud boolValue]? NSOnState: NSOffState; self.useICloudItem.state = [[MPMacConfig get].iCloud boolValue]? NSOnState: NSOffState;
if (!(self.useICloudItem.enabled = ![[MPMacConfig get].iCloud boolValue])) { if (!(self.useICloudItem.enabled = ![[MPMacConfig get].iCloud boolValue])) {
self.useICloudItem.title = @"Use iCloud (Required)"; self.useICloudItem.title = @"Use iCloud (Required)";
self.useICloudItem.toolTip = @"iCloud is required in this version. Future versions will work without iCloud as well."; self.useICloudItem.toolTip = @"iCloud is required in this version. Future versions will work without iCloud as well.";
} }
else { else {
self.useICloudItem.title = @"Use iCloud (Required)"; self.useICloudItem.title = @"Use iCloud (Required)";
self.useICloudItem.toolTip = nil; self.useICloudItem.toolTip = nil;
} }
} }
@ -315,7 +320,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
NSError *error = nil; NSError *error = nil;
if (![moc save:&error]) if (![moc save:&error])
err(@"While terminating: %@", error); err(@"While terminating: %@", error);
return NSTerminateNow; return NSTerminateNow;
} }
@ -345,17 +350,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[NSAlert alertWithMessageText:@"About iCloud" [[NSAlert alertWithMessageText:@"About iCloud"
defaultButton:[PearlStrings get].commonButtonThanks alternateButton:nil otherButton:nil defaultButton:[PearlStrings get].commonButtonThanks alternateButton:nil otherButton:nil
informativeTextWithFormat: informativeTextWithFormat:
@"iCloud is Apple's solution for saving your data in \"the cloud\" " @"iCloud is Apple's solution for saving your data in \"the cloud\" "
@"and making sure your other iPhones, iPads and Macs are in sync.\n\n" @"and making sure your other iPhones, iPads and Macs are in sync.\n\n"
@"For Master Password, that means your sites are available on all your " @"For Master Password, that means your sites are available on all your "
@"Apple devices, and you always have a backup of them in case " @"Apple devices, and you always have a backup of them in case "
@"you loose one or need to restore.\n\n" @"you loose one or need to restore.\n\n"
@"Because of the way Master Password works, it doesn't need to send your " @"Because of the way Master Password works, it doesn't need to send your "
@"site's passwords to Apple. Only their names are saved to make it easier " @"site's passwords to Apple. Only their names are saved to make it easier "
@"for you to find the site you need. For some sites you may have set " @"for you to find the site you need. For some sites you may have set "
@"a user-specified password: these are sent to iCloud after being encrypted " @"a user-specified password: these are sent to iCloud after being encrypted "
@"with your master password.\n\n" @"with your master password.\n\n"
@"Apple can never see any of your passwords."] runModal]; @"Apple can never see any of your passwords."] runModal];
[self ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; [self ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled];
break; break;
} }

View File

@ -10,6 +10,6 @@
@interface MPMacConfig : MPConfig @interface MPMacConfig : MPConfig
@property (nonatomic, retain) NSString *usedUserName; @property(nonatomic, retain) NSString *usedUserName;
@end @end

View File

@ -15,7 +15,7 @@
if (!(self = [super init])) if (!(self = [super init]))
return self; return self;
[self.defaults registerDefaults:@{NSStringFromSelector(@selector(iTunesID)): @"510296984"}]; [self.defaults registerDefaults:@{ NSStringFromSelector( @selector(iTunesID) ) : @"510296984" }];
return self; return self;
} }

View File

@ -13,14 +13,14 @@
NSString *_content; NSString *_content;
} }
@property (nonatomic, strong) NSString *content; @property(nonatomic, strong) NSString *content;
@property (nonatomic, weak) IBOutlet NSTextField *siteField; @property(nonatomic, weak) IBOutlet NSTextField *siteField;
@property (nonatomic, weak) IBOutlet NSTextField *contentField; @property(nonatomic, weak) IBOutlet NSTextField *contentField;
@property (nonatomic, weak) IBOutlet NSTextField *tipField; @property(nonatomic, weak) IBOutlet NSTextField *tipField;
@property (nonatomic, weak) IBOutlet NSView *contentContainer; @property(nonatomic, weak) IBOutlet NSView *contentContainer;
@property (nonatomic, weak) IBOutlet NSProgressIndicator *progressView; @property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
@property (nonatomic, weak) IBOutlet NSTextField *userLabel; @property(nonatomic, weak) IBOutlet NSTextField *userLabel;
- (void)unlock; - (void)unlock;

View File

@ -14,12 +14,11 @@
#define MPAlertUnlockMP @"MPAlertUnlockMP" #define MPAlertUnlockMP @"MPAlertUnlockMP"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP" #define MPAlertIncorrectMP @"MPAlertIncorrectMP"
@interface MPPasswordWindowController()
@interface MPPasswordWindowController () @property(nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
@property(nonatomic) BOOL inProgress;
@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults; @property(nonatomic) BOOL siteFieldPreventCompletion;
@property (nonatomic) BOOL inProgress;
@property (nonatomic) BOOL siteFieldPreventCompletion;
@end @end
@ -31,7 +30,7 @@
[self.tipField setStringValue:@""]; [self.tipField setStringValue:@""];
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[self.userLabel setStringValue:PearlString(@"%@'s password for:", [MPAppDelegate get].activeUser.name)]; [self.userLabel setStringValue:PearlString( @"%@'s password for:", [MPAppDelegate get].activeUser.name )];
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil]; } forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
if (![MPAppDelegate get].key) { if (![MPAppDelegate get].key) {
@ -44,25 +43,25 @@
if (![MPAlgorithmDefault migrateUser:activeUser]) if (![MPAlgorithmDefault migrateUser:activeUser])
[NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil [NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil
informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the " informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the "
@"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause " @"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause "
@"their passwords to change. You'll need to update your profile for that site with the new password."]; @"their passwords to change. You'll need to update your profile for that site with the new password."];
[moc saveToStore]; [moc saveToStore];
}]; }];
} forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil]; } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil usingBlock:^(NSNotification *note) {
if (!self.inProgress) if (!self.inProgress)
[self unlock]; [self unlock];
[self.siteField selectText:self]; [self.siteField selectText:self];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:NSWindowWillCloseNotification object:self.window queue:nil usingBlock:^(NSNotification *note) {
[[NSApplication sharedApplication] hide:self]; [[NSApplication sharedApplication] hide:self];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self.window close]; [self.window close];
}]; }];
[super windowDidLoad]; [super windowDidLoad];
} }
@ -70,18 +69,18 @@
- (void)unlock { - (void)unlock {
if (![MPAppDelegate get].activeUser) if (![MPAppDelegate get].activeUser)
// No user to sign in with. // No user to sign in with.
return; return;
if ([MPAppDelegate get].key) if ([MPAppDelegate get].key)
// Already logged in. // Already logged in.
return; return;
if ([[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:nil]) if ([[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:nil])
// Load the key from the keychain. // Load the key from the keychain.
return; return;
if (![MPAppDelegate get].key) if (![MPAppDelegate get].key)
// Ask the user to set the key through his master password. // Ask the user to set the key through his master password.
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if ([MPAppDelegate get].key) if ([MPAppDelegate get].key)
return; return;
@ -89,16 +88,17 @@
[self.siteField setStringValue:@""]; [self.siteField setStringValue:@""];
[self.tipField setStringValue:@""]; [self.tipField setStringValue:@""];
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked." NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel" defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel"
informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@", [MPAppDelegate get].activeUser.name]; informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@",
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 22)]; [MPAppDelegate get].activeUser.name];
NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert setAccessoryView:passwordField]; [alert setAccessoryView:passwordField];
[alert layout]; [alert layout];
[passwordField becomeFirstResponder]; [passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP]; didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
}); } );
} }
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
@ -114,12 +114,12 @@
if ([[NSAlert alertWithMessageText:@"Changing Master Password" if ([[NSAlert alertWithMessageText:@"Changing Master Password"
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
informativeTextWithFormat: informativeTextWithFormat:
@"This will allow you to log in with a different master password.\n\n" @"This will allow you to log in with a different master password.\n\n"
@"Note that you will only see the sites and passwords for the master password you log in with.\n" @"Note that you will only see the sites and passwords for the master password you log in with.\n"
@"If you log in with a different master password, your current sites will be unavailable.\n\n" @"If you log in with a different master password, your current sites will be unavailable.\n\n"
@"You can always change back to your current master password later.\n" @"You can always change back to your current master password later.\n"
@"Your current sites and passwords will then become available again."] runModal] @"Your current sites and passwords will then become available again."] runModal]
== 1) { == 1) {
[MPAppDelegate get].activeUser.keyID = nil; [MPAppDelegate get].activeUser.keyID = nil;
[[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser]; [[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser];
[[MPAppDelegate get] signOutAnimated:YES]; [[MPAppDelegate get] signOutAnimated:YES];
@ -136,25 +136,25 @@
self.contentContainer.alphaValue = 0; self.contentContainer.alphaValue = 0;
[self.progressView startAnimation:nil]; [self.progressView startAnimation:nil];
self.inProgress = YES; self.inProgress = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0 ), ^{
BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]]; usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
self.inProgress = NO; self.inProgress = NO;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[self.progressView stopAnimation:nil]; [self.progressView stopAnimation:nil];
if (success) if (success)
self.contentContainer.alphaValue = 1; self.contentContainer.alphaValue = 1;
else { else {
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : PearlString(@"Incorrect master password for user %@", NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@",
[MPAppDelegate get].activeUser.name) [MPAppDelegate get].activeUser.name )
}]] beginSheetModalForWindow:self.window modalDelegate:self }]] beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP]; didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP];
} }
}); } );
}); } );
} }
default: default:
@ -172,10 +172,10 @@
if (![query length] || ![MPAppDelegate get].key) if (![query length] || ![MPAppDelegate get].key)
return nil; return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@", fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
query, [MPAppDelegate get].activeUser]; query, [MPAppDelegate get].activeUser];
NSError *error = nil; NSError *error = nil;
self.siteResults = [[MPAppDelegate managedObjectContextForThreadIfReady] executeFetchRequest:fetchRequest error:&error]; self.siteResults = [[MPAppDelegate managedObjectContextForThreadIfReady] executeFetchRequest:fetchRequest error:&error];
@ -184,12 +184,12 @@
if ([self.siteResults count] == 1) { if ([self.siteResults count] == 1) {
[textView setString:[(MPElementEntity *)[self.siteResults objectAtIndex:0] name]]; [textView setString:[(MPElementEntity *)[self.siteResults objectAtIndex:0] name]];
[textView setSelectedRange:NSMakeRange([query length], [[textView string] length] - [query length])]; [textView setSelectedRange:NSMakeRange( [query length], [[textView string] length] - [query length] )];
if ([self trySite]) if ([self trySite])
return nil; return nil;
} }
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1]; NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
if (self.siteResults) if (self.siteResults)
for (MPElementEntity *element in self.siteResults) for (MPElementEntity *element in self.siteResults)
[mutableResults addObject:element.name]; [mutableResults addObject:element.name];
@ -204,7 +204,7 @@
[self.window close]; [self.window close];
return YES; return YES;
} }
if ((self.siteFieldPreventCompletion = [NSStringFromSelector(commandSelector) hasPrefix:@"delete"])) if ((self.siteFieldPreventCompletion = [NSStringFromSelector( commandSelector ) hasPrefix:@"delete"]))
return NO; return NO;
if (commandSelector == @selector(insertNewline:) && [self.content length]) { if (commandSelector == @selector(insertNewline:) && [self.content length]) {
if ([self trySite]) if ([self trySite])
@ -223,17 +223,17 @@
return; return;
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
self.tipField.alphaValue = 1; self.tipField.alphaValue = 1;
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."]; [self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)); dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) );
dispatch_after(popTime, dispatch_get_main_queue(), ^{ dispatch_after( popTime, dispatch_get_main_queue(), ^{
[NSAnimationContext beginGrouping]; [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f]; [[NSAnimationContext currentContext] setDuration:0.2f];
[self.tipField.animator setAlphaValue:0]; [self.tipField.animator setAlphaValue:0];
[NSAnimationContext endGrouping]; [NSAnimationContext endGrouping];
}); } );
}); } );
[[self findElement] use]; [[self findElement] use];
} }
@ -283,10 +283,10 @@
paragraph.alignment = NSCenterTextAlignment; paragraph.alignment = NSCenterTextAlignment;
[self.contentField setAttributedStringValue: [self.contentField setAttributedStringValue:
[[NSAttributedString alloc] initWithString:_content [[NSAttributedString alloc] initWithString:_content
attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys: attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
paragraph, NSParagraphStyleAttributeName, paragraph, NSParagraphStyleAttributeName,
nil]]]; nil]]];
} }
- (BOOL)trySite { - (BOOL)trySite {
@ -298,17 +298,17 @@
return NO; return NO;
} }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
NSString *description = [result.content description]; NSString *description = [result.content description];
if (!description) if (!description)
description = @""; description = @"";
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[self setContent:description]; [self setContent:description];
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."]; [self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
self.tipField.alphaValue = 1; self.tipField.alphaValue = 1;
}); } );
}); } );
// For when the app should be able to create new sites. // For when the app should be able to create new sites.
/* /*

View File

@ -1,38 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>Icon</string> <string>Icon</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}.Mac</string> <string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}.Mac</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>${PRODUCT_NAME}</string> <string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string> <string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string> <string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2011-2013 Lyndir. All rights reserved.</string> <string>Copyright © 2011-2013 Lyndir. All rights reserved.</string>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
</dict> </dict>
</plist> </plist>

View File

@ -4,7 +4,6 @@
#import "Pearl-Prefix.pch" #import "Pearl-Prefix.pch"
#ifdef __OBJC__ #ifdef __OBJC__
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>

View File

@ -6,9 +6,7 @@
// Copyright (c) 2012 Lyndir. All rights reserved. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
return NSApplicationMain(argc, (const char **)argv); return NSApplicationMain( argc, (const char **)argv );
} }

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/> <attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/> <attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/> <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
@ -21,7 +23,8 @@
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/> <attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"/> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"/>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/> <element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
@ -11,7 +12,8 @@
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/> <attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/> <attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/> <attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/> <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
@ -29,7 +31,8 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/> <element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1810" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
lastSavedToolsVersion="1810" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
@ -11,7 +12,8 @@
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/> <attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/> <attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/> <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
@ -29,7 +31,8 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/> <element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
@ -11,7 +12,8 @@
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/> <attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/> <attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/> <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
@ -28,7 +30,8 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/> <element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>

View File

@ -3,7 +3,7 @@
// //
#ifdef __OBJC__ #ifdef __OBJC__
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#endif #endif
#define PEARL_WITH_SCRYPT #define PEARL_WITH_SCRYPT

View File

@ -14,13 +14,13 @@
@interface MPAppDelegate : MPAppDelegate_Shared @interface MPAppDelegate : MPAppDelegate_Shared
@property (nonatomic, readonly) GPPShare *googlePlus; @property(nonatomic, readonly) GPPShare *googlePlus;
- (void)showGuide; - (void)showGuide;
- (void)showSetup; - (void)showSetup;
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController; - (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)export; - (void)export;
- (void)changeMasterPasswordFor:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc didResetBlock:(void(^)(void))didReset; - (void)changeMasterPasswordFor:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc didResetBlock:(void (^)(void))didReset;
@end @end

View File

@ -12,13 +12,12 @@
#import "IASKSettingsReader.h" #import "IASKSettingsReader.h"
@interface MPAppDelegate () @interface MPAppDelegate()
@property (nonatomic, readwrite) GPPShare *googlePlus; @property(nonatomic, readwrite) GPPShare *googlePlus;
@end @end
@implementation MPAppDelegate @implementation MPAppDelegate
+ (void)initialize { + (void)initialize {
@ -59,12 +58,12 @@
level = PearlLogLevelInfo; level = PearlLogLevelInfo;
if (message.level >= level) if (message.level >= level)
TFLog(@"%@", [message messageDescription]); TFLog( @"%@", [message messageDescription] );
return YES; return YES;
}]; }];
TFLog(@"TestFlight (%@) initialized for: %@ v%@.", // TFLog( @"TestFlight (%@) initialized for: %@ v%@.", //
TESTFLIGHT_SDK_VERSION, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion); TESTFLIGHT_SDK_VERSION, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
} }
} }
@catch (id exception) { @catch (id exception) {
@ -99,12 +98,12 @@
level = PearlLogLevelInfo; level = PearlLogLevelInfo;
if (message.level >= level) if (message.level >= level)
CLSLog(@"%@", [message messageDescription]); CLSLog( @"%@", [message messageDescription] );
return YES; return YES;
}]; }];
CLSLog(@"Crashlytics (%@) initialized for: %@ v%@.", // CLSLog( @"Crashlytics (%@) initialized for: %@ v%@.", //
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion); [Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
} }
} }
@catch (id exception) { @catch (id exception) {
@ -133,34 +132,34 @@
err(@"Localytics exception: %@", exception); err(@"Localytics exception: %@", exception);
} }
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)]; UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake( 0, 5, 0, 5 )];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault]; [[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone]; [[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
[[UINavigationBar appearance] setTitleTextAttributes: [[UINavigationBar appearance] setTitleTextAttributes:
@{ @{
UITextAttributeTextColor: [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f], UITextAttributeTextColor : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
UITextAttributeTextShadowColor: [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f], UITextAttributeTextShadowColor : [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f],
UITextAttributeTextShadowOffset: [NSValue valueWithUIOffset:UIOffsetMake(0, -1)], UITextAttributeTextShadowOffset : [NSValue valueWithUIOffset:UIOffsetMake( 0, -1 )],
UITextAttributeFont: [UIFont fontWithName:@"Exo-Bold" size:20.0f] UITextAttributeFont : [UIFont fontWithName:@"Exo-Bold" size:20.0f]
}]; }];
UIImage *navBarButton = [[UIImage imageNamed:@"ui_navbar_button"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)]; UIImage *navBarButton = [[UIImage imageNamed:@"ui_navbar_button"] resizableImageWithCapInsets:UIEdgeInsetsMake( 0, 5, 0, 5 )];
UIImage *navBarBack = [[UIImage imageNamed:@"ui_navbar_back"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)]; UIImage *navBarBack = [[UIImage imageNamed:@"ui_navbar_back"] resizableImageWithCapInsets:UIEdgeInsetsMake( 0, 13, 0, 5 )];
[[UIBarButtonItem appearance] setBackgroundImage:navBarButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackgroundImage:navBarButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone]; [[UIBarButtonItem appearance] setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:navBarBack forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:navBarBack forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[[UIBarButtonItem appearance] setTitleTextAttributes: [[UIBarButtonItem appearance] setTitleTextAttributes:
@{ @{
UITextAttributeTextColor: [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f], UITextAttributeTextColor : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
UITextAttributeTextShadowColor: [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f], UITextAttributeTextShadowColor : [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f],
UITextAttributeTextShadowOffset: [NSValue valueWithUIOffset:UIOffsetMake(0, 1)]//, UITextAttributeTextShadowOffset : [NSValue valueWithUIOffset:UIOffsetMake( 0, 1 )]//,
// Causes a bug in iOS where image views get oddly stretched... or something. // Causes a bug in iOS where image views get oddly stretched... or something.
//UITextAttributeFont: [UIFont fontWithName:@"HelveticaNeue" size:13.0f] //UITextAttributeFont: [UIFont fontWithName:@"HelveticaNeue" size:13.0f]
} }
forState:UIControlStateNormal]; forState:UIControlStateNormal];
UIImage *toolBarImage = [[UIImage imageNamed:@"ui_toolbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(25, 5, 5, 5)]; UIImage *toolBarImage = [[UIImage imageNamed:@"ui_toolbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake( 25, 5, 5, 5 )];
[[UISearchBar appearance] setBackgroundImage:toolBarImage]; [[UISearchBar appearance] setBackgroundImage:toolBarImage];
[[UIToolbar appearance] setBackgroundImage:toolBarImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault]; [[UIToolbar appearance] setBackgroundImage:toolBarImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
@ -188,58 +187,62 @@
*/ */
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
if ([[MPiOSConfig get].sendInfo boolValue]) { if ([[MPiOSConfig get].sendInfo boolValue]) {
if ([PearlLogger get].printLevel > PearlLogLevelInfo) if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo; [PearlLogger get].printLevel = PearlLogLevelInfo;
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"]; [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
[[Crashlytics sharedInstance] setBoolValue:[self storeManager].cloudEnabled forKey:@"iCloud"]; [[Crashlytics sharedInstance] setBoolValue:[self storeManager].cloudEnabled forKey:@"iCloud"];
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"]; [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"]; [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"]; [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"]; [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"]; [[Crashlytics sharedInstance]
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"]; setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
#ifdef TESTFLIGHT_SDK_VERSION #ifdef TESTFLIGHT_SDK_VERSION
[TestFlight addCustomEnvironmentInformation:[[MPConfig get].rememberLogin boolValue]? @"YES": @"NO" forKey:@"rememberLogin"]; [TestFlight addCustomEnvironmentInformation:[@([[MPConfig get].rememberLogin boolValue]) description]
[TestFlight addCustomEnvironmentInformation:[self storeManager].cloudEnabled? @"YES": @"NO" forKey:@"iCloud"]; forKey:@"rememberLogin"];
[TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO" forKey:@"iCloudDecided"]; [TestFlight addCustomEnvironmentInformation:[@([self storeManager].cloudEnabled) description] forKey:@"iCloud"];
[TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO" forKey:@"sendInfo"]; [TestFlight addCustomEnvironmentInformation:[@([[MPConfig get].iCloudDecided boolValue]) description]
[TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO" forKey:@"helpHidden"]; forKey:@"iCloudDecided"];
[TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].showSetup boolValue]? @"YES": @"NO" [TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].sendInfo boolValue]) description] forKey:@"sendInfo"];
forKey:@"showQuickStart"]; [TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].helpHidden boolValue]) description]
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].firstRun boolValue]? @"YES": @"NO" forKey:@"firstRun"]; forKey:@"helpHidden"];
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].launchCount description] forKey:@"launchCount"]; [TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].showSetup boolValue]) description]
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].askForReviews boolValue]? @"YES": @"NO" forKey:@"showQuickStart"];
forKey:@"askForReviews"]; [TestFlight addCustomEnvironmentInformation:[@([[PearlConfig get].firstRun boolValue]) description] forKey:@"firstRun"];
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].reviewAfterLaunches description] forKey:@"reviewAfterLaunches"]; [TestFlight addCustomEnvironmentInformation:[[PearlConfig get].launchCount description] forKey:@"launchCount"];
[TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"]; [TestFlight addCustomEnvironmentInformation:[@([[PearlConfig get].askForReviews boolValue]) description]
forKey:@"askForReviews"];
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].reviewAfterLaunches description]
forKey:@"reviewAfterLaunches"];
[TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
#endif #endif
MPCheckpoint( MPCheckpointConfig, @{ MPCheckpoint( MPCheckpointConfig, @{
@"rememberLogin" : [[MPConfig get].rememberLogin boolValue]? @"YES": @"NO", @"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]),
@"iCloud" : [self storeManager].cloudEnabled? @"YES": @"NO", @"iCloud" : @([self storeManager].cloudEnabled),
@"iCloudDecided" : [[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO", @"iCloudDecided" : @([[MPConfig get].iCloudDecided boolValue]),
@"sendInfo" : [[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO", @"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]),
@"helpHidden" : [[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO", @"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]),
@"showQuickStart" : [[MPiOSConfig get].showSetup boolValue]? @"YES": @"NO", @"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]),
@"firstRun" : [[PearlConfig get].firstRun boolValue]? @"YES": @"NO", @"firstRun" : @([[PearlConfig get].firstRun boolValue]),
@"launchCount" : NilToNSNull([[PearlConfig get].launchCount description]), @"launchCount" : NilToNSNull([[PearlConfig get].launchCount description]),
@"askForReviews" : [[PearlConfig get].askForReviews boolValue]? @"YES": @"NO", @"askForReviews" : @([[PearlConfig get].askForReviews boolValue]),
@"reviewAfterLaunches" : NilToNSNull([[PearlConfig get].reviewAfterLaunches description]), @"reviewAfterLaunches" : NilToNSNull([[PearlConfig get].reviewAfterLaunches description]),
@"reviewedVersion" : NilToNSNull([PearlConfig get].reviewedVersion) @"reviewedVersion" : NilToNSNull([PearlConfig get].reviewedVersion)
} ); } );
} }
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock:^(NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note userInfo:nil];
object:note userInfo:nil]; }];
}];
#ifdef ADHOC #ifdef ADHOC
[PearlAlert showAlertWithTitle:@"Welcome, tester!" message: [PearlAlert showAlertWithTitle:@"Welcome, tester!" message:
@ -257,10 +260,10 @@
inf(@"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier]); inf(@"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier]);
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if ([[MPiOSConfig get].showSetup boolValue]) if ([[MPiOSConfig get].showSetup boolValue])
[[MPAppDelegate get] showSetup]; [[MPAppDelegate get] showSetup];
}); } );
return YES; return YES;
} }
@ -277,11 +280,11 @@
return YES; return YES;
// Arbitrary URL to mpsites data. // Arbitrary URL to mpsites data.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSError *error; NSError *error;
NSURLResponse *response; NSURLResponse *response;
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error]; returningResponse:&response error:&error];
if (error) if (error)
err(@"While reading imported sites from %@: %@", url, error); err(@"While reading imported sites from %@: %@", url, error);
if (!importedSitesData) if (!importedSitesData)
@ -294,11 +297,11 @@
__block NSString *masterPassword = nil; __block NSString *masterPassword = nil;
dispatch_group_t importPasswordGroup = dispatch_group_create(); dispatch_group_t importPasswordGroup = dispatch_group_create();
dispatch_group_enter(importPasswordGroup); dispatch_group_enter( importPasswordGroup );
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import File's Master Password" [PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:PearlString(@"%@'s export was done using a different master password.\n" message:PearlString( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName) @"Enter that master password to unlock the exported data.", userName )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try { @try {
@ -308,23 +311,23 @@
masterPassword = [alert_ textFieldAtIndex:0].text; masterPassword = [alert_ textFieldAtIndex:0].text;
} }
@finally { @finally {
dispatch_group_leave(importPasswordGroup); dispatch_group_leave( importPasswordGroup );
} }
} }
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil]; cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
}); } );
dispatch_group_wait(importPasswordGroup, DISPATCH_TIME_FOREVER); dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword; return masterPassword;
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) { } askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
__block NSString *masterPassword = nil; __block NSString *masterPassword = nil;
dispatch_group_t userPasswordGroup = dispatch_group_create(); dispatch_group_t userPasswordGroup = dispatch_group_create();
dispatch_group_enter(userPasswordGroup); dispatch_group_enter( userPasswordGroup );
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:PearlString(@"Master Password for\n%@", userName) [PearlAlert showAlertWithTitle:PearlString( @"Master Password for\n%@", userName )
message:PearlString(@"Imports %d sites, overwriting %d.", importCount, message:PearlString( @"Imports %d sites, overwriting %d.", importCount,
deleteCount) deleteCount )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try { @try {
@ -334,12 +337,12 @@
masterPassword = [alert_ textFieldAtIndex:0].text; masterPassword = [alert_ textFieldAtIndex:0].text;
} }
@finally { @finally {
dispatch_group_leave(userPasswordGroup); dispatch_group_leave( userPasswordGroup );
} }
} }
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil]; cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
}); } );
dispatch_group_wait(userPasswordGroup, DISPATCH_TIME_FOREVER); dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword; return masterPassword;
}]; }];
@ -360,7 +363,7 @@
} }
[activityAlert cancelAlertAnimated:YES]; [activityAlert cancelAlertAnimated:YES];
}); } );
return YES; return YES;
} }
@ -411,8 +414,7 @@
- (void)applicationDidBecomeActive:(UIApplication *)application { - (void)applicationDidBecomeActive:(UIApplication *)application {
inf(@"Re-activated"); inf(@"Re-activated");
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:application userInfo:nil];
object:application userInfo:nil];
[[LocalyticsSession sharedLocalyticsSession] resume]; [[LocalyticsSession sharedLocalyticsSession] resume];
[[LocalyticsSession sharedLocalyticsSession] upload]; [[LocalyticsSession sharedLocalyticsSession] upload];
@ -453,26 +455,25 @@
if (![PearlEMail canSendMail]) if (![PearlEMail canSendMail])
[PearlAlert showAlertWithTitle:@"Feedback" [PearlAlert showAlertWithTitle:@"Feedback"
message: message:
@"Have a question, comment, issue or just saying thanks?\n\n" @"Have a question, comment, issue or just saying thanks?\n\n"
@"We'd love to hear what you think!\n" @"We'd love to hear what you think!\n"
@"masterpassword@lyndir.com" @"masterpassword@lyndir.com"
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
otherTitles:nil]; otherTitles:nil];
else if (logs)
[PearlAlert showAlertWithTitle:@"Feedback"
message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"If you're having trouble, it may help us if you can first reproduce the problem "
@"and then include log files in your message."
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
[self openFeedbackWithLogs:(buttonIndex_ == [alert_ firstOtherButtonIndex]) forVC:viewController];
} cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil];
else else
if (logs) [self openFeedbackWithLogs:NO forVC:viewController];
[PearlAlert showAlertWithTitle:@"Feedback"
message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"If you're having trouble, it may help us if you can first reproduce the problem "
@"and then include log files in your message."
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
[self openFeedbackWithLogs:(buttonIndex_ == [alert_ firstOtherButtonIndex]) forVC:viewController];
} cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil];
else
[self openFeedbackWithLogs:NO forVC:viewController];
} }
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController { - (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
@ -481,46 +482,48 @@
PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
[[[PearlEMail alloc] initForEMailTo:@"Master Password Development <masterpassword@lyndir.com>" [[[PearlEMail alloc] initForEMailTo:@"Master Password Development <masterpassword@lyndir.com>"
subject:PearlString(@"Feedback for Master Password [%@]", subject:PearlString( @"Feedback for Master Password [%@]",
[[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"]) [[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"] )
body:PearlString(@"\n\n\n" body:PearlString( @"\n\n\n"
@"--\n" @"--\n"
@"%@" @"%@"
@"Master Password %@, build %@", @"Master Password %@, build %@",
userName? ([userName stringByAppendingString:@"\n"]): @"", userName? ([userName stringByAppendingString:@"\n"]): @"",
[PearlInfoPlist get].CFBundleShortVersionString, [PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion) [PearlInfoPlist get].CFBundleVersion )
attachments:(logs attachments:(logs
? [[PearlEMailAttachment alloc] initWithContent:[[[PearlLogger get] formatMessagesWithLevel:logLevel] dataUsingEncoding:NSUTF8StringEncoding] ? [[PearlEMailAttachment alloc]
mimeType:@"text/plain" initWithContent:[[[PearlLogger get] formatMessagesWithLevel:logLevel]
fileName:PearlString(@"%@-%@.log", dataUsingEncoding:NSUTF8StringEncoding]
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]], mimeType:@"text/plain"
[PearlKeyChain deviceIdentifier])] fileName:PearlString( @"%@-%@.log",
: nil), nil] [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
showComposerForVC:viewController]; [PearlKeyChain deviceIdentifier] )]
: nil), nil]
showComposerForVC:viewController];
} }
- (void)export { - (void)export {
[PearlAlert showNotice: [PearlAlert showNotice:
@"This will export all your site names.\n\n" @"This will export all your site names.\n\n"
@"You can open the export with a text editor to get an overview of all your sites.\n\n" @"You can open the export with a text editor to get an overview of all your sites.\n\n"
@"The file also acts as a personal backup of your site list in case you don't sync with iCloud/iTunes." @"The file also acts as a personal backup of your site list in case you don't sync with iCloud/iTunes."
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[PearlAlert showAlertWithTitle:@"Reveal Passwords?" message: [PearlAlert showAlertWithTitle:@"Reveal Passwords?" message:
@"Would you like to make all your passwords visible in the export?\n\n" @"Would you like to make all your passwords visible in the export?\n\n"
@"A safe export will only include your stored passwords, in an encrypted manner, " @"A safe export will only include your stored passwords, in an encrypted manner, "
@"making the result safe from falling in the wrong hands.\n\n" @"making the result safe from falling in the wrong hands.\n\n"
@"If all your passwords are shown and somebody else finds the export, " @"If all your passwords are shown and somebody else finds the export, "
@"they could gain access to all your sites!" @"they could gain access to all your sites!"
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0) if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
// Safe Export // Safe Export
[self exportShowPasswords:NO]; [self exportShowPasswords:NO];
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1) if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
// Show Passwords // Show Passwords
[self exportShowPasswords:YES]; [self exportShowPasswords:YES];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil]; } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil];
} otherTitles:nil]; } otherTitles:nil];
@ -531,8 +534,8 @@
if (![PearlEMail canSendMail]) { if (![PearlEMail canSendMail]) {
[PearlAlert showAlertWithTitle:@"Cannot Send Mail" [PearlAlert showAlertWithTitle:@"Cannot Send Mail"
message: message:
@"Your device is not yet set up for sending mail.\n" @"Your device is not yet set up for sending mail.\n"
@"Close Master Password, go into Settings and add a Mail account." @"Close Master Password, go into Settings and add a Mail account."
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
otherTitles:nil]; otherTitles:nil];
@ -543,22 +546,22 @@
NSString *message; NSString *message;
if (showPasswords) if (showPasswords)
message = PearlString(@"Export of Master Password sites with passwords included.\n\n" message = PearlString( @"Export of Master Password sites with passwords included.\n\n"
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n" @"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
@"--\n" @"--\n"
@"%@\n" @"%@\n"
@"Master Password %@, build %@", @"Master Password %@, build %@",
[self activeUserForThread].name, [self activeUserForThread].name,
[PearlInfoPlist get].CFBundleShortVersionString, [PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion); [PearlInfoPlist get].CFBundleVersion );
else else
message = PearlString(@"Backup of Master Password sites.\n\n\n" message = PearlString( @"Backup of Master Password sites.\n\n\n"
@"--\n" @"--\n"
@"%@\n" @"%@\n"
@"Master Password %@, build %@", @"Master Password %@, build %@",
[self activeUserForThread].name, [self activeUserForThread].name,
[PearlInfoPlist get].CFBundleShortVersionString, [PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion); [PearlInfoPlist get].CFBundleVersion );
NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@ -566,7 +569,8 @@
[PearlEMail sendEMailTo:nil subject:@"Master Password Export" body:message [PearlEMail sendEMailTo:nil subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding] attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName: mimeType:@"text/plain" fileName:
PearlString(@"%@ (%@).mpsites", [self activeUserForThread].name, [exportDateFormatter stringFromDate:[NSDate date]])], PearlString( @"%@ (%@).mpsites", [self activeUserForThread].name,
[exportDateFormatter stringFromDate:[NSDate date]] )],
nil]; nil];
} }
@ -574,9 +578,9 @@
[PearlAlert showAlertWithTitle:@"Changing Master Password" [PearlAlert showAlertWithTitle:@"Changing Master Password"
message: message:
@"If you continue, you'll be able to set a new master password.\n\n" @"If you continue, you'll be able to set a new master password.\n\n"
@"Changing your master password will cause all your generated passwords to change!\n" @"Changing your master password will cause all your generated passwords to change!\n"
@"Changing the master password back to the old one will cause your passwords to revert as well." @"Changing the master password back to the old one will cause your passwords to revert as well."
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex]) if (buttonIndex == [alert cancelButtonIndex])
@ -603,8 +607,8 @@
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value { - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification [[NSNotificationCenter defaultCenter]
object:NSStringFromSelector(configKey) userInfo:nil]; postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey ) userInfo:nil];
} }
#pragma mark - UbiquityStoreManagerDelegate #pragma mark - UbiquityStoreManagerDelegate
@ -619,34 +623,33 @@
- (void)alertCloudDisabledForManager:(UbiquityStoreManager *)manager { - (void)alertCloudDisabledForManager:(UbiquityStoreManager *)manager {
[PearlAlert showAlertWithTitle:@"iCloud" message: [PearlAlert showAlertWithTitle:@"iCloud" message:
@"iCloud is now disabled.\n\n" @"iCloud is now disabled.\n\n"
@"It is highly recommended you enable iCloud." @"It is highly recommended you enable iCloud."
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { if (buttonIndex == [alert firstOtherButtonIndex] + 0) {
if (buttonIndex == [alert firstOtherButtonIndex] + 0) { [PearlAlert showAlertWithTitle:@"About iCloud" message:
[PearlAlert showAlertWithTitle:@"About iCloud" message: @"iCloud is Apple's solution for saving your data in \"the cloud\" "
@"iCloud is Apple's solution for saving your data in \"the cloud\" " @"and making sure your other iPhones, iPads and Macs are in sync.\n\n"
@"and making sure your other iPhones, iPads and Macs are in sync.\n\n" @"For Master Password, that means your sites are available on all your "
@"For Master Password, that means your sites are available on all your " @"Apple devices, and you always have a backup of them in case "
@"Apple devices, and you always have a backup of them in case " @"you lose one or need to restore.\n\n"
@"you lose one or need to restore.\n\n" @"Thanks to the way Master Password works, it doesn't need to send your "
@"Thanks to the way Master Password works, it doesn't need to send your " @"site's passwords to Apple for the backup to work: Only their names are "
@"site's passwords to Apple for the backup to work: Only their names are " @"saved. If you set a custom password it will be sent to iCloud after "
@"saved. If you set a custom password it will be sent to iCloud after " @"being encrypted with your master password.\n\n"
@"being encrypted with your master password.\n\n" @"Apple can never see any of your passwords."
@"Apple can never see any of your passwords." viewStyle:UIAlertViewStyleDefault
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { [self alertCloudDisabledForManager:manager];
[self alertCloudDisabledForManager:manager]; }
} cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil];
cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil]; return;
return; }
}
[MPConfig get].iCloudDecided = @YES; [MPConfig get].iCloudDecided = @YES;
if (buttonIndex == [alert firstOtherButtonIndex] + 1) if (buttonIndex == [alert firstOtherButtonIndex] + 1)
manager.cloudEnabled = YES; manager.cloudEnabled = YES;
} cancelTitle:@"Leave Off" otherTitles:@"Explain?", @"Enable iCloud", nil]; } cancelTitle:@"Leave Off" otherTitles:@"Explain?", @"Enable iCloud", nil];
} }
@ -657,7 +660,7 @@
static NSDictionary *googlePlusInfo = nil; static NSDictionary *googlePlusInfo = nil;
if (googlePlusInfo == nil) if (googlePlusInfo == nil)
googlePlusInfo = [[NSDictionary alloc] initWithContentsOfURL: googlePlusInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Google+" withExtension:@"plist"]]; [[NSBundle mainBundle] URLForResource:@"Google+" withExtension:@"plist"]];
return googlePlusInfo; return googlePlusInfo;
} }
@ -675,7 +678,7 @@
static NSDictionary *testFlightInfo = nil; static NSDictionary *testFlightInfo = nil;
if (testFlightInfo == nil) if (testFlightInfo == nil)
testFlightInfo = [[NSDictionary alloc] initWithContentsOfURL: testFlightInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"TestFlight" withExtension:@"plist"]]; [[NSBundle mainBundle] URLForResource:@"TestFlight" withExtension:@"plist"]];
return testFlightInfo; return testFlightInfo;
} }
@ -693,7 +696,7 @@
static NSDictionary *crashlyticsInfo = nil; static NSDictionary *crashlyticsInfo = nil;
if (crashlyticsInfo == nil) if (crashlyticsInfo == nil)
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL: crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]]; [[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
return crashlyticsInfo; return crashlyticsInfo;
} }
@ -711,7 +714,7 @@
static NSDictionary *localyticsInfo = nil; static NSDictionary *localyticsInfo = nil;
if (localyticsInfo == nil) if (localyticsInfo == nil)
localyticsInfo = [[NSDictionary alloc] initWithContentsOfURL: localyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Localytics" withExtension:@"plist"]]; [[NSBundle mainBundle] URLForResource:@"Localytics" withExtension:@"plist"]];
return localyticsInfo; return localyticsInfo;
} }

View File

@ -17,7 +17,6 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface MPAppViewController : UIViewController @interface MPAppViewController : UIViewController
- (IBAction)gorillas:(UIButton *)sender; - (IBAction)gorillas:(UIButton *)sender;

View File

@ -17,12 +17,9 @@
#import "MPAppViewController.h" #import "MPAppViewController.h"
@implementation MPAppViewController { @implementation MPAppViewController {
} }
- (IBAction)gorillas:(UIButton *)sender { - (IBAction)gorillas:(UIButton *)sender {
MPCheckpoint( MPCheckpointApp, @{ MPCheckpoint( MPCheckpointApp, @{
@ -35,7 +32,7 @@
- (IBAction)deblock:(UIButton *)sender { - (IBAction)deblock:(UIButton *)sender {
MPCheckpoint( MPCheckpointApp, @{ MPCheckpoint( MPCheckpointApp, @{
@"app": @"deblock" @"app" : @"deblock"
} ); } );
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/deblock/id325058485?mt=8"]]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/deblock/id325058485?mt=8"]];

View File

@ -17,10 +17,9 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface MPAppsViewController : UIViewController<UIPageViewControllerDataSource, UIPageViewControllerDelegate>
@interface MPAppsViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate> @property(weak, nonatomic) IBOutlet UIImageView *pagePositionView;
@property (weak, nonatomic) IBOutlet UIImageView *pagePositionView;
- (IBAction)exit; - (IBAction)exit;

View File

@ -17,30 +17,27 @@
#import "MPAppsViewController.h" #import "MPAppsViewController.h"
@interface MPAppsViewController()
@interface MPAppsViewController () @property(nonatomic, strong) NSMutableArray *pageVCs;
@property(nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSMutableArray *pageVCs;
@property (nonatomic, strong) UIPageViewController *pageViewController;
@end @end
@implementation MPAppsViewController { @implementation MPAppsViewController {
} }
@synthesize pagePositionView = _pagePositionView; @synthesize pagePositionView = _pagePositionView;
@synthesize pageVCs = _pageVCs; @synthesize pageVCs = _pageVCs;
@synthesize pageViewController = _pageViewController; @synthesize pageViewController = _pageViewController;
- (void)viewDidLoad { - (void)viewDidLoad {
self.pageVCs = [NSMutableArray array]; self.pageVCs = [NSMutableArray array];
UIViewController *vc; UIViewController *vc;
@try { @try {
for (NSUInteger p = 0; for (NSUInteger p = 0;
(vc = [self.storyboard instantiateViewControllerWithIdentifier:PearlString(@"MPAppViewController_%u", p)]); (vc = [self.storyboard instantiateViewControllerWithIdentifier:PearlString( @"MPAppViewController_%u", p )]);
++p) ++p)
[self.pageVCs addObject:vc]; [self.pageVCs addObject:vc];
} }
@ -49,11 +46,11 @@
[e raise]; [e raise];
} }
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil]; options:nil];
self.pageViewController.dataSource = self; self.pageViewController.dataSource = self;
self.pageViewController.delegate = self; self.pageViewController.delegate = self;
[self addChildViewController:self.pageViewController]; [self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view]; [self.view addSubview:self.pageViewController.view];
self.pageViewController.view.frame = self.pagePositionView.frame; self.pageViewController.view.frame = self.pagePositionView.frame;
@ -68,7 +65,7 @@
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide]; [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
MPCheckpoint( MPCheckpointApps, nil ); MPCheckpoint( MPCheckpointApps, nil );
[self.pageViewController setViewControllers:@[ [self.pageVCs objectAtIndex:1] ] direction:UIPageViewControllerNavigationDirectionForward [self.pageViewController setViewControllers:@[ [self.pageVCs objectAtIndex:1] ] direction:UIPageViewControllerNavigationDirectionForward
animated:NO completion:nil]; animated:NO completion:nil];
[super viewWillAppear:animated]; [super viewWillAppear:animated];
@ -101,7 +98,6 @@
return NO; return NO;
} }
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController { viewControllerBeforeViewController:(UIViewController *)viewController {

View File

@ -20,7 +20,8 @@
#import "MPElementListController.h" #import "MPElementListController.h"
@interface MPElementListAllViewController : MPElementListController @interface MPElementListAllViewController : MPElementListController
@property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
@property(weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
- (IBAction)close:(id)sender; - (IBAction)close:(id)sender;
- (IBAction)add:(id)sender; - (IBAction)add:(id)sender;

View File

@ -83,7 +83,7 @@
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
} }
- (void)performUpgradeAllWithCompletion:(void(^)(BOOL success, NSDictionary *changes))completion { - (void)performUpgradeAllWithCompletion:(void (^)(BOOL success, NSDictionary *changes))completion {
[MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
@ -93,7 +93,7 @@
NSArray *elements = [moc executeFetchRequest:fetchRequest error:&error]; NSArray *elements = [moc executeFetchRequest:fetchRequest error:&error];
if (!elements) { if (!elements) {
err(@"Failed to fetch outdated sites for upgrade: %@", error); err(@"Failed to fetch outdated sites for upgrade: %@", error);
completion(NO, nil); completion( NO, nil );
return; return;
} }
@ -111,7 +111,7 @@
} }
[moc saveToStore]; [moc saveToStore];
completion(YES, elementChanges); completion( YES, elementChanges );
}]; }];
} }
@ -131,14 +131,13 @@
[PearlAlert showAlertWithTitle:@"Sites Upgraded" [PearlAlert showAlertWithTitle:@"Sites Upgraded"
message:PearlString( @"This upgrade has caused %d passwords to change.\n" message:PearlString( @"This upgrade has caused %d passwords to change.\n"
@"To make updating the actual passwords of these accounts easier, " @"To make updating the actual passwords of these accounts easier, "
@"you can email a summary of these changes to yourself.", [changes count]) @"you can email a summary of these changes to yourself.", [changes count] )
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { if (buttonIndex == [alert cancelButtonIndex])
if (buttonIndex == [alert cancelButtonIndex]) return;
return;
[PearlEMail sendEMailTo:nil subject:@"[Master Password] Upgrade Changes" body:formattedChanges]; [PearlEMail sendEMailTo:nil subject:@"[Master Password] Upgrade Changes" body:formattedChanges];
} cancelTitle:@"Don't Email" otherTitles:@"Send Email", nil]; } cancelTitle:@"Don't Email" otherTitles:@"Send Email", nil];
} }
- (NSFetchedResultsController *)fetchedResultsControllerByUses { - (NSFetchedResultsController *)fetchedResultsControllerByUses {

View File

@ -5,14 +5,14 @@
#define MPElementListFilterNone @"MPElementListFilterNone" #define MPElementListFilterNone @"MPElementListFilterNone"
#define MPElementListFilterOutdated @"MPElementListFilterOutdated" #define MPElementListFilterOutdated @"MPElementListFilterOutdated"
@interface MPElementListController : UITableViewController <NSFetchedResultsControllerDelegate> @interface MPElementListController : UITableViewController<NSFetchedResultsControllerDelegate>
@property (weak, nonatomic) IBOutlet id<MPElementListDelegate> delegate; @property(weak, nonatomic) IBOutlet id<MPElementListDelegate> delegate;
@property (strong, nonatomic) NSString *filter; @property(strong, nonatomic) NSString *filter;
@property (readonly) NSFetchedResultsController *fetchedResultsControllerByUses; @property(readonly) NSFetchedResultsController *fetchedResultsControllerByUses;
@property (readonly) NSFetchedResultsController *fetchedResultsControllerByLastUsed; @property(readonly) NSFetchedResultsController *fetchedResultsControllerByLastUsed;
@property (readonly) NSDateFormatter *dateFormatter; @property(readonly) NSDateFormatter *dateFormatter;
- (void)updateData; - (void)updateData;
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion; - (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion;

View File

@ -3,7 +3,7 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
@interface MPElementListController () @interface MPElementListController()
@end @end
@implementation MPElementListController { @implementation MPElementListController {
@ -13,11 +13,11 @@
NSDateFormatter *_dateFormatter; NSDateFormatter *_dateFormatter;
} }
- (void)addElementNamed:(NSString *)siteName completion:(void(^)(BOOL success))completion { - (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion {
if (![siteName length]) { if (![siteName length]) {
if (completion) if (completion)
completion(false); completion( false );
return; return;
} }
@ -41,13 +41,13 @@
[moc saveToStore]; [moc saveToStore];
NSManagedObjectID *elementOID = [element objectID]; NSManagedObjectID *elementOID = [element objectID];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
MPElementEntity *element_ = (MPElementEntity *) [[MPAppDelegate managedObjectContextForThreadIfReady] MPElementEntity *element_ = (MPElementEntity *)[[MPAppDelegate managedObjectContextForThreadIfReady]
objectRegisteredForID:elementOID]; objectRegisteredForID:elementOID];
[self.delegate didSelectElement:element_]; [self.delegate didSelectElement:element_];
if (completion) if (completion)
completion(true); completion( true );
}); } );
}]; }];
} }
@ -59,11 +59,11 @@
if (!moc) if (!moc)
return nil; return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]]; fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO] ];
[self configureFetchRequest:fetchRequest]; [self configureFetchRequest:fetchRequest];
_fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc _fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
sectionNameKeyPath:nil cacheName:nil]; sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsControllerByLastUsed.delegate = self; _fetchedResultsControllerByLastUsed.delegate = self;
} }
@ -119,20 +119,20 @@
return; return;
predicate = [NSCompoundPredicate andPredicateWithSubpredicates: predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[predicate, [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", query]]]; @[ predicate, [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", query] ]];
} }
// Add filter predicate. // Add filter predicate.
if ([self.filter isEqualToString:MPElementListFilterOutdated]) if ([self.filter isEqualToString:MPElementListFilterOutdated])
predicate = [NSCompoundPredicate andPredicateWithSubpredicates: predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[[NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate]]; @[ [NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate ]];
// Fetch // Fetch
NSError *error; NSError *error;
self.fetchedResultsControllerByLastUsed.fetchRequest.predicate = predicate; self.fetchedResultsControllerByLastUsed.fetchRequest.predicate = predicate;
self.fetchedResultsControllerByUses.fetchRequest.predicate = predicate; self.fetchedResultsControllerByUses.fetchRequest.predicate = predicate;
if (![self.fetchedResultsControllerByLastUsed performFetch:&error]) if (![self.fetchedResultsControllerByLastUsed performFetch:&error])
err(@"Couldn't fetch elements: %@", error); err(@"Couldn't fetch elements: %@", error);
if (![self.fetchedResultsControllerByUses performFetch:&error]) if (![self.fetchedResultsControllerByUses performFetch:&error])
err(@"Couldn't fetch elements: %@", error); err(@"Couldn't fetch elements: %@", error);
@ -140,13 +140,12 @@
} }
- (void)customTableViewUpdates { - (void)customTableViewUpdates {
} }
// See MP-14, also crashes easily on internal assertions etc.. // See MP-14, also crashes easily on internal assertions etc..
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
dbg(@"%@", NSStringFromSelector(_cmd)); dbg(@"%@", NSStringFromSelector( _cmd ));
[self.tableView beginUpdates]; [self.tableView beginUpdates];
} }
@ -156,25 +155,25 @@
switch (type) { switch (type) {
case NSFetchedResultsChangeInsert: case NSFetchedResultsChangeInsert:
dbg(@"%@ -- NSFetchedResultsChangeInsert:%@", NSStringFromSelector(_cmd), anObject); dbg(@"%@ -- NSFetchedResultsChangeInsert:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ] [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic]; withRowAnimation:UITableViewRowAnimationAutomatic];
break; break;
case NSFetchedResultsChangeDelete: case NSFetchedResultsChangeDelete:
dbg(@"%@ -- NSFetchedResultsChangeDelete:%@", NSStringFromSelector(_cmd), anObject); dbg(@"%@ -- NSFetchedResultsChangeDelete:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ] [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic]; withRowAnimation:UITableViewRowAnimationAutomatic];
break; break;
case NSFetchedResultsChangeUpdate: case NSFetchedResultsChangeUpdate:
dbg(@"%@ -- NSFetchedResultsChangeUpdate:%@", NSStringFromSelector(_cmd), anObject); dbg(@"%@ -- NSFetchedResultsChangeUpdate:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ] [self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic]; withRowAnimation:UITableViewRowAnimationAutomatic];
break; break;
case NSFetchedResultsChangeMove: case NSFetchedResultsChangeMove:
dbg(@"%@ -- NSFetchedResultsChangeMove:%@", NSStringFromSelector(_cmd), anObject); dbg(@"%@ -- NSFetchedResultsChangeMove:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ] [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic]; withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ] [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
@ -185,7 +184,7 @@
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
dbg(@"%@ on %@", NSStringFromSelector(_cmd), [NSThread currentThread].name); dbg(@"%@ on %@", NSStringFromSelector( _cmd ), [NSThread currentThread].name);
[self customTableViewUpdates]; [self customTableViewUpdates];
[self.tableView endUpdates]; [self.tableView endUpdates];
} }
@ -210,7 +209,7 @@
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementListCell"]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementListCell"];
if (!cell) if (!cell)
cell = (UITableViewCell *) [[UIViewController alloc] initWithNibName:@"MPElementListCellView" bundle:nil].view; cell = (UITableViewCell *)[[UIViewController alloc] initWithNibName:@"MPElementListCellView" bundle:nil].view;
[self configureCell:cell inTableView:tableView atTableIndexPath:indexPath]; [self configureCell:cell inTableView:tableView atTableIndexPath:indexPath];
@ -222,11 +221,12 @@
MPElementEntity *element = [self elementForTableIndexPath:indexPath]; MPElementEntity *element = [self elementForTableIndexPath:indexPath];
cell.textLabel.text = element.name; cell.textLabel.text = element.name;
cell.detailTextLabel.text = PearlString(@"%d views, last on %@: %@", cell.detailTextLabel.text = PearlString( @"%d views, last on %@: %@",
element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]); element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type] );
} }
- (NSIndexPath *)tableIndexPathForFetchController:(NSFetchedResultsController *)fetchedResultsController indexPath:(NSIndexPath *)indexPath { - (NSIndexPath *)tableIndexPathForFetchController:(NSFetchedResultsController *)fetchedResultsController
indexPath:(NSIndexPath *)indexPath {
if (fetchedResultsController == self.fetchedResultsControllerByLastUsed) if (fetchedResultsController == self.fetchedResultsControllerByLastUsed)
return [NSIndexPath indexPathForRow:indexPath.row inSection:0]; return [NSIndexPath indexPathForRow:indexPath.row inSection:0];
@ -270,7 +270,7 @@
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return @[@"recency", @"uses"]; return @[ @"recency", @"uses" ];
} }
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
@ -292,7 +292,7 @@ forRowAtIndexPath:(NSIndexPath *)indexPath {
MPCheckpoint( MPCheckpointDeleteElement, @{ MPCheckpoint( MPCheckpointDeleteElement, @{
@"type" : element.typeName, @"type" : element.typeName,
@"version" : @(element.version) @"version" : @(element.version)
}); } );
}]; }];
} }
} }

View File

@ -17,7 +17,7 @@
#import "MPElementEntity.h" #import "MPElementEntity.h"
@protocol MPElementListDelegate <NSObject> @protocol MPElementListDelegate<NSObject>
- (void)didSelectElement:(MPElementEntity *)element; - (void)didSelectElement:(MPElementEntity *)element;

View File

@ -11,9 +11,9 @@
@interface MPElementListSearchController : MPElementListController<UISearchBarDelegate, UISearchDisplayDelegate> @interface MPElementListSearchController : MPElementListController<UISearchBarDelegate, UISearchDisplayDelegate>
@property (strong, nonatomic) UILabel *tipView; @property(strong, nonatomic) UILabel *tipView;
@property (strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController; @property(strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer; @property(weak, nonatomic) IBOutlet UIView *searchTipContainer;
@end @end

View File

@ -10,13 +10,14 @@
#import "MPMainViewController.h" #import "MPMainViewController.h"
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
@interface MPElementListSearchController () @interface MPElementListSearchController()
@property (nonatomic) BOOL newSiteSectionWasNeeded; @property(nonatomic) BOOL newSiteSectionWasNeeded;
@end @end
@implementation MPElementListSearchController @implementation MPElementListSearchController
@synthesize searchDisplayController; @synthesize searchDisplayController;
- (id)init { - (id)init {
@ -24,22 +25,22 @@
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
self.tipView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 170)]; self.tipView = [[UILabel alloc] initWithFrame:CGRectMake( 0, 0, 320, 170 )];
self.tipView.textAlignment = NSTextAlignmentCenter; self.tipView.textAlignment = NSTextAlignmentCenter;
self.tipView.backgroundColor = [UIColor clearColor]; self.tipView.backgroundColor = [UIColor clearColor];
self.tipView.textColor = [UIColor lightTextColor]; self.tipView.textColor = [UIColor lightTextColor];
self.tipView.shadowColor = [UIColor blackColor]; self.tipView.shadowColor = [UIColor blackColor];
self.tipView.shadowOffset = CGSizeMake(0, -1); self.tipView.shadowOffset = CGSizeMake( 0, -1 );
self.tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight self.tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
| UIViewAutoresizingFlexibleBottomMargin; | UIViewAutoresizingFlexibleBottomMargin;
self.tipView.numberOfLines = 0; self.tipView.numberOfLines = 0;
self.tipView.font = [UIFont systemFontOfSize:14]; self.tipView.font = [UIFont systemFontOfSize:14];
self.tipView.text = self.tipView.text =
@"Tip:\n" @"Tip:\n"
@"Name your sites by their domain name:\n" @"Name your sites by their domain name:\n"
@"apple.com, twitter.com\n\n" @"apple.com, twitter.com\n\n"
@"For email accounts, use the address:\n" @"For email accounts, use the address:\n"
@"john@apple.com, john@gmail.com"; @"john@apple.com, john@gmail.com";
return self; return self;
} }
@ -69,7 +70,7 @@
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller { - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
controller.searchBar.text = @""; controller.searchBar.text = @"";
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.searchTipContainer.alpha = 0; self.searchTipContainer.alpha = 0;
@ -83,15 +84,15 @@
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller { - (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
controller.searchBar.prompt = nil; controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO; controller.searchBar.searchResultsButtonSelected = NO;
} }
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView { - (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
tableView.backgroundColor = [UIColor blackColor]; tableView.backgroundColor = [UIColor blackColor];
tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.rowHeight = 48.0f; tableView.rowHeight = 48.0f;
self.tableView = tableView; self.tableView = tableView;
} }
@ -111,13 +112,13 @@
[searchBar.superview enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { [searchBar.superview enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if ([subview isKindOfClass:[UIControl class]] && if ([subview isKindOfClass:[UIControl class]] &&
CGPointEqualToPoint( CGPointEqualToPoint(
CGPointDistanceBetweenCGPoints(searchBarFrame.origin, subview.frame.origin), CGPointDistanceBetweenCGPoints( searchBarFrame.origin, subview.frame.origin ),
CGPointMake(0, searchBarFrame.size.height))) { CGPointMake( 0, searchBarFrame.size.height ) )) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[self.tipView removeFromSuperview]; [self.tipView removeFromSuperview];
[subview addSubview:self.tipView]; [subview addSubview:self.tipView];
}); } );
*stop = YES; *stop = YES;
} }
@ -131,7 +132,7 @@
return NO; return NO;
__block BOOL hasExactQueryMatch = NO; __block BOOL hasExactQueryMatch = NO;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsControllerByUses sections] lastObject]; id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsControllerByUses sections] lastObject];
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) { [[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) { if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES; hasExactQueryMatch = YES;
@ -178,7 +179,7 @@
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section < [super numberOfSectionsInTableView:tableView]) if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections. // Section is one of super's sections.
return [super tableView:tableView numberOfRowsInSection:section]; return [super tableView:tableView numberOfRowsInSection:section];
return 1; return 1;
@ -195,8 +196,8 @@
// "New" section // "New" section
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
cell.textLabel.text = query; cell.textLabel.text = query;
cell.detailTextLabel.text = PearlString(@"New site: %@", cell.detailTextLabel.text = PearlString( @"New site: %@",
[MPAlgorithmDefault shortNameOfType:[[[MPAppDelegate get] activeUserForThread] defaultType]]); [MPAlgorithmDefault shortNameOfType:[[[MPAppDelegate get] activeUserForThread] defaultType]] );
} }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
@ -208,10 +209,11 @@
} }
// "New" section. // "New" section.
NSString *siteName = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *siteName
= [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[PearlAlert showAlertWithTitle:@"New Site" [PearlAlert showAlertWithTitle:@"New Site"
message:PearlString(@"Do you want to create a new site named:\n%@", siteName) message:PearlString( @"Do you want to create a new site named:\n%@", siteName )
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
@ -226,7 +228,7 @@
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section < [super numberOfSectionsInTableView:tableView]) if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections. // Section is one of super's sections.
return [super tableView:tableView titleForHeaderInSection:section]; return [super tableView:tableView titleForHeaderInSection:section];
return @"Create"; return @"Create";
@ -236,9 +238,8 @@
forRowAtIndexPath:(NSIndexPath *)indexPath { forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section < [super numberOfSectionsInTableView:tableView]) if (indexPath.section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections. // Section is one of super's sections.
[super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath]; [super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
} }
@end @end

View File

@ -8,24 +8,24 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface MPGuideViewController : UIViewController <UIScrollViewDelegate> @interface MPGuideViewController : UIViewController<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIView *siteNameTip; @property(weak, nonatomic) IBOutlet UIView *siteNameTip;
@property (weak, nonatomic) IBOutlet UIView *contentTip; @property(weak, nonatomic) IBOutlet UIView *contentTip;
@property (weak, nonatomic) IBOutlet UILabel *contentTipText; @property(weak, nonatomic) IBOutlet UILabel *contentTipText;
@property (weak, nonatomic) IBOutlet UIButton *usernameButton; @property(weak, nonatomic) IBOutlet UIButton *usernameButton;
@property (weak, nonatomic) IBOutlet UIView *usernameTip; @property(weak, nonatomic) IBOutlet UIView *usernameTip;
@property (weak, nonatomic) IBOutlet UIButton *typeButton; @property(weak, nonatomic) IBOutlet UIButton *typeButton;
@property (weak, nonatomic) IBOutlet UIView *typeTip; @property(weak, nonatomic) IBOutlet UIView *typeTip;
@property (weak, nonatomic) IBOutlet UIButton *toolButton; @property(weak, nonatomic) IBOutlet UIButton *toolButton;
@property (weak, nonatomic) IBOutlet UIView *toolTip; @property(weak, nonatomic) IBOutlet UIView *toolTip;
@property (weak, nonatomic) IBOutlet UIProgressView *progress; @property(weak, nonatomic) IBOutlet UIProgressView *progress;
@property (weak, nonatomic) IBOutlet UIView *content; @property(weak, nonatomic) IBOutlet UIView *content;
@property (weak, nonatomic) IBOutlet UIButton *contentButton; @property(weak, nonatomic) IBOutlet UIButton *contentButton;
@property (weak, nonatomic) IBOutlet UITextField *contentText; @property(weak, nonatomic) IBOutlet UITextField *contentText;
@property (weak, nonatomic) IBOutlet UIButton *volumeButton; @property(weak, nonatomic) IBOutlet UIButton *volumeButton;
@property (weak, nonatomic) IBOutlet UIButton *largePlayButton; @property(weak, nonatomic) IBOutlet UIButton *largePlayButton;
@property (weak, nonatomic) IBOutlet UIButton *smallPlayButton; @property(weak, nonatomic) IBOutlet UIButton *smallPlayButton;
- (IBAction)play; - (IBAction)play;
- (IBAction)close; - (IBAction)close;

View File

@ -12,46 +12,45 @@
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPElementListDelegate, UIWebViewDelegate, UIGestureRecognizerDelegate> @interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPElementListDelegate, UIWebViewDelegate, UIGestureRecognizerDelegate>
@property (assign, nonatomic) BOOL siteInfoHidden; @property(assign, nonatomic) BOOL siteInfoHidden;
@property (strong, nonatomic) IBOutlet MPElementListSearchController *searchDelegate; @property(strong, nonatomic) IBOutlet MPElementListSearchController *searchDelegate;
@property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullDownGesture; @property(strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullDownGesture;
@property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullUpGesture; @property(strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullUpGesture;
@property (weak, nonatomic) IBOutlet UITextField *contentField; @property(weak, nonatomic) IBOutlet UITextField *contentField;
@property (weak, nonatomic) IBOutlet UIButton *typeButton; @property(weak, nonatomic) IBOutlet UIButton *typeButton;
@property (weak, nonatomic) IBOutlet UIWebView *helpView; @property(weak, nonatomic) IBOutlet UIWebView *helpView;
@property (weak, nonatomic) IBOutlet UILabel *siteName; @property(weak, nonatomic) IBOutlet UILabel *siteName;
@property (weak, nonatomic) IBOutlet UILabel *passwordCounter; @property(weak, nonatomic) IBOutlet UILabel *passwordCounter;
@property (weak, nonatomic) IBOutlet UIButton *passwordIncrementer; @property(weak, nonatomic) IBOutlet UIButton *passwordIncrementer;
@property (weak, nonatomic) IBOutlet UIButton *passwordEdit; @property(weak, nonatomic) IBOutlet UIButton *passwordEdit;
@property (weak, nonatomic) IBOutlet UIButton *passwordUpgrade; @property(weak, nonatomic) IBOutlet UIButton *passwordUpgrade;
@property (weak, nonatomic) IBOutlet UIView *contentContainer; @property(weak, nonatomic) IBOutlet UIView *contentContainer;
@property (weak, nonatomic) IBOutlet UIView *displayContainer; @property(weak, nonatomic) IBOutlet UIView *displayContainer;
@property (weak, nonatomic) IBOutlet UIView *helpContainer; @property(weak, nonatomic) IBOutlet UIView *helpContainer;
@property (weak, nonatomic) IBOutlet UIView *contentTipContainer; @property(weak, nonatomic) IBOutlet UIView *contentTipContainer;
@property (weak, nonatomic) IBOutlet UIView *loginNameTipContainer; @property(weak, nonatomic) IBOutlet UIView *loginNameTipContainer;
@property (weak, nonatomic) IBOutlet UIView *alertContainer; @property(weak, nonatomic) IBOutlet UIView *alertContainer;
@property (weak, nonatomic) IBOutlet UILabel *alertTitle; @property(weak, nonatomic) IBOutlet UILabel *alertTitle;
@property (weak, nonatomic) IBOutlet UITextView *alertBody; @property(weak, nonatomic) IBOutlet UITextView *alertBody;
@property (weak, nonatomic) IBOutlet UILabel *contentTipBody; @property(weak, nonatomic) IBOutlet UILabel *contentTipBody;
@property (weak, nonatomic) IBOutlet UILabel *loginNameTipBody; @property(weak, nonatomic) IBOutlet UILabel *loginNameTipBody;
@property (weak, nonatomic) IBOutlet UIImageView *toolTipEditIcon; @property(weak, nonatomic) IBOutlet UIImageView *toolTipEditIcon;
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer; @property(weak, nonatomic) IBOutlet UIView *searchTipContainer;
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer; @property(weak, nonatomic) IBOutlet UIView *actionsTipContainer;
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer; @property(weak, nonatomic) IBOutlet UIView *typeTipContainer;
@property (weak, nonatomic) IBOutlet UIView *toolTipContainer; @property(weak, nonatomic) IBOutlet UIView *toolTipContainer;
@property (weak, nonatomic) IBOutlet UILabel *toolTipBody; @property(weak, nonatomic) IBOutlet UILabel *toolTipBody;
@property (weak, nonatomic) IBOutlet UIView *loginNameContainer; @property(weak, nonatomic) IBOutlet UIView *loginNameContainer;
@property (weak, nonatomic) IBOutlet UITextField *loginNameField; @property(weak, nonatomic) IBOutlet UITextField *loginNameField;
@property (weak, nonatomic) IBOutlet UIButton *passwordUser; @property(weak, nonatomic) IBOutlet UIButton *passwordUser;
@property (weak, nonatomic) IBOutlet UIView *outdatedAlertContainer; @property(weak, nonatomic) IBOutlet UIView *outdatedAlertContainer;
@property (weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack; @property(weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack;
@property (weak, nonatomic) IBOutlet UIButton *outdatedAlertCloseButton; @property(weak, nonatomic) IBOutlet UIButton *outdatedAlertCloseButton;
@property (weak, nonatomic) IBOutlet UIImageView *pullUpView; @property(weak, nonatomic) IBOutlet UIImageView *pullUpView;
@property (weak, nonatomic) IBOutlet UIImageView *pullDownView; @property(weak, nonatomic) IBOutlet UIImageView *pullDownView;
@property(copy, nonatomic) void (^contentTipCleanup)(BOOL finished);
@property (copy, nonatomic) void (^contentTipCleanup)(BOOL finished); @property(copy, nonatomic) void (^toolTipCleanup)(BOOL finished);
@property (copy, nonatomic) void (^toolTipCleanup)(BOOL finished);
- (IBAction)copyContent; - (IBAction)copyContent;
- (IBAction)incrementPasswordCounter; - (IBAction)incrementPasswordCounter;

View File

@ -11,9 +11,9 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPElementListAllViewController.h" #import "MPElementListAllViewController.h"
@interface MPMainViewController() @interface MPMainViewController()
@property (nonatomic)BOOL suppressOutdatedAlert;
@property(nonatomic) BOOL suppressOutdatedAlert;
@end @end
@implementation MPMainViewController { @implementation MPMainViewController {
@ -43,7 +43,6 @@
[self updateUserHiddenAnimated:NO]; [self updateUserHiddenAnimated:NO];
} }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"MP_ChooseType"]) if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
@ -56,13 +55,13 @@
- (void)viewDidLoad { - (void)viewDidLoad {
self.searchDelegate = [MPElementListSearchController new]; self.searchDelegate = [MPElementListSearchController new];
self.searchDelegate.delegate = self; self.searchDelegate.delegate = self;
self.searchDelegate.searchDisplayController = self.searchDisplayController; self.searchDelegate.searchDisplayController = self.searchDisplayController;
self.searchDelegate.searchTipContainer = self.searchTipContainer; self.searchDelegate.searchTipContainer = self.searchTipContainer;
self.searchDisplayController.searchBar.delegate = self.searchDelegate; self.searchDisplayController.searchBar.delegate = self.searchDelegate;
self.searchDisplayController.delegate = self.searchDelegate; self.searchDisplayController.delegate = self.searchDelegate;
self.searchDisplayController.searchResultsDelegate = self.searchDelegate; self.searchDisplayController.searchResultsDelegate = self.searchDelegate;
self.searchDisplayController.searchResultsDataSource = self.searchDelegate; self.searchDisplayController.searchResultsDataSource = self.searchDelegate;
[self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self [self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
@ -75,21 +74,21 @@
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]]; self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
self.alertBody.text = nil; self.alertBody.text = nil;
self.toolTipEditIcon.hidden = YES; self.toolTipEditIcon.hidden = YES;
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:^(NSNotification *note) {
self.suppressOutdatedAlert = NO; self.suppressOutdatedAlert = NO;
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil queue:nil usingBlock:
^void(NSNotification *note) { ^void(NSNotification *note) {
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
if (activeElement.type & MPElementTypeClassStored && ![[activeElement.content description] length]) if (activeElement.type & MPElementTypeClassStored && ![[activeElement.content description] length])
[self showToolTip:@"Tap to set a password." withIcon:self.toolTipEditIcon]; [self showToolTip:@"Tap to set a password." withIcon:self.toolTipEditIcon];
if (activeElement.requiresExplicitMigration) if (activeElement.requiresExplicitMigration)
[self showToolTip:@"Password outdated. Tap to upgrade it." withIcon:nil]; [self showToolTip:@"Password outdated. Tap to upgrade it." withIcon:nil];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:
^(NSNotification *note) { ^(NSNotification *note) {
BOOL animated = [[note.userInfo objectForKey:@"animated"] boolValue]; BOOL animated = [[note.userInfo objectForKey:@"animated"] boolValue];
@ -119,14 +118,14 @@
_activeElementOID = nil; _activeElementOID = nil;
self.searchDisplayController.searchBar.text = nil; self.searchDisplayController.searchBar.text = nil;
self.alertContainer.hidden = NO; self.alertContainer.hidden = NO;
self.outdatedAlertContainer.hidden = NO; self.outdatedAlertContainer.hidden = NO;
self.searchTipContainer.hidden = NO; self.searchTipContainer.hidden = NO;
self.actionsTipContainer.hidden = NO; self.actionsTipContainer.hidden = NO;
self.typeTipContainer.hidden = NO; self.typeTipContainer.hidden = NO;
self.toolTipContainer.hidden = NO; self.toolTipContainer.hidden = NO;
self.contentTipContainer.hidden = NO; self.contentTipContainer.hidden = NO;
self.loginNameTipContainer.hidden = NO; self.loginNameTipContainer.hidden = NO;
[self updateAnimated:NO]; [self updateAnimated:NO];
@ -142,7 +141,7 @@
// Needed for when we appear after a modal VC dismisses: // Needed for when we appear after a modal VC dismisses:
// We can't present until the other modal VC has been fully dismissed and presenting in -viewWillAppear: will fail. // We can't present until the other modal VC has been fully dismissed and presenting in -viewWillAppear: will fail.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread]; MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
if ([MPAlgorithmDefault migrateUser:activeUser] && !self.suppressOutdatedAlert) if ([MPAlgorithmDefault migrateUser:activeUser] && !self.suppressOutdatedAlert)
[UIView animateWithDuration:0.3f animations:^{ [UIView animateWithDuration:0.3f animations:^{
@ -150,7 +149,7 @@
self.suppressOutdatedAlert = YES; self.suppressOutdatedAlert = YES;
}]; }];
[activeUser.managedObjectContext saveToStore]; [activeUser.managedObjectContext saveToStore];
}); } );
if (![[MPiOSConfig get].actionsTipShown boolValue]) if (![[MPiOSConfig get].actionsTipShown boolValue])
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{ [UIView animateWithDuration:animated? 0.3f: 0 animations:^{
@ -161,7 +160,7 @@
[MPiOSConfig get].actionsTipShown = @YES; [MPiOSConfig get].actionsTipShown = @YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.actionsTipContainer.alpha = 0; self.actionsTipContainer.alpha = 0;
} completion:^(BOOL finished_) { } completion:^(BOOL finished_) {
@ -170,7 +169,7 @@
self.searchTipContainer.alpha = 1; self.searchTipContainer.alpha = 1;
}]; }];
}]; }];
}); } );
}]; }];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Main"]; [[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Main"];
@ -197,15 +196,15 @@
[self setHelpChapter:activeElement? @"2": @"1"]; [self setHelpChapter:activeElement? @"2": @"1"];
[self updateHelpHiddenAnimated:NO]; [self updateHelpHiddenAnimated:NO];
self.passwordCounter.alpha = 0; self.passwordCounter.alpha = 0;
self.passwordIncrementer.alpha = 0; self.passwordIncrementer.alpha = 0;
self.passwordEdit.alpha = 0; self.passwordEdit.alpha = 0;
self.passwordUpgrade.alpha = 0; self.passwordUpgrade.alpha = 0;
self.passwordUser.alpha = 0; self.passwordUser.alpha = 0;
self.displayContainer.alpha = 0; self.displayContainer.alpha = 0;
if (activeElement) { if (activeElement) {
self.passwordUser.alpha = 0.5f; self.passwordUser.alpha = 0.5f;
self.displayContainer.alpha = 1.0f; self.displayContainer.alpha = 1.0f;
} }
@ -214,11 +213,11 @@
else { else {
if (activeElement.type & MPElementTypeClassGenerated) { if (activeElement.type & MPElementTypeClassGenerated) {
self.passwordCounter.alpha = 0.5f; self.passwordCounter.alpha = 0.5f;
self.passwordIncrementer.alpha = 0.5f; self.passwordIncrementer.alpha = 0.5f;
} else }
if (activeElement.type & MPElementTypeClassStored) else if (activeElement.type & MPElementTypeClassStored)
self.passwordEdit.alpha = 0.5f; self.passwordEdit.alpha = 0.5f;
} }
self.siteName.text = activeElement.name; self.siteName.text = activeElement.name;
@ -228,21 +227,21 @@
forState:UIControlStateNormal]; forState:UIControlStateNormal];
if ([activeElement isKindOfClass:[MPElementGeneratedEntity class]]) if ([activeElement isKindOfClass:[MPElementGeneratedEntity class]])
self.passwordCounter.text = PearlString(@"%u", ((MPElementGeneratedEntity *)activeElement).counter); self.passwordCounter.text = PearlString( @"%u", ((MPElementGeneratedEntity *)activeElement).counter );
self.contentField.enabled = NO; self.contentField.enabled = NO;
self.contentField.text = @""; self.contentField.text = @"";
if (activeElement.name && ![activeElement isDeleted]) if (activeElement.name && ![activeElement isDeleted])
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
NSString *description = [activeElement.content description]; NSString *description = [activeElement.content description];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
self.contentField.text = description; self.contentField.text = description;
}); } );
}); } );
self.loginNameField.enabled = NO; self.loginNameField.enabled = NO;
self.loginNameField.text = activeElement.loginName; self.loginNameField.text = activeElement.loginName;
self.siteInfoHidden = !activeElement || ([[MPiOSConfig get].siteInfoHidden boolValue] && (activeElement.loginName == nil)); self.siteInfoHidden = !activeElement || ([[MPiOSConfig get].siteInfoHidden boolValue] && (activeElement.loginName == nil));
[self updateUserHiddenAnimated:NO]; [self updateUserHiddenAnimated:NO];
} }
@ -271,10 +270,11 @@
self.pullDownView.hidden = [[MPiOSConfig get].helpHidden boolValue]; self.pullDownView.hidden = [[MPiOSConfig get].helpHidden boolValue];
if ([[MPiOSConfig get].helpHidden boolValue]) { if ([[MPiOSConfig get].helpHidden boolValue]) {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, self.view.bounds.size.height - 44 /* search bar */); self.contentContainer.frame = CGRectSetHeight( self.contentContainer.frame, self.view.bounds.size.height - 44 /* search bar */);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height - 20 /* pull-up */); self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, self.view.bounds.size.height - 20 /* pull-up */);
} else { }
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 225); else {
self.contentContainer.frame = CGRectSetHeight( self.contentContainer.frame, 225 );
[self.helpContainer setFrameFromCurrentSizeAndParentPaddingTop:CGFLOAT_MAX right:0 bottom:0 left:0]; [self.helpContainer setFrameFromCurrentSizeAndParentPaddingTop:CGFLOAT_MAX right:0 bottom:0 left:0];
} }
} }
@ -287,7 +287,7 @@
- (void)toggleUserAnimated:(BOOL)animated { - (void)toggleUserAnimated:(BOOL)animated {
[MPiOSConfig get].siteInfoHidden = PearlBool(!self.siteInfoHidden); [MPiOSConfig get].siteInfoHidden = PearlBool(!self.siteInfoHidden);
self.siteInfoHidden = [[MPiOSConfig get].siteInfoHidden boolValue]; self.siteInfoHidden = [[MPiOSConfig get].siteInfoHidden boolValue];
[self updateUserHiddenAnimated:animated]; [self updateUserHiddenAnimated:animated];
} }
@ -301,11 +301,11 @@
} }
if (self.siteInfoHidden) { if (self.siteInfoHidden) {
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 87); self.displayContainer.frame = CGRectSetHeight( self.displayContainer.frame, 87 );
} else { }
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 137); else {
self.displayContainer.frame = CGRectSetHeight( self.displayContainer.frame, 137 );
} }
} }
- (void)setHelpChapter:(NSString *)chapter { - (void)setHelpChapter:(NSString *)chapter {
@ -314,11 +314,11 @@
@"chapter" : chapter @"chapter" : chapter
} ); } );
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
NSURL *url = [NSURL URLWithString:[@"#" stringByAppendingString:chapter] NSURL *url = [NSURL URLWithString:[@"#" stringByAppendingString:chapter]
relativeToURL:[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]; relativeToURL:[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]];
[self.helpView loadRequest:[NSURLRequest requestWithURL:url]]; [self.helpView loadRequest:[NSURLRequest requestWithURL:url]];
}); } );
} }
- (IBAction)panHelpDown:(UIPanGestureRecognizer *)sender { - (IBAction)panHelpDown:(UIPanGestureRecognizer *)sender {
@ -330,7 +330,7 @@
targetY = 246; targetY = 246;
} }
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, targetY); self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, targetY );
if (sender.state == UIGestureRecognizerStateEnded) if (sender.state == UIGestureRecognizerStateEnded)
[self setHelpHidden:hideHelp animated:YES]; [self setHelpHidden:hideHelp animated:YES];
@ -342,10 +342,10 @@
BOOL hideHelp = NO; BOOL hideHelp = NO;
if (targetY >= self.view.bounds.size.height - 20) { if (targetY >= self.view.bounds.size.height - 20) {
hideHelp = YES; hideHelp = YES;
targetY = self.view.bounds.size.height - 20 ; targetY = self.view.bounds.size.height - 20;
} }
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, targetY); self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, targetY );
if (sender.state == UIGestureRecognizerStateEnded) if (sender.state == UIGestureRecognizerStateEnded)
[self setHelpHidden:hideHelp animated:YES]; [self setHelpHidden:hideHelp animated:YES];
@ -355,21 +355,21 @@
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString: NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString:
PearlString(@"setClass('%@');", activeElement.typeClassName)]; PearlString( @"setClass('%@');", activeElement.typeClassName )];
if (error.length) if (error.length)
err(@"helpView.setClass: %@", error); err(@"helpView.setClass: %@", error);
} }
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon { - (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if (self.contentTipCleanup) if (self.contentTipCleanup)
self.contentTipCleanup(NO); self.contentTipCleanup( NO );
__weak MPMainViewController *wSelf = self; __weak MPMainViewController *wSelf = self;
self.contentTipBody.text = message; self.contentTipBody.text = message;
self.contentTipCleanup = ^(BOOL finished) { self.contentTipCleanup = ^(BOOL finished) {
icon.hidden = YES; icon.hidden = YES;
wSelf.contentTipCleanup = nil; wSelf.contentTipCleanup = nil;
}; };
@ -378,47 +378,47 @@
self.contentTipContainer.alpha = 1; self.contentTipContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC); dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC );
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { dispatch_after( popTime, dispatch_get_main_queue(), ^(void) {
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.contentTipContainer.alpha = 0; self.contentTipContainer.alpha = 0;
} completion:self.contentTipCleanup]; } completion:self.contentTipCleanup];
}); } );
} }
}]; }];
}); } );
} }
- (void)showLoginNameTip:(NSString *)message { - (void)showLoginNameTip:(NSString *)message {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
self.loginNameTipBody.text = message; self.loginNameTipBody.text = message;
[UIView animateWithDuration:0.3f animations:^{ [UIView animateWithDuration:0.3f animations:^{
self.loginNameTipContainer.alpha = 1; self.loginNameTipContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC); dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC );
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { dispatch_after( popTime, dispatch_get_main_queue(), ^(void) {
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.loginNameTipContainer.alpha = 0; self.loginNameTipContainer.alpha = 0;
}]; }];
}); } );
} }
}]; }];
}); } );
} }
- (void)showToolTip:(NSString *)message withIcon:(UIImageView *)icon { - (void)showToolTip:(NSString *)message withIcon:(UIImageView *)icon {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if (self.toolTipCleanup) if (self.toolTipCleanup)
self.toolTipCleanup(NO); self.toolTipCleanup( NO );
__weak MPMainViewController *wSelf = self; __weak MPMainViewController *wSelf = self;
self.toolTipBody.text = message; self.toolTipBody.text = message;
self.toolTipCleanup = ^(BOOL finished) { self.toolTipCleanup = ^(BOOL finished) {
icon.hidden = YES; icon.hidden = YES;
wSelf.toolTipCleanup = nil; wSelf.toolTipCleanup = nil;
}; };
@ -427,22 +427,22 @@
self.toolTipContainer.alpha = 1; self.toolTipContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC); dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC );
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { dispatch_after( popTime, dispatch_get_main_queue(), ^(void) {
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.toolTipContainer.alpha = 0; self.toolTipContainer.alpha = 0;
} completion:self.toolTipCleanup]; } completion:self.toolTipCleanup];
}); } );
} }
}]; }];
}); } );
} }
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
self.alertTitle.text = title; self.alertTitle.text = title;
NSRange scrollRange = NSMakeRange(self.alertBody.text.length, message.length); NSRange scrollRange = NSMakeRange( self.alertBody.text.length, message.length );
if ([self.alertBody.text length]) if ([self.alertBody.text length])
self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message]; self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message];
else else
@ -452,7 +452,7 @@
[UIView animateWithDuration:0.3f animations:^{ [UIView animateWithDuration:0.3f animations:^{
self.alertContainer.alpha = 1; self.alertContainer.alpha = 1;
}]; }];
}); } );
} }
#pragma mark - Protocols #pragma mark - Protocols
@ -462,7 +462,7 @@
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
id content = activeElement.content; id content = activeElement.content;
if (!content) if (!content)
// Nothing to copy. // Nothing to copy.
return; return;
inf(@"Copying password for: %@", activeElement.name); inf(@"Copying password for: %@", activeElement.name);
@ -497,65 +497,65 @@
- (IBAction)incrementPasswordCounter { - (IBAction)incrementPasswordCounter {
[self changeActiveElementWithWarning: [self changeActiveElementWithWarning:
@"You are incrementing the site's password counter.\n\n" @"You are incrementing the site's password counter.\n\n"
@"If you continue, a new password will be generated for this site. " @"If you continue, a new password will be generated for this site. "
@"You will then need to update your account's old password to this newly generated password.\n\n" @"You will then need to update your account's old password to this newly generated password.\n\n"
@"You can reset the counter by holding down on this button." @"You can reset the counter by holding down on this button."
do:^BOOL(MPElementEntity *activeElement) { do:^BOOL(MPElementEntity *activeElement) {
if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) { if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) {
// Not of a type that supports a password counter. // Not of a type that supports a password counter.
err(@"Cannot increment password counter: Element is not generated: %@", activeElement.name); err(@"Cannot increment password counter: Element is not generated: %@", activeElement.name);
return NO; return NO;
} }
MPElementGeneratedEntity *activeGeneratedElement = (MPElementGeneratedEntity *)activeElement; MPElementGeneratedEntity *activeGeneratedElement = (MPElementGeneratedEntity *)activeElement;
inf(@"Incrementing password counter for: %@", activeGeneratedElement.name); inf(@"Incrementing password counter for: %@", activeGeneratedElement.name);
++activeGeneratedElement.counter; ++activeGeneratedElement.counter;
MPCheckpoint( MPCheckpointIncrementPasswordCounter, @{ MPCheckpoint( MPCheckpointIncrementPasswordCounter, @{
@"type" : activeGeneratedElement.typeName, @"type" : activeGeneratedElement.typeName,
@"version" : @(activeGeneratedElement.version), @"version" : @(activeGeneratedElement.version),
@"counter" : @(activeGeneratedElement.counter) @"counter" : @(activeGeneratedElement.counter)
} ); } );
return YES; return YES;
}]; }];
} }
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender { - (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateBegan) if (sender.state != UIGestureRecognizerStateBegan)
// Only fire when the gesture was first detected. // Only fire when the gesture was first detected.
return; return;
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) { if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) {
// Not of a type that supports a password counter. // Not of a type that supports a password counter.
err(@"Cannot reset password counter: Element is not generated: %@", activeElement.name); err(@"Cannot reset password counter: Element is not generated: %@", activeElement.name);
return; return;
} else }
if (((MPElementGeneratedEntity *)activeElement).counter == 1) else if (((MPElementGeneratedEntity *)activeElement).counter == 1)
// Counter has initial value, no point resetting. // Counter has initial value, no point resetting.
return; return;
[self changeActiveElementWithWarning: [self changeActiveElementWithWarning:
@"You are resetting the site's password counter.\n\n" @"You are resetting the site's password counter.\n\n"
@"If you continue, the site's password will change back to its original value. " @"If you continue, the site's password will change back to its original value. "
@"You will then need to update your account's password back to this original value." @"You will then need to update your account's password back to this original value."
do:^BOOL(MPElementEntity *activeElement_){ do:^BOOL(MPElementEntity *activeElement_) {
inf(@"Resetting password counter for: %@", activeElement_.name); inf(@"Resetting password counter for: %@", activeElement_.name);
((MPElementGeneratedEntity *)activeElement_).counter = 1; ((MPElementGeneratedEntity *)activeElement_).counter = 1;
MPCheckpoint( MPCheckpointResetPasswordCounter, @{ MPCheckpoint( MPCheckpointResetPasswordCounter, @{
@"type" : activeElement_.typeName, @"type" : activeElement_.typeName,
@"version" : @(activeElement_.version) @"version" : @(activeElement_.version)
} ); } );
return YES; return YES;
}]; }];
} }
- (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender { - (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateBegan) if (sender.state != UIGestureRecognizerStateBegan)
// Only fire when the gesture was first detected. // Only fire when the gesture was first detected.
return; return;
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
@ -620,12 +620,11 @@
NSError *error; NSError *error;
MPElementEntity *activeElement = (MPElementEntity *)[moc existingObjectWithID:_activeElementOID error:&error]; MPElementEntity *activeElement = (MPElementEntity *)[moc existingObjectWithID:_activeElementOID error:&error];
if (!activeElement) if (!activeElement)
err(@"Couldn't retrieve active element: %@", error); err(@"Couldn't retrieve active element: %@", error);
return activeElement; return activeElement;
} }
- (IBAction)editPassword { - (IBAction)editPassword {
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
@ -651,12 +650,12 @@
return; return;
NSString *warning = activeElement.type & MPElementTypeClassGenerated? NSString *warning = activeElement.type & MPElementTypeClassGenerated?
@"You are upgrading the site.\n\n" @"You are upgrading the site.\n\n"
@"This upgrade improves the site's compatibility with the latest version of Master Password.\n\n" @"This upgrade improves the site's compatibility with the latest version of Master Password.\n\n"
@"Your password will change and you will need to update your site's account." @"Your password will change and you will need to update your site's account."
: :
@"You are upgrading the site.\n\n" @"You are upgrading the site.\n\n"
@"This upgrade improves the site's compatibility with the latest version of Master Password."; @"This upgrade improves the site's compatibility with the latest version of Master Password.";
[self changeActiveElementWithWarning:warning do: [self changeActiveElementWithWarning:warning do:
^BOOL(MPElementEntity *activeElement_) { ^BOOL(MPElementEntity *activeElement_) {
@ -772,19 +771,20 @@
- (void)didSelectType:(MPElementType)type { - (void)didSelectType:(MPElementType)type {
[self changeActiveElementWithWarning: [self changeActiveElementWithWarning:
@"You are about to change the type of this password.\n\n" @"You are about to change the type of this password.\n\n"
@"If you continue, the password for this site will change. " @"If you continue, the password for this site will change. "
@"You will need to update your account's old password to the new one." @"You will need to update your account's old password to the new one."
do:^BOOL(MPElementEntity *activeElement){ do:^BOOL(MPElementEntity *activeElement) {
if ([activeElement.algorithm classOfType:type] != activeElement.typeClass) { if ([activeElement.algorithm classOfType:type] != activeElement.typeClass) {
// Type requires a different class of element. Recreate the element. // Type requires a different class of element. Recreate the element.
MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:[activeElement.algorithm classNameOfType:type] MPElementEntity *newElement
inManagedObjectContext:activeElement.managedObjectContext]; = [NSEntityDescription insertNewObjectForEntityForName:[activeElement.algorithm classNameOfType:type]
newElement.name = activeElement.name; inManagedObjectContext:activeElement.managedObjectContext];
newElement.user = activeElement.user; newElement.name = activeElement.name;
newElement.uses = activeElement.uses; newElement.user = activeElement.user;
newElement.lastUsed = activeElement.lastUsed; newElement.uses = activeElement.uses;
newElement.version = activeElement.version; newElement.lastUsed = activeElement.lastUsed;
newElement.version = activeElement.version;
newElement.loginName = activeElement.loginName; newElement.loginName = activeElement.loginName;
[activeElement.managedObjectContext deleteObject:activeElement]; [activeElement.managedObjectContext deleteObject:activeElement];
@ -793,8 +793,8 @@
} }
activeElement.type = type; activeElement.type = type;
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification [[NSNotificationCenter defaultCenter]
object:activeElement.objectID]; postNotificationName:MPElementUpdatedNotification object:activeElement.objectID];
return YES; return YES;
}]; }];
} }
@ -809,11 +809,11 @@
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) { [self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
if ([activeElement use] == 1) if ([activeElement use] == 1)
[self showAlertWithTitle:@"New Site" message: [self showAlertWithTitle:@"New Site" message:
PearlString(@"You've just created a password for %@.\n\n" PearlString( @"You've just created a password for %@.\n\n"
@"IMPORTANT:\n" @"IMPORTANT:\n"
@"Go to %@ and set or change the password for your account to the password above.\n" @"Go to %@ and set or change the password for your account to the password above.\n"
@"Do this right away: if you forget, you may have trouble remembering which password to use to log into the site later on.", @"Do this right away: if you forget, you may have trouble remembering which password to use to log into the site later on.",
activeElement.name, activeElement.name)]; activeElement.name, activeElement.name )];
return YES; return YES;
}]; }];
@ -828,11 +828,11 @@
[MPiOSConfig get].typeTipShown = PearlBool(YES); [MPiOSConfig get].typeTipShown = PearlBool(YES);
dispatch_after( dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.typeTipContainer.alpha = 0; self.typeTipContainer.alpha = 0;
}]; }];
}); } );
} }
}]; }];
@ -866,8 +866,9 @@
// Not of a type whose content can be edited. // Not of a type whose content can be edited.
err(@"Cannot update element content: Element is not stored: %@", activeElement.name); err(@"Cannot update element content: Element is not stored: %@", activeElement.name);
return; return;
} else if ([((MPElementStoredEntity *)activeElement).content isEqual:self.contentField.text]) }
// Content hasn't changed. else if ([((MPElementStoredEntity *)activeElement).content isEqual:self.contentField.text])
// Content hasn't changed.
return; return;
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement_) { [self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement_) {

View File

@ -12,12 +12,12 @@
@interface MPPreferencesViewController : UITableViewController<IASKSettingsDelegate, MPTypeDelegate> @interface MPPreferencesViewController : UITableViewController<IASKSettingsDelegate, MPTypeDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *avatarsView; @property(weak, nonatomic) IBOutlet UIScrollView *avatarsView;
@property (weak, nonatomic) IBOutlet UIButton *avatarTemplate; @property(weak, nonatomic) IBOutlet UIButton *avatarTemplate;
@property (weak, nonatomic) IBOutlet UISwitch *savePasswordSwitch; @property(weak, nonatomic) IBOutlet UISwitch *savePasswordSwitch;
@property (weak, nonatomic) IBOutlet UITableViewCell *exportCell; @property(weak, nonatomic) IBOutlet UITableViewCell *exportCell;
@property (weak, nonatomic) IBOutlet UITableViewCell *changeMPCell; @property(weak, nonatomic) IBOutlet UITableViewCell *changeMPCell;
@property (weak, nonatomic) IBOutlet UILabel *defaultTypeLabel; @property(weak, nonatomic) IBOutlet UILabel *defaultTypeLabel;
- (IBAction)didToggleSwitch:(UISwitch *)sender; - (IBAction)didToggleSwitch:(UISwitch *)sender;
- (IBAction)settings:(id)sender; - (IBAction)settings:(id)sender;

View File

@ -12,7 +12,7 @@
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@interface MPPreferencesViewController () @interface MPPreferencesViewController()
@end @end
@ -24,35 +24,35 @@
for (int a = 0; a < MPAvatarCount; ++a) { for (int a = 0; a < MPAvatarCount; ++a) {
UIButton *avatar = [self.avatarTemplate clone]; UIButton *avatar = [self.avatarTemplate clone];
avatar.tag = a; avatar.tag = a;
avatar.hidden = NO; avatar.hidden = NO;
avatar.center = CGPointMake( avatar.center = CGPointMake(
self.avatarTemplate.center.x * (a + 1) + self.avatarTemplate.bounds.size.width / 2 * a, self.avatarTemplate.center.x * (a + 1) + self.avatarTemplate.bounds.size.width / 2 * a,
self.avatarTemplate.center.y); self.avatarTemplate.center.y );
[avatar setBackgroundImage:[UIImage imageNamed:PearlString(@"avatar-%d", a)] [avatar setBackgroundImage:[UIImage imageNamed:PearlString( @"avatar-%d", a )]
forState:UIControlStateNormal]; forState:UIControlStateNormal];
[avatar setSelectionInSuperviewCandidate:YES isClearable:NO]; [avatar setSelectionInSuperviewCandidate:YES isClearable:NO];
avatar.layer.cornerRadius = avatar.bounds.size.height / 2; avatar.layer.cornerRadius = avatar.bounds.size.height / 2;
avatar.layer.shadowColor = [UIColor blackColor].CGColor; avatar.layer.shadowColor = [UIColor blackColor].CGColor;
avatar.layer.shadowOpacity = 1; avatar.layer.shadowOpacity = 1;
avatar.layer.shadowRadius = 5; avatar.layer.shadowRadius = 5;
avatar.backgroundColor = [UIColor clearColor]; avatar.backgroundColor = [UIColor clearColor];
[avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) { [avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) {
if (highlighted || selected) if (highlighted || selected)
avatar.backgroundColor = self.avatarTemplate.backgroundColor; avatar.backgroundColor = self.avatarTemplate.backgroundColor;
else else
avatar.backgroundColor = [UIColor clearColor]; avatar.backgroundColor = [UIColor clearColor];
} options:0]; } options:0];
[avatar onSelect:^(BOOL selected) { [avatar onSelect:^(BOOL selected) {
if (selected) { if (selected) {
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread]; MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
activeUser.avatar = (unsigned)avatar.tag; activeUser.avatar = (unsigned)avatar.tag;
[activeUser.managedObjectContext saveToStore]; [activeUser.managedObjectContext saveToStore];
} }
} options:0]; } options:0];
avatar.selected = (a == [[MPAppDelegate get] activeUserForThread].avatar); avatar.selected = (a == [[MPAppDelegate get] activeUserForThread].avatar);
} }
[super viewDidLoad]; [super viewDidLoad];
@ -64,9 +64,10 @@
[self.avatarsView autoSizeContent]; [self.avatarsView autoSizeContent];
[self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { [self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if (subview.tag && ((UIControl *)subview).selected) { if (subview.tag && ((UIControl *)subview).selected) {
[self.avatarsView setContentOffset:CGPointMake(subview.center.x - self.avatarsView.bounds.size.width / 2, 0) animated:animated]; [self.avatarsView setContentOffset:CGPointMake( subview.center.x - self.avatarsView.bounds.size.width / 2, 0 )
animated:animated];
} }
} recurse:NO]; } recurse:NO];
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread]; MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
self.savePasswordSwitch.on = activeUser.saveKey; self.savePasswordSwitch.on = activeUser.saveKey;
@ -112,11 +113,10 @@
if (cell == self.exportCell) if (cell == self.exportCell)
[[MPAppDelegate get] export]; [[MPAppDelegate get] export];
else else if (cell == self.changeMPCell) {
if (cell == self.changeMPCell) { MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread]; [[MPAppDelegate get] changeMasterPasswordFor:activeUser inContext:activeUser.managedObjectContext didResetBlock:nil];
[[MPAppDelegate get] changeMasterPasswordFor:activeUser inContext:activeUser.managedObjectContext didResetBlock:nil]; }
}
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
} }

View File

@ -20,8 +20,8 @@
@interface MPSetupViewController : UIViewController @interface MPSetupViewController : UIViewController
@property (weak, nonatomic) IBOutlet UISwitch *cloudSwitch; @property(weak, nonatomic) IBOutlet UISwitch *cloudSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *rememberLoginSwitch; @property(weak, nonatomic) IBOutlet UISwitch *rememberLoginSwitch;
- (IBAction)close:(UIBarButtonItem *)sender; - (IBAction)close:(UIBarButtonItem *)sender;

View File

@ -10,7 +10,6 @@
#import "MPEntities.h" #import "MPEntities.h"
@protocol MPTypeDelegate<NSObject> @protocol MPTypeDelegate<NSObject>
@required @required
@ -24,7 +23,7 @@
@interface MPTypeViewController : UITableViewController @interface MPTypeViewController : UITableViewController
@property (nonatomic, weak) id<MPTypeDelegate> delegate; @property(nonatomic, weak) id<MPTypeDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIView *recommendedTipContainer; @property(weak, nonatomic) IBOutlet UIView *recommendedTipContainer;
@end @end

View File

@ -9,10 +9,8 @@
#import "MPTypeViewController.h" #import "MPTypeViewController.h"
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h"
@interface MPTypeViewController()
@interface MPTypeViewController ()
- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath; - (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath;
@ -37,11 +35,11 @@
self.recommendedTipContainer.alpha = 1; self.recommendedTipContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.recommendedTipContainer.alpha = 0; self.recommendedTipContainer.alpha = 0;
}]; }];
}); } );
} }
}]; }];
@ -76,7 +74,7 @@
cell.selected = (selectedType == cellType); cell.selected = (selectedType == cellType);
if (cellType != NSNotFound && cellType & MPElementTypeClassGenerated) { if (cellType != NSNotFound && cellType & MPElementTypeClassGenerated) {
[(UITextField *) [cell viewWithTag:2] setText:@"..."]; [(UITextField *)[cell viewWithTag:2] setText:@"..."];
NSString *name = selectedElement.name; NSString *name = selectedElement.name;
NSUInteger counter = ((MPElementGeneratedEntity *)selectedElement).counter; NSUInteger counter = ((MPElementGeneratedEntity *)selectedElement).counter;
@ -100,7 +98,7 @@
MPElementType type = [self typeAtIndexPath:indexPath]; MPElementType type = [self typeAtIndexPath:indexPath];
if (type == NSNotFound) if (type == NSNotFound)
// Selected a non-type row. // Selected a non-type row.
return; return;
[self.delegate didSelectType:type]; [self.delegate didSelectType:type];

View File

@ -10,34 +10,34 @@
@interface MPUnlockViewController : UIViewController<UITextFieldDelegate, UIScrollViewDelegate, UIWebViewDelegate> @interface MPUnlockViewController : UIViewController<UITextFieldDelegate, UIScrollViewDelegate, UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *spinner; @property(weak, nonatomic) IBOutlet UIImageView *spinner;
@property (weak, nonatomic) IBOutlet UILabel *passwordFieldLabel; @property(weak, nonatomic) IBOutlet UILabel *passwordFieldLabel;
@property (weak, nonatomic) IBOutlet UITextField *passwordField; @property(weak, nonatomic) IBOutlet UITextField *passwordField;
@property (weak, nonatomic) IBOutlet UIView *passwordView; @property(weak, nonatomic) IBOutlet UIView *passwordView;
@property (weak, nonatomic) IBOutlet UIScrollView *avatarsView; @property(weak, nonatomic) IBOutlet UIScrollView *avatarsView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel; @property(weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *oldNameLabel; @property(weak, nonatomic) IBOutlet UILabel *oldNameLabel;
@property (weak, nonatomic) IBOutlet UIButton *avatarTemplate; @property(weak, nonatomic) IBOutlet UIButton *avatarTemplate;
@property (weak, nonatomic) IBOutlet UIView *createPasswordTipView; @property(weak, nonatomic) IBOutlet UIView *createPasswordTipView;
@property (weak, nonatomic) IBOutlet UILabel *tip; @property(weak, nonatomic) IBOutlet UILabel *tip;
@property (weak, nonatomic) IBOutlet UIView *passwordTipView; @property(weak, nonatomic) IBOutlet UIView *passwordTipView;
@property (weak, nonatomic) IBOutlet UILabel *passwordTipLabel; @property(weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
@property (weak, nonatomic) IBOutlet UIView *wordWall; @property(weak, nonatomic) IBOutlet UIView *wordWall;
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture; @property(strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture;
@property (weak, nonatomic) IBOutlet UIView *uiContainer; @property(weak, nonatomic) IBOutlet UIView *uiContainer;
@property (weak, nonatomic) IBOutlet UIWebView *newsView; @property(weak, nonatomic) IBOutlet UIWebView *newsView;
@property (weak, nonatomic) IBOutlet UIView *emergencyGeneratorContainer; @property(weak, nonatomic) IBOutlet UIView *emergencyGeneratorContainer;
@property (weak, nonatomic) IBOutlet UITextField *emergencyName; @property(weak, nonatomic) IBOutlet UITextField *emergencyName;
@property (weak, nonatomic) IBOutlet UITextField *emergencyMasterPassword; @property(weak, nonatomic) IBOutlet UITextField *emergencyMasterPassword;
@property (weak, nonatomic) IBOutlet UITextField *emergencySite; @property(weak, nonatomic) IBOutlet UITextField *emergencySite;
@property (weak, nonatomic) IBOutlet UIStepper *emergencyCounterStepper; @property(weak, nonatomic) IBOutlet UIStepper *emergencyCounterStepper;
@property (weak, nonatomic) IBOutlet UISegmentedControl *emergencyTypeControl; @property(weak, nonatomic) IBOutlet UISegmentedControl *emergencyTypeControl;
@property (weak, nonatomic) IBOutlet UILabel *emergencyCounter; @property(weak, nonatomic) IBOutlet UILabel *emergencyCounter;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *emergencyActivity; @property(weak, nonatomic) IBOutlet UIActivityIndicatorView *emergencyActivity;
@property (weak, nonatomic) IBOutlet UIButton *emergencyPassword; @property(weak, nonatomic) IBOutlet UIButton *emergencyPassword;
@property (weak, nonatomic) IBOutlet UIView *emergencyContentTipContainer; @property(weak, nonatomic) IBOutlet UIView *emergencyContentTipContainer;
@property (nonatomic, strong) UIColor *avatarShadowColor; @property(nonatomic, strong) UIColor *avatarShadowColor;
- (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender; - (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender;
- (IBAction)facebook:(UIButton *)sender; - (IBAction)facebook:(UIButton *)sender;

View File

@ -14,11 +14,11 @@
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@interface MPUnlockViewController () @interface MPUnlockViewController()
@property (strong, nonatomic) NSMutableDictionary *avatarToUserOID; @property(strong, nonatomic) NSMutableDictionary *avatarToUserOID;
@property (nonatomic) BOOL wordWallAnimating; @property(nonatomic) BOOL wordWallAnimating;
@property (nonatomic, strong) NSArray *wordList; @property(nonatomic, strong) NSArray *wordList;
@property(nonatomic, strong) NSOperationQueue *emergencyQueue; @property(nonatomic, strong) NSOperationQueue *emergencyQueue;
@property(nonatomic, strong) MPKey *emergencyKey; @property(nonatomic, strong) MPKey *emergencyKey;
@ -34,28 +34,28 @@
- (void)initializeAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc { - (void)initializeAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
UIScrollView *alertAvatarScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(12, 30, 260, 150)]; UIScrollView *alertAvatarScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake( 12, 30, 260, 150 )];
alertAvatarScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite; alertAvatarScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
[alertAvatarScrollView flashScrollIndicatorsContinuously]; [alertAvatarScrollView flashScrollIndicatorsContinuously];
[alert addSubview:alertAvatarScrollView]; [alert addSubview:alertAvatarScrollView];
CGPoint selectedOffset = CGPointZero; CGPoint selectedOffset = CGPointZero;
for (int a = 0; a < MPAvatarCount; ++a) { for (int a = 0; a < MPAvatarCount; ++a) {
UIButton *avatar = [self.avatarTemplate cloneAddedTo:alertAvatarScrollView]; UIButton *avatar = [self.avatarTemplate cloneAddedTo:alertAvatarScrollView];
avatar.tag = a; avatar.tag = a;
avatar.hidden = NO; avatar.hidden = NO;
avatar.center = CGPointMake( avatar.center = CGPointMake(
(20 + self.avatarTemplate.bounds.size.width / 2) * (a + 1) + self.avatarTemplate.bounds.size.width / 2 * a, (20 + self.avatarTemplate.bounds.size.width / 2) * (a + 1) + self.avatarTemplate.bounds.size.width / 2 * a,
20 + self.avatarTemplate.bounds.size.height / 2); 20 + self.avatarTemplate.bounds.size.height / 2 );
[avatar setBackgroundImage:[UIImage imageNamed:PearlString(@"avatar-%d", a)] forState:UIControlStateNormal]; [avatar setBackgroundImage:[UIImage imageNamed:PearlString( @"avatar-%d", a )] forState:UIControlStateNormal];
[avatar setSelectionInSuperviewCandidate:YES isClearable:NO]; [avatar setSelectionInSuperviewCandidate:YES isClearable:NO];
avatar.layer.cornerRadius = avatar.bounds.size.height / 2; avatar.layer.cornerRadius = avatar.bounds.size.height / 2;
avatar.layer.shadowColor = [UIColor blackColor].CGColor; avatar.layer.shadowColor = [UIColor blackColor].CGColor;
avatar.layer.shadowOpacity = 1; avatar.layer.shadowOpacity = 1;
avatar.layer.shadowRadius = 5; avatar.layer.shadowRadius = 5;
avatar.backgroundColor = [UIColor clearColor]; avatar.backgroundColor = [UIColor clearColor];
[avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) { [avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) {
if (highlighted || selected) if (highlighted || selected)
@ -69,9 +69,9 @@
user.avatar = (unsigned)avatar.tag; user.avatar = (unsigned)avatar.tag;
}]; }];
} options:0]; } options:0];
avatar.selected = (a == user.avatar); avatar.selected = (a == user.avatar);
if (avatar.selected) if (avatar.selected)
selectedOffset = CGPointMake(avatar.center.x - alertAvatarScrollView.bounds.size.width / 2, 0); selectedOffset = CGPointMake( avatar.center.x - alertAvatarScrollView.bounds.size.width / 2, 0 );
} }
[alertAvatarScrollView autoSizeContent]; [alertAvatarScrollView autoSizeContent];
@ -80,28 +80,28 @@
- (void)initializeConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user { - (void)initializeConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user {
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(12, 70, 260, 110)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake( 12, 70, 260, 110 )];
[alert addSubview:container]; [alert addSubview:container];
UIButton *alertAvatar = [self.avatarTemplate cloneAddedTo:container]; UIButton *alertAvatar = [self.avatarTemplate cloneAddedTo:container];
alertAvatar.center = CGPointMake(130, 55); alertAvatar.center = CGPointMake( 130, 55 );
alertAvatar.hidden = NO; alertAvatar.hidden = NO;
alertAvatar.layer.shadowColor = [UIColor blackColor].CGColor; alertAvatar.layer.shadowColor = [UIColor blackColor].CGColor;
alertAvatar.layer.shadowOpacity = 1; alertAvatar.layer.shadowOpacity = 1;
alertAvatar.layer.shadowRadius = 5; alertAvatar.layer.shadowRadius = 5;
alertAvatar.backgroundColor = [UIColor clearColor]; alertAvatar.backgroundColor = [UIColor clearColor];
[alertAvatar setBackgroundImage:[UIImage imageNamed:PearlString(@"avatar-%d", user.avatar)] forState:UIControlStateNormal]; [alertAvatar setBackgroundImage:[UIImage imageNamed:PearlString( @"avatar-%d", user.avatar )] forState:UIControlStateNormal];
UILabel *alertNameLabel = [self.nameLabel cloneAddedTo:container]; UILabel *alertNameLabel = [self.nameLabel cloneAddedTo:container];
alertNameLabel.center = alertAvatar.center; alertNameLabel.center = alertAvatar.center;
alertNameLabel.text = user.name; alertNameLabel.text = user.name;
alertNameLabel.bounds = CGRectSetHeight(alertNameLabel.bounds, alertNameLabel.bounds = CGRectSetHeight( alertNameLabel.bounds,
[alertNameLabel.text sizeWithFont:self.nameLabel.font [alertNameLabel.text sizeWithFont:self.nameLabel.font
constrainedToSize:CGSizeMake(alertNameLabel.bounds.size.width - 10, constrainedToSize:CGSizeMake( alertNameLabel.bounds.size.width - 10,
100) 100 )
lineBreakMode:self.nameLabel.lineBreakMode].height); lineBreakMode:self.nameLabel.lineBreakMode].height );
alertNameLabel.layer.cornerRadius = 5; alertNameLabel.layer.cornerRadius = 5;
alertNameLabel.backgroundColor = [UIColor blackColor]; alertNameLabel.backgroundColor = [UIColor blackColor];
} }
- (BOOL)shouldAutorotate { - (BOOL)shouldAutorotate {
@ -122,19 +122,19 @@
[self.avatarsView addGestureRecognizer:self.targetedUserActionGesture]; [self.avatarsView addGestureRecognizer:self.targetedUserActionGesture];
self.avatarsView.decelerationRate = UIScrollViewDecelerationRateFast; self.avatarsView.decelerationRate = UIScrollViewDecelerationRateFast;
self.avatarsView.clipsToBounds = NO; self.avatarsView.clipsToBounds = NO;
self.tip.text = @""; self.tip.text = @"";
self.nameLabel.layer.cornerRadius = 5; self.nameLabel.layer.cornerRadius = 5;
self.avatarTemplate.hidden = YES; self.avatarTemplate.hidden = YES;
self.spinner.alpha = 0; self.spinner.alpha = 0;
self.passwordTipView.hidden = NO; self.passwordTipView.hidden = NO;
self.createPasswordTipView.hidden = NO; self.createPasswordTipView.hidden = NO;
[self.emergencyPassword setTitle:@"" forState:UIControlStateNormal]; [self.emergencyPassword setTitle:@"" forState:UIControlStateNormal];
self.emergencyGeneratorContainer.alpha = 0; self.emergencyGeneratorContainer.alpha = 0;
self.emergencyGeneratorContainer.hidden = YES; self.emergencyGeneratorContainer.hidden = YES;
self.emergencyQueue = [NSOperationQueue new]; self.emergencyQueue = [NSOperationQueue new];
[self.emergencyCounterStepper addTargetBlock:^(id sender, UIControlEvents event) { [self.emergencyCounterStepper addTargetBlock:^(id sender, UIControlEvents event) {
self.emergencyCounter.text = PearlString( @"%d", (NSUInteger)self.emergencyCounterStepper.value); self.emergencyCounter.text = PearlString( @"%d", (NSUInteger)self.emergencyCounterStepper.value );
[self updateEmergencyPassword]; [self updateEmergencyPassword];
} forControlEvents:UIControlEventValueChanged]; } forControlEvents:UIControlEventValueChanged];
@ -143,8 +143,10 @@
} forControlEvents:UIControlEventValueChanged]; } forControlEvents:UIControlEventValueChanged];
self.emergencyContentTipContainer.alpha = 0; self.emergencyContentTipContainer.alpha = 0;
self.emergencyContentTipContainer.hidden = NO; self.emergencyContentTipContainer.hidden = NO;
self.marqueeTipTexts = @[ @"Tap and hold to delete or reset user.", self.marqueeTipTexts = @[
@"Shake for emergency generator." ]; @"Tap and hold to delete or reset user.",
@"Shake for emergency generator."
];
NSMutableArray *wordListLines = [NSMutableArray arrayWithCapacity:27413]; NSMutableArray *wordListLines = [NSMutableArray arrayWithCapacity:27413];
[[[NSString alloc] initWithData:[NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"dictionary" withExtension:@"lst"]] [[[NSString alloc] initWithData:[NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"dictionary" withExtension:@"lst"]]
@ -160,26 +162,26 @@
[self initializeWordLabel:wordLabel]; [self initializeWordLabel:wordLabel];
} recurse:NO]; } recurse:NO];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self emergencyCloseAnimated:NO]; [self emergencyCloseAnimated:NO];
self.uiContainer.alpha = 0; self.uiContainer.alpha = 0;
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil [[NSNotificationCenter defaultCenter]
usingBlock:^(NSNotification *note) { addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self updateLayoutAnimated:NO allowScroll:NO completion:nil]; [self updateLayoutAnimated:NO allowScroll:NO completion:nil];
[UIView animateWithDuration:1 animations:^{ [UIView animateWithDuration:1 animations:^{
self.uiContainer.alpha = 1; self.uiContainer.alpha = 1;
}]; }];
}]; }];
[self updateLayoutAnimated:NO allowScroll:YES completion:nil]; [self updateLayoutAnimated:NO allowScroll:YES completion:nil];
@ -250,16 +252,17 @@
self.emergencyGeneratorContainer.hidden = NO; self.emergencyGeneratorContainer.hidden = NO;
[UIView animateWithDuration:0.5 animations:^{ [UIView animateWithDuration:0.5 animations:^{
self.emergencyGeneratorContainer.alpha = 1; self.emergencyGeneratorContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[self.emergencyName becomeFirstResponder]; [self.emergencyName becomeFirstResponder];
}]; }];
} }
} }
- (void)marqueeTip { - (void)marqueeTip {
[UIView animateWithDuration:0.5 animations:^{ [UIView animateWithDuration:0.5 animations:^{
self.tip.alpha = 0; self.tip.alpha = 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (!finished) if (!finished)
return; return;
@ -278,9 +281,9 @@
__block NSArray *users = nil; __block NSArray *users = nil;
[moc performBlockAndWait:^{ [moc performBlockAndWait:^{
NSError *error = nil; NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]]; fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
users = [moc executeFetchRequest:fetchRequest error:&error]; users = [moc executeFetchRequest:fetchRequest error:&error];
if (!users) if (!users)
err(@"Failed to load users: %@", error); err(@"Failed to load users: %@", error);
@ -289,7 +292,7 @@
// Clean up avatars. // Clean up avatars.
for (UIView *subview in [self.avatarsView subviews]) for (UIView *subview in [self.avatarsView subviews])
if ([[self.avatarToUserOID allKeys] containsObject:[NSValue valueWithNonretainedObject:subview]]) if ([[self.avatarToUserOID allKeys] containsObject:[NSValue valueWithNonretainedObject:subview]])
// This subview is a former avatar. // This subview is a former avatar.
[subview removeFromSuperview]; [subview removeFromSuperview];
[self.avatarToUserOID removeAllObjects]; [self.avatarToUserOID removeAllObjects];
@ -308,17 +311,17 @@
- (UIButton *)setupAvatar:(UIButton *)avatar forUser:(MPUserEntity *)user { - (UIButton *)setupAvatar:(UIButton *)avatar forUser:(MPUserEntity *)user {
avatar.center = CGPointMake(avatar.center.x + [self.avatarToUserOID count] * 160, avatar.center.y); avatar.center = CGPointMake( avatar.center.x + [self.avatarToUserOID count] * 160, avatar.center.y );
avatar.hidden = NO; avatar.hidden = NO;
avatar.layer.cornerRadius = avatar.bounds.size.height / 2; avatar.layer.cornerRadius = avatar.bounds.size.height / 2;
avatar.layer.shadowColor = [UIColor blackColor].CGColor; avatar.layer.shadowColor = [UIColor blackColor].CGColor;
avatar.layer.shadowOpacity = 1; avatar.layer.shadowOpacity = 1;
avatar.layer.shadowRadius = 20; avatar.layer.shadowRadius = 20;
avatar.layer.masksToBounds = NO; avatar.layer.masksToBounds = NO;
avatar.backgroundColor = [UIColor clearColor]; avatar.backgroundColor = [UIColor clearColor];
avatar.tag = user.avatar; avatar.tag = user.avatar;
[avatar setBackgroundImage:[UIImage imageNamed:PearlString(@"avatar-%u", user.avatar)] [avatar setBackgroundImage:[UIImage imageNamed:PearlString( @"avatar-%u", user.avatar )]
forState:UIControlStateNormal]; forState:UIControlStateNormal];
[avatar setSelectionInSuperviewCandidate:YES isClearable:YES]; [avatar setSelectionInSuperviewCandidate:YES isClearable:YES];
[avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) { [avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) {
@ -331,7 +334,8 @@
if (!selected) { if (!selected) {
self.selectedUser = nil; self.selectedUser = nil;
[self didToggleUserSelection]; [self didToggleUserSelection];
} else if ((self.selectedUser = user)) }
else if ((self.selectedUser = user))
[self didToggleUserSelection]; [self didToggleUserSelection];
else else
[self didSelectNewUserAvatar:avatar]; [self didSelectNewUserAvatar:avatar];
@ -341,7 +345,7 @@
if ([_selectedUserOID isEqual:[user objectID]]) { if ([_selectedUserOID isEqual:[user objectID]]) {
self.selectedUser = user; self.selectedUser = user;
avatar.selected = YES; avatar.selected = YES;
} }
return avatar; return avatar;
@ -352,11 +356,10 @@
MPUserEntity *selectedUser = self.selectedUser; MPUserEntity *selectedUser = self.selectedUser;
if (!selectedUser) if (!selectedUser)
[self.passwordField resignFirstResponder]; [self.passwordField resignFirstResponder];
else else if ([[MPAppDelegate get] signInAsUser:selectedUser usingMasterPassword:nil]) {
if ([[MPAppDelegate get] signInAsUser:selectedUser usingMasterPassword:nil]) { [self performSegueWithIdentifier:@"MP_Unlock" sender:self];
[self performSegueWithIdentifier:@"MP_Unlock" sender:self]; return;
return; }
}
[self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) { [self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) {
if (self.selectedUser) if (self.selectedUser)
@ -367,7 +370,8 @@
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar { - (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
[MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) inManagedObjectContext:moc]; MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc];
[self showNewUserNameAlertFor:newUser inContext:moc completion:^(BOOL finished) { [self showNewUserNameAlertFor:newUser inContext:moc completion:^(BOOL finished) {
newUserAvatar.selected = NO; newUserAvatar.selected = NO;
@ -383,14 +387,14 @@
message:nil viewStyle:UIAlertViewStylePlainTextInput message:nil viewStyle:UIAlertViewStylePlainTextInput
initAlert:^(UIAlertView *alert, UITextField *firstField) { initAlert:^(UIAlertView *alert, UITextField *firstField) {
firstField.autocapitalizationType = UITextAutocapitalizationTypeWords; firstField.autocapitalizationType = UITextAutocapitalizationTypeWords;
firstField.keyboardType = UIKeyboardTypeAlphabet; firstField.keyboardType = UIKeyboardTypeAlphabet;
firstField.text = newUser.name; firstField.text = newUser.name;
firstField.placeholder = @"eg. Robert Lee Mitchell"; firstField.placeholder = @"eg. Robert Lee Mitchell";
firstField.enablesReturnKeyAutomatically = YES; firstField.enablesReturnKeyAutomatically = YES;
} }
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex]) { if (buttonIndex == [alert cancelButtonIndex]) {
completion(NO); completion( NO );
return; return;
} }
NSString *name = [alert textFieldAtIndex:0].text; NSString *name = [alert textFieldAtIndex:0].text;
@ -398,7 +402,7 @@
[PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil [PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
[self showNewUserNameAlertFor:newUser inContext:moc completion:completion]; [self showNewUserNameAlertFor:newUser inContext:moc completion:completion];
} cancelTitle:@"Try Again" otherTitles:nil]; } cancelTitle:@"Try Again" otherTitles:nil];
return; return;
} }
@ -431,8 +435,8 @@
[PearlAlert showAlertWithTitle:@"Is this correct?" [PearlAlert showAlertWithTitle:@"Is this correct?"
message: message:
@"Please double-check your name.\n" @"Please double-check your name.\n"
@"\n\n\n\n\n\n" @"\n\n\n\n\n\n"
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
initAlert:^void(UIAlertView *__alert, UITextField *__firstField) { initAlert:^void(UIAlertView *__alert, UITextField *__firstField) {
[self initializeConfirmationAlert:__alert forUser:newUser]; [self initializeConfirmationAlert:__alert forUser:newUser];
@ -445,7 +449,7 @@
// Confirm // Confirm
[moc saveToStore]; [moc saveToStore];
completion(YES); completion( YES );
[self updateUsers]; [self updateUsers];
} }
@ -455,18 +459,18 @@
- (void)updateLayoutAnimated:(BOOL)animated allowScroll:(BOOL)allowScroll completion:(void (^)(BOOL finished))completion { - (void)updateLayoutAnimated:(BOOL)animated allowScroll:(BOOL)allowScroll completion:(void (^)(BOOL finished))completion {
if (animated) { if (animated) {
self.oldNameLabel.text = self.nameLabel.text; self.oldNameLabel.text = self.nameLabel.text;
self.oldNameLabel.alpha = 1; self.oldNameLabel.alpha = 1;
self.nameLabel.alpha = 0; self.nameLabel.alpha = 0;
[UIView animateWithDuration:0.5f animations:^{ [UIView animateWithDuration:0.5f animations:^{
[self updateLayoutAnimated:NO allowScroll:allowScroll completion:nil]; [self updateLayoutAnimated:NO allowScroll:allowScroll completion:nil];
self.oldNameLabel.alpha = 0; self.oldNameLabel.alpha = 0;
self.nameLabel.alpha = 1; self.nameLabel.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (completion) if (completion)
completion(finished); completion( finished );
}]; }];
return; return;
} }
@ -474,92 +478,93 @@
// Lay out password entry and user selection views. // Lay out password entry and user selection views.
if (self.selectedUser && !self.passwordView.alpha) { if (self.selectedUser && !self.passwordView.alpha) {
// User was just selected. // User was just selected.
self.passwordView.alpha = 1; self.passwordView.alpha = 1;
self.avatarsView.center = CGPointMake(160, 180); self.avatarsView.center = CGPointMake( 160, 180 );
self.avatarsView.scrollEnabled = NO; self.avatarsView.scrollEnabled = NO;
self.nameLabel.center = CGPointMake(160, 94); self.nameLabel.center = CGPointMake( 160, 94 );
self.nameLabel.backgroundColor = [UIColor blackColor]; self.nameLabel.backgroundColor = [UIColor blackColor];
self.oldNameLabel.center = self.nameLabel.center; self.oldNameLabel.center = self.nameLabel.center;
self.avatarShadowColor = [UIColor whiteColor]; self.avatarShadowColor = [UIColor whiteColor];
} else }
if (!self.selectedUser && self.passwordView.alpha == 1) { else if (!self.selectedUser && self.passwordView.alpha == 1) {
// User was just deselected. // User was just deselected.
self.passwordField.text = nil; self.passwordField.text = nil;
self.passwordView.alpha = 0; self.passwordView.alpha = 0;
self.avatarsView.center = CGPointMake(160, 310); self.avatarsView.center = CGPointMake( 160, 310 );
self.avatarsView.scrollEnabled = YES; self.avatarsView.scrollEnabled = YES;
self.nameLabel.center = CGPointMake(160, 296); self.nameLabel.center = CGPointMake( 160, 296 );
self.nameLabel.backgroundColor = [UIColor clearColor]; self.nameLabel.backgroundColor = [UIColor clearColor];
self.oldNameLabel.center = self.nameLabel.center; self.oldNameLabel.center = self.nameLabel.center;
self.avatarShadowColor = [UIColor lightGrayColor]; self.avatarShadowColor = [UIColor lightGrayColor];
} }
// Lay out the word wall. // Lay out the word wall.
if (!self.selectedUser || self.selectedUser.keyID) { if (!self.selectedUser || self.selectedUser.keyID) {
self.passwordFieldLabel.text = @"Enter your master password:"; self.passwordFieldLabel.text = @"Enter your master password:";
self.wordWall.alpha = 0; self.wordWall.alpha = 0;
self.createPasswordTipView.alpha = 0; self.createPasswordTipView.alpha = 0;
self.wordWallAnimating = NO; self.wordWallAnimating = NO;
} else { }
else {
self.passwordFieldLabel.text = @"Create your master password:"; self.passwordFieldLabel.text = @"Create your master password:";
if (!self.wordWallAnimating) { if (!self.wordWallAnimating) {
self.wordWallAnimating = YES; self.wordWallAnimating = YES;
self.wordWall.alpha = 1; self.wordWall.alpha = 1;
self.createPasswordTipView.alpha = 1; self.createPasswordTipView.alpha = 1;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
// Jump out of our UIView animation block. // Jump out of our UIView animation block.
[self beginWordWallAnimation]; [self beginWordWallAnimation];
}); } );
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 15 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ dispatch_after( dispatch_time( DISPATCH_TIME_NOW, 15 * NSEC_PER_SEC ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:1 animations:^{ [UIView animateWithDuration:1 animations:^{
self.createPasswordTipView.alpha = 0; self.createPasswordTipView.alpha = 0;
}]; }];
}); } );
} }
} }
// Lay out user targeting. // Lay out user targeting.
MPUserEntity *targetedUser = self.selectedUser; MPUserEntity *targetedUser = self.selectedUser;
UIButton *selectedAvatar = [self avatarForUser:self.selectedUser]; UIButton *selectedAvatar = [self avatarForUser:self.selectedUser];
UIButton *targetedAvatar = selectedAvatar; UIButton *targetedAvatar = selectedAvatar;
if (!targetedAvatar) { if (!targetedAvatar) {
targetedAvatar = [self findTargetedAvatar]; targetedAvatar = [self findTargetedAvatar];
targetedUser = [self userForAvatar:targetedAvatar]; targetedUser = [self userForAvatar:targetedAvatar];
} }
[self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { [self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if (![[self.avatarToUserOID allKeys] containsObject:[NSValue valueWithNonretainedObject:subview]]) if (![[self.avatarToUserOID allKeys] containsObject:[NSValue valueWithNonretainedObject:subview]])
// This subview is not one of the user avatars. // This subview is not one of the user avatars.
return; return;
UIButton *avatar = (UIButton *)subview; UIButton *avatar = (UIButton *)subview;
BOOL isTargeted = avatar == targetedAvatar; BOOL isTargeted = avatar == targetedAvatar;
avatar.userInteractionEnabled = isTargeted; avatar.userInteractionEnabled = isTargeted;
avatar.alpha = isTargeted? 1: self.selectedUser? 0.1: 0.4; avatar.alpha = isTargeted? 1: self.selectedUser? 0.1: 0.4;
[self updateAvatarShadowColor:avatar isTargeted:isTargeted]; [self updateAvatarShadowColor:avatar isTargeted:isTargeted];
} recurse:NO]; } recurse:NO];
if (allowScroll) { if (allowScroll) {
CGPoint targetContentOffset = CGPointMake(MAX(0, targetedAvatar.center.x - self.avatarsView.bounds.size.width / 2), CGPoint targetContentOffset = CGPointMake( MAX(0, targetedAvatar.center.x - self.avatarsView.bounds.size.width / 2),
self.avatarsView.contentOffset.y); self.avatarsView.contentOffset.y );
if (!CGPointEqualToPoint(self.avatarsView.contentOffset, targetContentOffset)) if (!CGPointEqualToPoint( self.avatarsView.contentOffset, targetContentOffset ))
[self.avatarsView setContentOffset:targetContentOffset animated:animated]; [self.avatarsView setContentOffset:targetContentOffset animated:animated];
} }
// Lay out user name label. // Lay out user name label.
self.nameLabel.text = targetedAvatar? (targetedUser? targetedUser.name: @"New User"): nil; self.nameLabel.text = targetedAvatar? (targetedUser? targetedUser.name: @"New User"): nil;
self.nameLabel.bounds = CGRectSetHeight(self.nameLabel.bounds, self.nameLabel.bounds = CGRectSetHeight( self.nameLabel.bounds,
[self.nameLabel.text sizeWithFont:self.nameLabel.font [self.nameLabel.text sizeWithFont:self.nameLabel.font
constrainedToSize:CGSizeMake(self.nameLabel.bounds.size.width - 10, 100) constrainedToSize:CGSizeMake( self.nameLabel.bounds.size.width - 10, 100 )
lineBreakMode:self.nameLabel.lineBreakMode].height); lineBreakMode:self.nameLabel.lineBreakMode].height );
self.oldNameLabel.bounds = self.nameLabel.bounds; self.oldNameLabel.bounds = self.nameLabel.bounds;
if (completion) if (completion)
completion(YES); completion( YES );
} }
- (void)beginWordWallAnimation { - (void)beginWordWallAnimation {
@ -568,7 +573,7 @@
UILabel *wordLabel = (UILabel *)subview; UILabel *wordLabel = (UILabel *)subview;
if (wordLabel.frame.origin.x < -self.wordWall.frame.size.width / 3) { if (wordLabel.frame.origin.x < -self.wordWall.frame.size.width / 3) {
wordLabel.frame = CGRectSetX(wordLabel.frame, wordLabel.frame.origin.x + self.wordWall.frame.size.width); wordLabel.frame = CGRectSetX( wordLabel.frame, wordLabel.frame.origin.x + self.wordWall.frame.size.width );
[self initializeWordLabel:wordLabel]; [self initializeWordLabel:wordLabel];
} }
} recurse:NO]; } recurse:NO];
@ -578,7 +583,7 @@
[self.wordWall enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { [self.wordWall enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
UILabel *wordLabel = (UILabel *)subview; UILabel *wordLabel = (UILabel *)subview;
wordLabel.frame = CGRectSetX(wordLabel.frame, wordLabel.frame.origin.x - self.wordWall.frame.size.width / 3); wordLabel.frame = CGRectSetX( wordLabel.frame, wordLabel.frame.origin.x - self.wordWall.frame.size.width / 3 );
} recurse:NO]; } recurse:NO];
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) if (finished)
@ -589,7 +594,7 @@
- (void)initializeWordLabel:(UILabel *)wordLabel { - (void)initializeWordLabel:(UILabel *)wordLabel {
wordLabel.alpha = 0.05 + (random() % 35) / 100.0F; wordLabel.alpha = 0.05 + (random() % 35) / 100.0F;
wordLabel.text = [self.wordList objectAtIndex:(NSUInteger)random() % [self.wordList count]]; wordLabel.text = [self.wordList objectAtIndex:(NSUInteger)random() % [self.wordList count]];
} }
- (void)setPasswordTip:(NSString *)string { - (void)setPasswordTip:(NSString *)string {
@ -605,15 +610,15 @@
- (void)tryMasterPassword { - (void)tryMasterPassword {
if (!self.selectedUser) if (!self.selectedUser)
// No user selected, can't try sign-in. // No user selected, can't try sign-in.
return; return;
[self setSpinnerActive:YES]; [self setSpinnerActive:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
BOOL unlocked = [[MPAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:self.passwordField.text]; BOOL unlocked = [[MPAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:self.passwordField.text];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if (unlocked) if (unlocked)
[self performSegueWithIdentifier:@"MP_Unlock" sender:self]; [self performSegueWithIdentifier:@"MP_Unlock" sender:self];
@ -623,14 +628,14 @@
[self setSpinnerActive:NO]; [self setSpinnerActive:NO];
} }
}); } );
}); } );
} }
- (UIButton *)findTargetedAvatar { - (UIButton *)findTargetedAvatar {
CGFloat xOfMiddle = self.avatarsView.contentOffset.x + self.avatarsView.bounds.size.width / 2; CGFloat xOfMiddle = self.avatarsView.contentOffset.x + self.avatarsView.bounds.size.width / 2;
return (UIButton *)[PearlUIUtils viewClosestTo:CGPointMake(xOfMiddle, self.avatarsView.contentOffset.y) return (UIButton *)[PearlUIUtils viewClosestTo:CGPointMake( xOfMiddle, self.avatarsView.contentOffset.y )
ofArray:self.avatarsView.subviews]; ofArray:self.avatarsView.subviews];
} }
@ -660,7 +665,7 @@
NSError *error; NSError *error;
MPUserEntity *user = (MPUserEntity *)[moc existingObjectWithID:userOID error:&error]; MPUserEntity *user = (MPUserEntity *)[moc existingObjectWithID:userOID error:&error];
if (!user) if (!user)
err(@"Failed retrieving user for avatar: %@", error); err(@"Failed retrieving user for avatar: %@", error);
return user; return user;
} }
@ -669,16 +674,17 @@
PearlMainThread(^{ PearlMainThread(^{
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotate.toValue = [NSNumber numberWithDouble:2 * M_PI]; rotate.toValue = [NSNumber numberWithDouble:2 * M_PI];
rotate.duration = 5.0; rotate.duration = 5.0;
if (active) { if (active) {
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
rotate.fromValue = [NSNumber numberWithFloat:0]; rotate.fromValue = [NSNumber numberWithFloat:0];
rotate.repeatCount = MAXFLOAT; rotate.repeatCount = MAXFLOAT;
} else { }
else {
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
rotate.repeatCount = 1; rotate.repeatCount = 1;
} }
[self.spinner.layer removeAnimationForKey:@"rotation"]; [self.spinner.layer removeAnimationForKey:@"rotation"];
@ -700,44 +706,45 @@
if (targeted) { if (targeted) {
if (![avatar.layer animationForKey:@"targetedShadow"]) { if (![avatar.layer animationForKey:@"targetedShadow"]) {
CABasicAnimation *toShadowColorAnimation = [CABasicAnimation animationWithKeyPath:@"shadowColor"]; CABasicAnimation *toShadowColorAnimation = [CABasicAnimation animationWithKeyPath:@"shadowColor"];
toShadowColorAnimation.toValue = (__bridge id)(avatar.selected? self.avatarTemplate.backgroundColor toShadowColorAnimation.toValue = (__bridge id)(avatar.selected? self.avatarTemplate.backgroundColor
: [UIColor whiteColor]).CGColor; : [UIColor whiteColor]).CGColor;
toShadowColorAnimation.beginTime = 0.0f; toShadowColorAnimation.beginTime = 0.0f;
toShadowColorAnimation.duration = 0.5f; toShadowColorAnimation.duration = 0.5f;
toShadowColorAnimation.fillMode = kCAFillModeForwards; toShadowColorAnimation.fillMode = kCAFillModeForwards;
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"]; CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
toShadowOpacityAnimation.toValue = PearlFloat(0.2); toShadowOpacityAnimation.toValue = PearlFloat(0.2);
toShadowOpacityAnimation.duration = 0.5f; toShadowOpacityAnimation.duration = 0.5f;
CABasicAnimation *pulseShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"]; CABasicAnimation *pulseShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
pulseShadowOpacityAnimation.fromValue = PearlFloat(0.2); pulseShadowOpacityAnimation.fromValue = PearlFloat(0.2);
pulseShadowOpacityAnimation.toValue = PearlFloat(0.6); pulseShadowOpacityAnimation.toValue = PearlFloat(0.6);
pulseShadowOpacityAnimation.beginTime = 0.5f; pulseShadowOpacityAnimation.beginTime = 0.5f;
pulseShadowOpacityAnimation.duration = 2.0f; pulseShadowOpacityAnimation.duration = 2.0f;
pulseShadowOpacityAnimation.autoreverses = YES; pulseShadowOpacityAnimation.autoreverses = YES;
pulseShadowOpacityAnimation.repeatCount = MAXFLOAT; pulseShadowOpacityAnimation.repeatCount = MAXFLOAT;
CAAnimationGroup *group = [[CAAnimationGroup alloc] init]; CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = @[toShadowColorAnimation, toShadowOpacityAnimation, pulseShadowOpacityAnimation]; group.animations = @[ toShadowColorAnimation, toShadowOpacityAnimation, pulseShadowOpacityAnimation ];
group.duration = MAXFLOAT; group.duration = MAXFLOAT;
[avatar.layer removeAnimationForKey:@"inactiveShadow"]; [avatar.layer removeAnimationForKey:@"inactiveShadow"];
[avatar.layer addAnimation:group forKey:@"targetedShadow"]; [avatar.layer addAnimation:group forKey:@"targetedShadow"];
} }
} else { }
else {
if ([avatar.layer animationForKey:@"targetedShadow"]) { if ([avatar.layer animationForKey:@"targetedShadow"]) {
CABasicAnimation *toShadowColorAnimation = [CABasicAnimation animationWithKeyPath:@"shadowColor"]; CABasicAnimation *toShadowColorAnimation = [CABasicAnimation animationWithKeyPath:@"shadowColor"];
toShadowColorAnimation.toValue = (__bridge id)[UIColor blackColor].CGColor; toShadowColorAnimation.toValue = (__bridge id)[UIColor blackColor].CGColor;
toShadowColorAnimation.duration = 0.5f; toShadowColorAnimation.duration = 0.5f;
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"]; CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
toShadowOpacityAnimation.toValue = PearlFloat(1); toShadowOpacityAnimation.toValue = PearlFloat(1);
toShadowOpacityAnimation.duration = 0.5f; toShadowOpacityAnimation.duration = 0.5f;
CAAnimationGroup *group = [[CAAnimationGroup alloc] init]; CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = @[toShadowColorAnimation, toShadowOpacityAnimation]; group.animations = @[ toShadowColorAnimation, toShadowOpacityAnimation ];
group.duration = 0.5f; group.duration = 0.5f;
[avatar.layer removeAnimationForKey:@"targetedShadow"]; [avatar.layer removeAnimationForKey:@"targetedShadow"];
[avatar.layer addAnimation:group forKey:@"inactiveShadow"]; [avatar.layer addAnimation:group forKey:@"inactiveShadow"];
@ -911,7 +918,7 @@
if (animated) { if (animated) {
[UIView animateWithDuration:0.5 animations:^{ [UIView animateWithDuration:0.5 animations:^{
self.emergencyGeneratorContainer.alpha = 0; self.emergencyGeneratorContainer.alpha = 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[self emergencyCloseAnimated:NO]; [self emergencyCloseAnimated:NO];
}]; }];
return; return;
@ -933,9 +940,9 @@
targetContentOffset:(inout CGPoint *)targetContentOffset { targetContentOffset:(inout CGPoint *)targetContentOffset {
CGFloat xOfMiddle = targetContentOffset->x + scrollView.bounds.size.width / 2; CGFloat xOfMiddle = targetContentOffset->x + scrollView.bounds.size.width / 2;
UIButton *middleAvatar = (UIButton *)[PearlUIUtils viewClosestTo:CGPointMake(xOfMiddle, targetContentOffset->y) UIButton *middleAvatar = (UIButton *)[PearlUIUtils viewClosestTo:CGPointMake( xOfMiddle, targetContentOffset->y )
ofArray:scrollView.subviews]; ofArray:scrollView.subviews];
*targetContentOffset = CGPointMake(middleAvatar.center.x - scrollView.bounds.size.width / 2, targetContentOffset->y); *targetContentOffset = CGPointMake( middleAvatar.center.x - scrollView.bounds.size.width / 2, targetContentOffset->y );
[self updateLayoutAnimated:NO allowScroll:NO completion:nil]; [self updateLayoutAnimated:NO allowScroll:NO completion:nil];
} }
@ -979,7 +986,7 @@
NSManagedObjectContext *moc = targetedUser.managedObjectContext; NSManagedObjectContext *moc = targetedUser.managedObjectContext;
[PearlSheet showSheetWithTitle:targetedUser.name [PearlSheet showSheetWithTitle:targetedUser.name
viewStyle:UIActionSheetStyleBlackTranslucent viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])
return; return;
@ -989,18 +996,18 @@
[moc deleteObject:targetedUser]; [moc deleteObject:targetedUser];
[moc saveToStore]; [moc saveToStore];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[self updateUsers]; [self updateUsers];
}); } );
}]; }];
return; return;
} }
if (buttonIndex == [sheet firstOtherButtonIndex]) if (buttonIndex == [sheet firstOtherButtonIndex])
[[MPAppDelegate get] changeMasterPasswordFor:targetedUser inContext:moc didResetBlock:^{ [[MPAppDelegate get] changeMasterPasswordFor:targetedUser inContext:moc didResetBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
[[self avatarForUser:targetedUser] setSelected:YES]; [[self avatarForUser:targetedUser] setSelected:YES];
}); } );
}]; }];
} cancelTitle:[PearlStrings get].commonButtonCancel } cancelTitle:[PearlStrings get].commonButtonCancel
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil]; destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
@ -1040,7 +1047,7 @@
id<GPPShareBuilder> shareDialog = [[MPAppDelegate get].googlePlus shareDialog]; id<GPPShareBuilder> shareDialog = [[MPAppDelegate get].googlePlus shareDialog];
[[[shareDialog setURLToShare:[NSURL URLWithString:@"http://masterpasswordapp.com"]] [[[shareDialog setURLToShare:[NSURL URLWithString:@"http://masterpasswordapp.com"]]
setPrefillText:@"I've started doing passwords properly thanks to Master Password for iOS."] open]; setPrefillText:@"I've started doing passwords properly thanks to Master Password for iOS."] open];
} }
- (IBAction)mail:(UIButton *)sender { - (IBAction)mail:(UIButton *)sender {
@ -1069,18 +1076,19 @@
// Twitter // Twitter
UIApplication *application = [UIApplication sharedApplication]; UIApplication *application = [UIApplication sharedApplication];
for (NSString *candidate in @[ for (NSString *candidate in @[
@"twitter://user?screen_name=%@", // Twitter @"twitter://user?screen_name=%@", // Twitter
@"tweetbot:///user_profile/%@", // TweetBot @"tweetbot:///user_profile/%@", // TweetBot
@"echofon:///user_timeline?%@", // Echofon @"echofon:///user_timeline?%@", // Echofon
@"twit:///user?screen_name=%@", // Twittelator Pro @"twit:///user?screen_name=%@", // Twittelator Pro
@"x-seesmic://twitter_profile?twitter_screen_name=%@", // Seesmic @"x-seesmic://twitter_profile?twitter_screen_name=%@", // Seesmic
@"x-birdfeed://user?screen_name=%@", // Birdfeed @"x-birdfeed://user?screen_name=%@", // Birdfeed
@"tweetings:///user?screen_name=%@", // Tweetings @"tweetings:///user?screen_name=%@", // Tweetings
@"simplytweet:?link=http://twitter.com/%@", // SimplyTweet @"simplytweet:?link=http://twitter.com/%@", // SimplyTweet
@"icebird://user?screen_name=%@", // IceBird @"icebird://user?screen_name=%@", // IceBird
@"fluttr://user/%@", // Fluttr @"fluttr://user/%@", // Fluttr
@"http://twitter.com/%@"]) { @"http://twitter.com/%@"
NSURL *url = [NSURL URLWithString:PearlString(candidate, @"master_password")]; ]) {
NSURL *url = [NSURL URLWithString:PearlString( candidate, @"master_password" )];
if ([application canOpenURL:url]) { if ([application canOpenURL:url]) {
[application openURL:url]; [application openURL:url];
@ -1093,7 +1101,7 @@
// Mailing List // Mailing List
[PearlEMail sendEMailTo:@"masterpassword-join@lists.lyndir.com" subject:@"Subscribe" [PearlEMail sendEMailTo:@"masterpassword-join@lists.lyndir.com" subject:@"Subscribe"
body:@"Press 'Send' now to subscribe to the Master Password mailing list.\n\n" body:@"Press 'Send' now to subscribe to the Master Password mailing list.\n\n"
@"You'll be kept up-to-date on the evolution of and discussions revolving Master Password."]; @"You'll be kept up-to-date on the evolution of and discussions revolving Master Password."];
return; return;
} }
if (buttonIndex == [sheet firstOtherButtonIndex] + 4) { if (buttonIndex == [sheet firstOtherButtonIndex] + 4) {
@ -1119,7 +1127,7 @@
NSError *error; NSError *error;
MPUserEntity *selectedUser = (MPUserEntity *)[moc existingObjectWithID:_selectedUserOID error:&error]; MPUserEntity *selectedUser = (MPUserEntity *)[moc existingObjectWithID:_selectedUserOID error:&error];
if (!selectedUser) if (!selectedUser)
err(@"Failed to retrieve selected user: %@", error); err(@"Failed to retrieve selected user: %@", error);
return selectedUser; return selectedUser;
} }

View File

@ -10,11 +10,11 @@
@interface MPiOSConfig : MPConfig @interface MPiOSConfig : MPConfig
@property (nonatomic, retain) NSNumber *helpHidden; @property(nonatomic, retain) NSNumber *helpHidden;
@property (nonatomic, retain) NSNumber *siteInfoHidden; @property(nonatomic, retain) NSNumber *siteInfoHidden;
@property (nonatomic, retain) NSNumber *showSetup; @property(nonatomic, retain) NSNumber *showSetup;
@property (nonatomic, retain) NSNumber *actionsTipShown; @property(nonatomic, retain) NSNumber *actionsTipShown;
@property (nonatomic, retain) NSNumber *typeTipShown; @property(nonatomic, retain) NSNumber *typeTipShown;
@property (nonatomic, retain) NSNumber *loginNameTipShown; @property(nonatomic, retain) NSNumber *loginNameTipShown;
@end @end

View File

@ -7,6 +7,7 @@
// //
@implementation MPiOSConfig @implementation MPiOSConfig
@dynamic sendInfo, helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown; @dynamic sendInfo, helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown;
- (id)init { - (id)init {
@ -14,13 +15,15 @@
if (!(self = [super init])) if (!(self = [super init]))
return self; return self;
[self.defaults registerDefaults:@{ NSStringFromSelector(@selector(helpHidden)): @NO, [self.defaults registerDefaults:@{
NSStringFromSelector(@selector(siteInfoHidden)): @YES, NSStringFromSelector( @selector(helpHidden) ) : @NO,
NSStringFromSelector(@selector(showSetup)): @YES, NSStringFromSelector( @selector(siteInfoHidden) ) : @YES,
NSStringFromSelector(@selector(iTunesID)): @"510296984", NSStringFromSelector( @selector(showSetup) ) : @YES,
NSStringFromSelector(@selector(actionsTipShown)): PearlBoolNot(self.firstRun), NSStringFromSelector( @selector(iTunesID) ) : @"510296984",
NSStringFromSelector(@selector(typeTipShown)): PearlBoolNot(self.firstRun), NSStringFromSelector( @selector(actionsTipShown) ) : PearlBoolNot(self.firstRun),
NSStringFromSelector(@selector(loginNameTipShown)): PearlBool(NO)}]; NSStringFromSelector( @selector(typeTipShown) ) : PearlBoolNot(self.firstRun),
NSStringFromSelector( @selector(loginNameTipShown) ) : PearlBool(NO)
}];
return self; return self;
} }

View File

@ -1,165 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>M. Password</string> <string>M. Password</string>
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>
<array> <array>
<dict> <dict>
<key>CFBundleTypeExtensions</key> <key>CFBundleTypeExtensions</key>
<array> <array>
<string>mpsites</string> <string>mpsites</string>
</array> </array>
<key>CFBundleTypeIconFiles</key> <key>CFBundleTypeIconFiles</key>
<array/> <array/>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>Master Password sites</string> <string>Master Password sites</string>
<key>LSHandlerRank</key> <key>LSHandlerRank</key>
<string>Owner</string> <string>Owner</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
<array> <array>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.lhunath.MasterPassword.sites</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFiles</key> <key>CFBundleIconFiles</key>
<array> <array>
<string>Icon.png</string> <string>Icon.png</string>
<string>Icon@2x.png</string> <string>Icon@2x.png</string>
<string>Icon-72.png</string> <string>Icon-72.png</string>
<string>Icon-72@2x.png</string> <string>Icon-72@2x.png</string>
</array> </array>
<key>CFBundleIcons</key> <key>CFBundleIcons</key>
<dict> <dict>
<key>CFBundlePrimaryIcon</key> <key>CFBundlePrimaryIcon</key>
<dict> <dict>
<key>CFBundleIconFiles</key> <key>CFBundleIconFiles</key>
<array> <array>
<string>Icon.png</string> <string>Icon.png</string>
<string>Icon@2x.png</string> <string>Icon@2x.png</string>
<string>Icon-72.png</string> <string>Icon-72.png</string>
<string>Icon-72@2x.png</string> <string>Icon-72@2x.png</string>
</array> </array>
<key>UIPrerenderedIcon</key> <key>UIPrerenderedIcon</key>
<true/> <true/>
</dict> </dict>
</dict> </dict>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string> <string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>${PRODUCT_NAME}</string> <string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string>com.lyndir.lhunath.MasterPassword</string> <string>com.lyndir.lhunath.MasterPassword</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>com.lyndir.lhunath.MasterPassword</string> <string>com.lyndir.lhunath.MasterPassword</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>FacebookAppID</key> <key>FacebookAppID</key>
<string>257095917745237</string> <string>257095917745237</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2011-2013 Lyndir. All rights reserved.</string> <string>Copyright © 2011-2013 Lyndir. All rights reserved.</string>
<key>ReplacementFonts</key> <key>ReplacementFonts</key>
<dict> <dict>
<key>AmericanTypewriter-Bold</key> <key>AmericanTypewriter-Bold</key>
<string>SourceCodePro-Black</string> <string>SourceCodePro-Black</string>
<key>AmericanTypewriter-Light</key> <key>AmericanTypewriter-Light</key>
<string>SourceCodePro-ExtraLight</string> <string>SourceCodePro-ExtraLight</string>
<key>Futura-CondensedExtraBold</key> <key>Futura-CondensedExtraBold</key>
<string>Exo-ExtraBold</string> <string>Exo-ExtraBold</string>
<key>Futura-Medium</key> <key>Futura-Medium</key>
<string>Exo</string> <string>Exo</string>
</dict> </dict>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>Exo-Bold.otf</string> <string>Exo-Bold.otf</string>
<string>Exo-ExtraBold.otf</string> <string>Exo-ExtraBold.otf</string>
<string>Exo-Regular.otf</string> <string>Exo-Regular.otf</string>
<string>SourceCodePro-Black.otf</string> <string>SourceCodePro-Black.otf</string>
<string>SourceCodePro-ExtraLight.otf</string> <string>SourceCodePro-ExtraLight.otf</string>
</array> </array>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>MainStoryboard_iPhone</string> <string>MainStoryboard_iPhone</string>
<key>UIPrerenderedIcon</key> <key>UIPrerenderedIcon</key>
<true/> <true/>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<true/> <true/>
<key>UIStatusBarStyle</key> <key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string> <string>UIStatusBarStyleDefault</string>
<key>UIStatusBarTintParameters</key> <key>UIStatusBarTintParameters</key>
<dict> <dict>
<key>UINavigationBar</key> <key>UINavigationBar</key>
<dict> <dict>
<key>Style</key> <key>Style</key>
<string>UIBarStyleDefault</string> <string>UIBarStyleDefault</string>
<key>TintColor</key> <key>TintColor</key>
<dict> <dict>
<key>Blue</key> <key>Blue</key>
<real>0.42745098039215684</real> <real>0.42745098039215684</real>
<key>Green</key> <key>Green</key>
<real>0.39215686274509803</real> <real>0.39215686274509803</real>
<key>Red</key> <key>Red</key>
<real>0.37254901960784315</real> <real>0.37254901960784315</real>
</dict> </dict>
<key>Translucent</key> <key>Translucent</key>
<false/> <false/>
</dict> </dict>
</dict> </dict>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict> <dict>
<key>UTTypeDescription</key> <key>UTTypeDescription</key>
<string>Master Password sites</string> <string>Master Password sites</string>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.lhunath.MasterPassword.sites</string>
<key>UTTypeSize320IconFile</key> <key>UTTypeSize320IconFile</key>
<string></string> <string></string>
<key>UTTypeSize64IconFile</key> <key>UTTypeSize64IconFile</key>
<string></string> <string></string>
<key>UTTypeTagSpecification</key> <key>UTTypeTagSpecification</key>
<dict> <dict>
<key>public.filename-extension</key> <key>public.filename-extension</key>
<array> <array>
<string>mpsites</string> <string>mpsites</string>
</array> </array>
</dict> </dict>
</dict> </dict>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@ -1,95 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>PreferenceSpecifiers</key> <key>PreferenceSpecifiers</key>
<array> <array>
<dict> <dict>
<key>FooterText</key> <key>FooterText</key>
<string>If you&apos;re experiencing problems, enabling this will send us details that can help diagnose and resolve them. You will remain completely anonymous amongst the other thousands of users. No sensitive information is ever sent.</string> <string>If you&apos;re experiencing problems, enabling this will send us details that can help diagnose and resolve them.
<key>Title</key> You will remain completely anonymous amongst the other thousands of users. No sensitive information is ever sent.
<string></string> </string>
<key>Type</key> <key>Title</key>
<string>PSGroupSpecifier</string> <string></string>
</dict> <key>Type</key>
<dict> <string>PSGroupSpecifier</string>
<key>DefaultValue</key> </dict>
<string>[auto]</string> <dict>
<key>Title</key> <key>DefaultValue</key>
<string>Version</string> <string>[auto]</string>
<key>Key</key> <key>Title</key>
<string>unset</string> <string>Version</string>
<key>Type</key> <key>Key</key>
<string>PSTitleValueSpecifier</string> <string>unset</string>
</dict> <key>Type</key>
<dict> <string>PSTitleValueSpecifier</string>
<key>DefaultValue</key> </dict>
<string>[auto]</string> <dict>
<key>Title</key> <key>DefaultValue</key>
<string>Build</string> <string>[auto]</string>
<key>Key</key> <key>Title</key>
<string>unset</string> <string>Build</string>
<key>Type</key> <key>Key</key>
<string>PSTitleValueSpecifier</string> <string>unset</string>
</dict> <key>Type</key>
<dict> <string>PSTitleValueSpecifier</string>
<key>DefaultValue</key> </dict>
<string>[auto]</string> <dict>
<key>Title</key> <key>DefaultValue</key>
<string>Copyright</string> <string>[auto]</string>
<key>Type</key> <key>Title</key>
<string>PSTitleValueSpecifier</string> <string>Copyright</string>
<key>Key</key> <key>Type</key>
<string>unset</string> <string>PSTitleValueSpecifier</string>
</dict> <key>Key</key>
<dict> <string>unset</string>
<key>Type</key> </dict>
<string>PSToggleSwitchSpecifier</string> <dict>
<key>Title</key> <key>Type</key>
<string>Send Diagnostic Info</string> <string>PSToggleSwitchSpecifier</string>
<key>Key</key> <key>Title</key>
<string>sendInfo</string> <string>Send Diagnostic Info</string>
<key>DefaultValue</key> <key>Key</key>
<false/> <string>sendInfo</string>
</dict> <key>DefaultValue</key>
<dict> <false/>
<key>FooterText</key> </dict>
<string>When enabled, closing the application will not log out the user. Similar to your phone&apos;s SIM lock, you only need to log in once after powering on.</string> <dict>
<key>Title</key> <key>FooterText</key>
<string>Master Password</string> <string>When enabled, closing the application will not log out the user. Similar to your phone&apos;s SIM lock, you only
<key>Type</key> need to log in once after powering on.
<string>PSGroupSpecifier</string> </string>
</dict> <key>Title</key>
<dict> <string>Master Password</string>
<key>DefaultValue</key> <key>Type</key>
<false/> <string>PSGroupSpecifier</string>
<key>Key</key> </dict>
<string>rememberLogin</string> <dict>
<key>Title</key> <key>DefaultValue</key>
<string>Stay logged in</string> <false/>
<key>Type</key> <key>Key</key>
<string>PSToggleSwitchSpecifier</string> <string>rememberLogin</string>
</dict> <key>Title</key>
<dict> <string>Stay logged in</string>
<key>Type</key> <key>Type</key>
<string>PSGroupSpecifier</string> <string>PSToggleSwitchSpecifier</string>
<key>Title</key> </dict>
<string></string> <dict>
<key>FooterText</key> <key>Type</key>
<string>Makes your sites available to all your Apple devices. This also works as a way of automatically keeping a back-up of your sites. Apple cannot see any of your passwords.</string> <string>PSGroupSpecifier</string>
</dict> <key>Title</key>
<dict> <string></string>
<key>Type</key> <key>FooterText</key>
<string>PSToggleSwitchSpecifier</string> <string>Makes your sites available to all your Apple devices. This also works as a way of automatically keeping a back-up of
<key>Title</key> your sites. Apple cannot see any of your passwords.
<string>iCloud</string> </string>
<key>Key</key> </dict>
<string>USMCloudEnabledKey</string> <dict>
<key>DefaultValue</key> <key>Type</key>
<true/> <string>PSToggleSwitchSpecifier</string>
</dict> <key>Title</key>
</array> <string>iCloud</string>
<key>StringsTable</key> <key>Key</key>
<string>Root</string> <string>USMCloudEnabledKey</string>
</dict> <key>DefaultValue</key>
<true/>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>
</dict>
</plist> </plist>

View File

@ -11,6 +11,6 @@
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@autoreleasepool { @autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MPAppDelegate class])); return UIApplicationMain( argc, argv, nil, NSStringFromClass( [MPAppDelegate class] ) );
} }
} }