Fix login/result state import/export, keyID; Volto export; Pearl log sink.
This commit is contained in:
parent
b15417aa31
commit
131720eb8d
@ -87,7 +87,10 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
|||||||
|
|
||||||
- (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
- (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
- (void)importLogin:(NSString *)protectedLogin protectedByKey:(MPKey *)importKey
|
||||||
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
- (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker;
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker;
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
||||||
|
@ -73,7 +73,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
return [(id<MPAlgorithm>)other version] == [self version];
|
return [(id<MPAlgorithm>)other version] == [self version];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mpw_perform:(void ( ^ )(void))operationBlock {
|
- (void)mpw_await:(void ( ^ )(void))operationBlock {
|
||||||
|
|
||||||
if ([NSOperationQueue currentQueue] == _mpwQueue) {
|
if ([NSOperationQueue currentQueue] == _mpwQueue) {
|
||||||
operationBlock();
|
operationBlock();
|
||||||
@ -126,7 +126,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||||
|
|
||||||
__block NSData *keyData;
|
__block NSData *keyData;
|
||||||
[self mpw_perform:^{
|
[self mpw_await:^{
|
||||||
NSDate *start = [NSDate date];
|
NSDate *start = [NSDate date];
|
||||||
MPMasterKey masterKey = mpw_master_key( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
MPMasterKey masterKey = mpw_master_key( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
||||||
if (masterKey) {
|
if (masterKey) {
|
||||||
@ -363,7 +363,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
usingKey:(MPKey *)key {
|
usingKey:(MPKey *)key {
|
||||||
|
|
||||||
__block NSString *result = nil;
|
__block NSString *result = nil;
|
||||||
[self mpw_perform:^{
|
[self mpw_await:^{
|
||||||
NSData *masterKey = [key keyForAlgorithm:self];
|
NSData *masterKey = [key keyForAlgorithm:self];
|
||||||
char const *resultBytes = mpw_site_result( masterKey.bytes, name.UTF8String,
|
char const *resultBytes = mpw_site_result( masterKey.bytes, name.UTF8String,
|
||||||
counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
|
counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
|
||||||
@ -392,7 +392,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
|
|
||||||
__block NSData *state = nil;
|
__block NSData *state = nil;
|
||||||
if (plainText)
|
if (plainText)
|
||||||
[self mpw_perform:^{
|
[self mpw_await:^{
|
||||||
NSData *masterKey = [key keyForAlgorithm:self];
|
NSData *masterKey = [key keyForAlgorithm:self];
|
||||||
char const *stateBytes = mpw_site_state( masterKey.bytes, site.name.UTF8String,
|
char const *stateBytes = mpw_site_state( masterKey.bytes, site.name.UTF8String,
|
||||||
MPCounterValueInitial, MPKeyPurposeAuthentication, NULL, site.type, plainText.UTF8String, [self version] );
|
MPCounterValueInitial, MPKeyPurposeAuthentication, NULL, site.type, plainText.UTF8String, [self version] );
|
||||||
@ -589,6 +589,19 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)importLogin:(NSString *)cipherText protectedByKey:(MPKey *)importKey
|
||||||
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
|
if (cipherText && cipherText.length) {
|
||||||
|
NSString *plainText = [self mpwResultForSiteNamed:site.name ofType:MPResultTypeStatefulPersonal parameter:cipherText
|
||||||
|
withCounter:MPCounterValueInitial variant:MPKeyPurposeIdentification context:nil
|
||||||
|
usingKey:importKey];
|
||||||
|
if (plainText)
|
||||||
|
site.loginName = plainText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (NSDictionary *)queryForSite:(MPSiteEntity *)site {
|
- (NSDictionary *)queryForSite:(MPSiteEntity *)site {
|
||||||
|
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
|
||||||
@ -607,6 +620,26 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
return [state?: ((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
return [state?: ((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
|
if (!(site.type & MPSiteFeatureExportContent) || site.loginGenerated || ![site.loginName length])
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
__block NSData *state = nil;
|
||||||
|
[self mpw_await:^{
|
||||||
|
NSData *masterKey = [key keyForAlgorithm:self];
|
||||||
|
char const *stateBytes = mpw_site_state( masterKey.bytes, site.name.UTF8String,
|
||||||
|
MPCounterValueInitial, MPKeyPurposeIdentification, NULL, MPResultTypeStatefulPersonal,
|
||||||
|
site.loginName.UTF8String, [self version] );
|
||||||
|
if (stateBytes) {
|
||||||
|
state = [[NSString stringWithCString:stateBytes encoding:NSUTF8StringEncoding] decodeBase64];
|
||||||
|
mpw_free_string( &stateBytes );
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [state encodeBase64];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker {
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker {
|
||||||
|
|
||||||
if (!(type & MPResultTypeClassTemplate))
|
if (!(type & MPResultTypeClassTemplate))
|
||||||
|
@ -41,6 +41,6 @@
|
|||||||
result:(void ( ^ )(NSError *error))resultBlock;
|
result:(void ( ^ )(NSError *error))resultBlock;
|
||||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||||
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock;
|
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -700,14 +700,16 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
usingKey:(MPKey *)userKey {
|
usingKey:(MPKey *)userKey {
|
||||||
|
|
||||||
site.name = @(importSite->siteName);
|
site.name = @(importSite->siteName);
|
||||||
if (importSite->resultState)
|
site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||||
[site.algorithm importPassword:@(importSite->resultState) protectedByKey:importKey intoSite:site usingKey:userKey];
|
|
||||||
site.type = importSite->resultType;
|
|
||||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
((MPGeneratedSiteEntity *)site).counter = importSite->counter;
|
((MPGeneratedSiteEntity *)site).counter = importSite->counter;
|
||||||
site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
site.type = importSite->resultType;
|
||||||
site.loginName = importSite->loginState? @(importSite->loginState): nil;
|
if (importSite->resultState)
|
||||||
site.loginGenerated = importSite->loginType & MPResultTypeClassTemplate;
|
[site.algorithm importPassword:@(importSite->resultState) protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||||
|
site.loginGenerated = (importSite->loginType & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
|
||||||
|
site.loginName = nil;
|
||||||
|
if (importSite->loginState)
|
||||||
|
[site.algorithm importLogin:@(importSite->loginState) protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||||
site.url = importSite->url? @(importSite->url): nil;
|
site.url = importSite->url? @(importSite->url): nil;
|
||||||
site.uses = importSite->uses;
|
site.uses = importSite->uses;
|
||||||
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
|
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
|
||||||
@ -715,7 +717,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
|
|
||||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||||
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock {
|
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock {
|
||||||
|
|
||||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *user = [self activeUserInContext:context];
|
MPUserEntity *user = [self activeUserInContext:context];
|
||||||
@ -725,6 +727,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
|
mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
|
||||||
exportUser->redacted = !revealPasswords;
|
exportUser->redacted = !revealPasswords;
|
||||||
exportUser->avatar = (unsigned int)user.avatar;
|
exportUser->avatar = (unsigned int)user.avatar;
|
||||||
|
exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
|
||||||
exportUser->defaultType = user.defaultType;
|
exportUser->defaultType = user.defaultType;
|
||||||
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
|
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
|
||||||
|
|
||||||
@ -732,16 +735,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
MPCounterValue counter = MPCounterValueInitial;
|
MPCounterValue counter = MPCounterValueInitial;
|
||||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||||
NSString *content = revealPasswords
|
|
||||||
? [site.algorithm exportPasswordForSite:site usingKey:self.key]
|
|
||||||
: [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
|
||||||
|
|
||||||
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser,
|
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser,
|
||||||
site.name.UTF8String, site.type, counter, site.algorithm.version );
|
site.name.UTF8String, site.type, counter, site.algorithm.version );
|
||||||
exportSite->resultState = content.UTF8String;
|
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:self.key].UTF8String );
|
||||||
exportSite->loginState = site.loginName.UTF8String;
|
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:self.key].UTF8String );
|
||||||
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
|
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
|
||||||
exportSite->url = site.url.UTF8String;
|
exportSite->url = mpw_strdup( site.url.UTF8String );
|
||||||
exportSite->uses = (unsigned int)site.uses;
|
exportSite->uses = (unsigned int)site.uses;
|
||||||
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
||||||
|
|
||||||
@ -750,14 +749,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
}
|
}
|
||||||
|
|
||||||
MPMarshalledFile *exportFile = NULL;
|
MPMarshalledFile *exportFile = NULL;
|
||||||
const char *export = mpw_marshal_write( MPMarshalFormatFlat, &exportFile, exportUser );
|
const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
|
||||||
NSString *mpsites = nil;
|
NSString *exportedUser = nil;
|
||||||
if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
|
if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
|
||||||
mpsites = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
|
exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
|
||||||
mpw_free_string( &export );
|
mpw_free_string( &export );
|
||||||
|
|
||||||
resultBlock( mpsites, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
|
resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
|
||||||
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||||
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
|
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
|
||||||
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
|
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
|
||||||
}] );
|
}] );
|
||||||
|
@ -28,41 +28,11 @@ MP_LIBS_BEGIN
|
|||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
MP_LIBS_END
|
MP_LIBS_END
|
||||||
|
|
||||||
#define mpw_log_os(level, file, line, function, format, ...) \
|
|
||||||
do { \
|
|
||||||
if (mpw_verbosity < level) { \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
switch (level) { \
|
|
||||||
case LogLevelTrace: \
|
|
||||||
os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld TRC | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
case LogLevelDebug: \
|
|
||||||
os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld DBG | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
case LogLevelInfo: \
|
|
||||||
os_log_info( OS_LOG_DEFAULT, "%30s:%-3ld INF | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
case LogLevelWarning: \
|
|
||||||
os_log( OS_LOG_DEFAULT, "%30s:%-3ld WRN | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
case LogLevelError: \
|
|
||||||
os_log_error( OS_LOG_DEFAULT, "%30s:%-3ld ERR | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
case LogLevelFatal: \
|
|
||||||
os_log_fault( OS_LOG_DEFAULT, "%30s:%-3ld FTL | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
mpw_log_sink( level, file, line, function, format, ##__VA_ARGS__ ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
#ifdef __OBJC__
|
||||||
|
|
||||||
#import "Pearl-Prefix.pch"
|
#import "Pearl-Prefix.pch"
|
||||||
|
|
||||||
#define MPW_LOG(level, file, line, function, format, ...) mpw_log_os(level, file, line, function, strf(format, ##__VA_ARGS__))
|
#define MPW_LOG(level, file, line, function, format, ...) mpw_log_sink(level, file, line, function, strf(format, ##__VA_ARGS__))
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
#import "MPTypes.h"
|
#import "MPTypes.h"
|
||||||
@ -72,10 +42,6 @@ MP_LIBS_END
|
|||||||
#import "MPMacConfig.h"
|
#import "MPMacConfig.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define MPW_LOG mpw_log_os
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
@ -479,8 +479,43 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
|
|
||||||
- (IBAction)upgradeVolto:(UIButton *)sender {
|
- (IBAction)upgradeVolto:(UIButton *)sender {
|
||||||
|
|
||||||
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto://"]])
|
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
|
||||||
[UIApp openURL:[[NSURL alloc] initWithString:@"volto://"]];
|
[[MPiOSAppDelegate get] exportSitesRevealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
|
||||||
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
|
||||||
|
message:@"Enter your master password to export the user."
|
||||||
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
|
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||||
|
textField.secureTextEntry = YES;
|
||||||
|
}];
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
|
||||||
|
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
|
||||||
|
^(UIAlertAction *action) { setResult( nil ); }]];
|
||||||
|
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} result:^(NSString *exportedUser, NSError *error) {
|
||||||
|
if (!exportedUser || error) {
|
||||||
|
MPError( error, @"Failed to export user." );
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
|
||||||
|
message:[error localizedDescription]
|
||||||
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
|
||||||
|
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURLComponents *components = [NSURLComponents new];
|
||||||
|
components.scheme = @"volto";
|
||||||
|
components.path = @"import";
|
||||||
|
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
|
||||||
|
[UIApp openURL:components.URL];
|
||||||
|
}];
|
||||||
|
}
|
||||||
else if (self.voltoViewController)
|
else if (self.voltoViewController)
|
||||||
[self presentViewController:self.voltoViewController animated:YES completion:nil];
|
[self presentViewController:self.voltoViewController animated:YES completion:nil];
|
||||||
}
|
}
|
||||||
@ -489,7 +524,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
|
|
||||||
- (void)updateVoltoAlerts {
|
- (void)updateVoltoAlerts {
|
||||||
|
|
||||||
BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto://"]];
|
BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]];
|
||||||
if (voltoInstalled) {
|
if (voltoInstalled) {
|
||||||
self.voltoInstallAlert.visible = NO;
|
self.voltoInstallAlert.visible = NO;
|
||||||
self.voltoMigrateAlert.visible = YES;
|
self.voltoMigrateAlert.visible = YES;
|
||||||
|
@ -33,6 +33,35 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
MPLogSink mpw_log_sink_pearl;
|
||||||
|
void mpw_log_sink_pearl(const MPLogEvent *record) {
|
||||||
|
|
||||||
|
PearlLogLevel level = PearlLogLevelInfo;
|
||||||
|
switch (record->level) {
|
||||||
|
case LogLevelTrace:
|
||||||
|
level = PearlLogLevelDebug;
|
||||||
|
break;
|
||||||
|
case LogLevelDebug:
|
||||||
|
level = PearlLogLevelDebug;
|
||||||
|
break;
|
||||||
|
case LogLevelInfo:
|
||||||
|
level = PearlLogLevelInfo;
|
||||||
|
break;
|
||||||
|
case LogLevelWarning:
|
||||||
|
level = PearlLogLevelWarn;
|
||||||
|
break;
|
||||||
|
case LogLevelError:
|
||||||
|
level = PearlLogLevelError;
|
||||||
|
break;
|
||||||
|
case LogLevelFatal:
|
||||||
|
level = PearlLogLevelFatal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[PearlLogger get] inFile:[@(record->file) lastPathComponent] atLine:record->line fromFunction:@(record->function)
|
||||||
|
withLevel:level text:@(record->message)];
|
||||||
|
}
|
||||||
|
|
||||||
@implementation MPiOSAppDelegate
|
@implementation MPiOSAppDelegate
|
||||||
|
|
||||||
+ (void)initialize {
|
+ (void)initialize {
|
||||||
@ -44,6 +73,9 @@
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
[PearlLogger get].printLevel = PearlLogLevelDebug;
|
[PearlLogger get].printLevel = PearlLogLevelDebug;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
mpw_verbosity = LogLevelTrace;
|
||||||
|
mpw_log_sink_register( &mpw_log_sink_pearl );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,27 +535,31 @@
|
|||||||
|
|
||||||
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
|
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) message:
|
PearlMainQueue( ^{
|
||||||
@"Enter the user's master password to create an export file."
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
message:@"Enter your master password to export the user."
|
||||||
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
textField.secureTextEntry = YES;
|
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||||
}];
|
textField.secureTextEntry = YES;
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
}];
|
||||||
setResult( alert.textFields.firstObject.text );
|
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||||
}]];
|
setResult( alert.textFields.firstObject.text );
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
}]];
|
||||||
setResult( nil );
|
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||||
}]];
|
setResult( nil );
|
||||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
}]];
|
||||||
|
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
} result:^(NSString *mpsites, NSError *error) {
|
} result:^(NSString *exportedUser, NSError *error) {
|
||||||
if (!mpsites || error) {
|
if (!exportedUser || error) {
|
||||||
MPError( error, @"Failed to export mpsites." );
|
MPError( error, @"Failed to export mpsites." );
|
||||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
|
PearlMainQueue( ^{
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
|
||||||
|
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||||
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,11 +591,9 @@
|
|||||||
[PearlInfoPlist get].CFBundleVersion );
|
[PearlInfoPlist get].CFBundleVersion );
|
||||||
|
|
||||||
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
||||||
attachments:[[PearlEMailAttachment alloc]
|
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
|
mimeType:@"text/plain"
|
||||||
mimeType:@"text/plain"
|
fileName:exportFileName], nil];
|
||||||
fileName:exportFileName],
|
|
||||||
nil];
|
|
||||||
return;
|
return;
|
||||||
}]];
|
}]];
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
[alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||||
@ -569,7 +603,7 @@
|
|||||||
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
||||||
URLByAppendingPathComponent:exportFileName isDirectory:NO];
|
URLByAppendingPathComponent:exportFileName isDirectory:NO];
|
||||||
NSError *writeError = nil;
|
NSError *writeError = nil;
|
||||||
if (![[mpsites dataUsingEncoding:NSUTF8StringEncoding]
|
if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
|
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
|
||||||
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
|
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
|
||||||
else {
|
else {
|
||||||
|
@ -9,10 +9,6 @@
|
|||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>mpsites</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFiles</key>
|
<key>CFBundleTypeIconFiles</key>
|
||||||
<array>
|
<array>
|
||||||
<string>Icon-Small</string>
|
<string>Icon-Small</string>
|
||||||
@ -20,7 +16,7 @@
|
|||||||
<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>Alternate</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.lyndir.masterpassword.sites</string>
|
<string>com.lyndir.masterpassword.sites</string>
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
</collectionViewFlowLayout>
|
</collectionViewFlowLayout>
|
||||||
<cells>
|
<cells>
|
||||||
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell">
|
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell">
|
||||||
<rect key="frame" x="80" y="115" width="215" height="667"/>
|
<rect key="frame" x="80" y="114.5" width="215" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user