Fix login/result state import/export, keyID; Volto export; Pearl log sink.
This commit is contained in:
		@@ -87,7 +87,10 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
 | 
			
		||||
 | 
			
		||||
- (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
 | 
			
		||||
              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 *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
 | 
			
		||||
 | 
			
		||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type 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];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)mpw_perform:(void ( ^ )(void))operationBlock {
 | 
			
		||||
- (void)mpw_await:(void ( ^ )(void))operationBlock {
 | 
			
		||||
 | 
			
		||||
    if ([NSOperationQueue currentQueue] == _mpwQueue) {
 | 
			
		||||
        operationBlock();
 | 
			
		||||
@@ -126,7 +126,7 @@ static NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
 | 
			
		||||
 | 
			
		||||
    __block NSData *keyData;
 | 
			
		||||
    [self mpw_perform:^{
 | 
			
		||||
    [self mpw_await:^{
 | 
			
		||||
        NSDate *start = [NSDate date];
 | 
			
		||||
        MPMasterKey masterKey = mpw_master_key( fullName.UTF8String, masterPassword.UTF8String, [self version] );
 | 
			
		||||
        if (masterKey) {
 | 
			
		||||
@@ -363,7 +363,7 @@ static NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
                           usingKey:(MPKey *)key {
 | 
			
		||||
 | 
			
		||||
    __block NSString *result = nil;
 | 
			
		||||
    [self mpw_perform:^{
 | 
			
		||||
    [self mpw_await:^{
 | 
			
		||||
        NSData *masterKey = [key keyForAlgorithm:self];
 | 
			
		||||
        char const *resultBytes = mpw_site_result( masterKey.bytes, name.UTF8String,
 | 
			
		||||
                counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
 | 
			
		||||
@@ -392,7 +392,7 @@ static NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
 | 
			
		||||
    __block NSData *state = nil;
 | 
			
		||||
    if (plainText)
 | 
			
		||||
        [self mpw_perform:^{
 | 
			
		||||
        [self mpw_await:^{
 | 
			
		||||
            NSData *masterKey = [key keyForAlgorithm:self];
 | 
			
		||||
            char const *stateBytes = mpw_site_state( masterKey.bytes, site.name.UTF8String,
 | 
			
		||||
                    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 {
 | 
			
		||||
 | 
			
		||||
    return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
 | 
			
		||||
@@ -607,6 +620,26 @@ static NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
    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 {
 | 
			
		||||
 | 
			
		||||
    if (!(type & MPResultTypeClassTemplate))
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,6 @@
 | 
			
		||||
                       result:(void ( ^ )(NSError *error))resultBlock;
 | 
			
		||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
 | 
			
		||||
                 askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
 | 
			
		||||
                            result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock;
 | 
			
		||||
                            result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 
 | 
			
		||||
@@ -700,14 +700,16 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
          usingKey:(MPKey *)userKey {
 | 
			
		||||
 | 
			
		||||
    site.name = @(importSite->siteName);
 | 
			
		||||
    if (importSite->resultState)
 | 
			
		||||
        [site.algorithm importPassword:@(importSite->resultState) protectedByKey:importKey intoSite:site usingKey:userKey];
 | 
			
		||||
    site.type = importSite->resultType;
 | 
			
		||||
    site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
 | 
			
		||||
    if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
 | 
			
		||||
        ((MPGeneratedSiteEntity *)site).counter = importSite->counter;
 | 
			
		||||
    site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
 | 
			
		||||
    site.loginName = importSite->loginState? @(importSite->loginState): nil;
 | 
			
		||||
    site.loginGenerated = importSite->loginType & MPResultTypeClassTemplate;
 | 
			
		||||
    site.type = importSite->resultType;
 | 
			
		||||
    if (importSite->resultState)
 | 
			
		||||
        [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.uses = importSite->uses;
 | 
			
		||||
    site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
 | 
			
		||||
@@ -715,7 +717,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
 | 
			
		||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
 | 
			
		||||
                 askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
 | 
			
		||||
                            result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock {
 | 
			
		||||
                            result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock {
 | 
			
		||||
 | 
			
		||||
    [MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
			
		||||
        MPUserEntity *user = [self activeUserInContext:context];
 | 
			
		||||
@@ -725,6 +727,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
                mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
 | 
			
		||||
        exportUser->redacted = !revealPasswords;
 | 
			
		||||
        exportUser->avatar = (unsigned int)user.avatar;
 | 
			
		||||
        exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
 | 
			
		||||
        exportUser->defaultType = user.defaultType;
 | 
			
		||||
        exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
 | 
			
		||||
 | 
			
		||||
@@ -732,16 +735,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
            MPCounterValue counter = MPCounterValueInitial;
 | 
			
		||||
            if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
 | 
			
		||||
                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,
 | 
			
		||||
                    site.name.UTF8String, site.type, counter, site.algorithm.version );
 | 
			
		||||
            exportSite->resultState = content.UTF8String;
 | 
			
		||||
            exportSite->loginState = site.loginName.UTF8String;
 | 
			
		||||
            exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:self.key].UTF8String );
 | 
			
		||||
            exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:self.key].UTF8String );
 | 
			
		||||
            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->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
 | 
			
		||||
 | 
			
		||||
@@ -750,14 +749,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MPMarshalledFile *exportFile = NULL;
 | 
			
		||||
        const char *export = mpw_marshal_write( MPMarshalFormatFlat, &exportFile, exportUser );
 | 
			
		||||
        NSString *mpsites = nil;
 | 
			
		||||
        const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
 | 
			
		||||
        NSString *exportedUser = nil;
 | 
			
		||||
        if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
 | 
			
		||||
            mpsites = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
 | 
			
		||||
            exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
 | 
			
		||||
        mpw_free_string( &export );
 | 
			
		||||
 | 
			
		||||
        resultBlock( mpsites, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
 | 
			
		||||
                              [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
 | 
			
		||||
        resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
 | 
			
		||||
                                   [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
 | 
			
		||||
                                      @"type"                  : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
 | 
			
		||||
                                      NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
 | 
			
		||||
                              }] );
 | 
			
		||||
 
 | 
			
		||||
@@ -28,41 +28,11 @@ MP_LIBS_BEGIN
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
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__
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#import "MPTypes.h"
 | 
			
		||||
@@ -72,10 +42,6 @@ MP_LIBS_END
 | 
			
		||||
#import "MPMacConfig.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#define MPW_LOG mpw_log_os
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "mpw-util.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -479,8 +479,43 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
			
		||||
 | 
			
		||||
- (IBAction)upgradeVolto:(UIButton *)sender {
 | 
			
		||||
 | 
			
		||||
    if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto://"]])
 | 
			
		||||
        [UIApp openURL:[[NSURL alloc] initWithString:@"volto://"]];
 | 
			
		||||
    if ([UIApp canOpenURL:[[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)
 | 
			
		||||
        [self presentViewController:self.voltoViewController animated:YES completion:nil];
 | 
			
		||||
}
 | 
			
		||||
@@ -489,7 +524,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
			
		||||
 | 
			
		||||
- (void)updateVoltoAlerts {
 | 
			
		||||
 | 
			
		||||
    BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto://"]];
 | 
			
		||||
    BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]];
 | 
			
		||||
    if (voltoInstalled) {
 | 
			
		||||
        self.voltoInstallAlert.visible = NO;
 | 
			
		||||
        self.voltoMigrateAlert.visible = YES;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,35 @@
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
 | 
			
		||||
+ (void)initialize {
 | 
			
		||||
@@ -44,6 +73,9 @@
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
        [PearlLogger get].printLevel = PearlLogLevelDebug;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        mpw_verbosity = LogLevelTrace;
 | 
			
		||||
        mpw_log_sink_register( &mpw_log_sink_pearl );
 | 
			
		||||
    } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -503,27 +535,31 @@
 | 
			
		||||
 | 
			
		||||
    [self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
 | 
			
		||||
        return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) message:
 | 
			
		||||
                            @"Enter the user's master password to create an export file."
 | 
			
		||||
                                                                    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];
 | 
			
		||||
            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 *mpsites, NSError *error) {
 | 
			
		||||
        if (!mpsites || error) {
 | 
			
		||||
    }                         result:^(NSString *exportedUser, NSError *error) {
 | 
			
		||||
        if (!exportedUser || error) {
 | 
			
		||||
            MPError( error, @"Failed to export mpsites." );
 | 
			
		||||
            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];
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -555,11 +591,9 @@
 | 
			
		||||
                        [PearlInfoPlist get].CFBundleVersion );
 | 
			
		||||
 | 
			
		||||
            [PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
 | 
			
		||||
                        attachments:[[PearlEMailAttachment alloc]
 | 
			
		||||
                                            initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
 | 
			
		||||
                                                   mimeType:@"text/plain"
 | 
			
		||||
                                                   fileName:exportFileName],
 | 
			
		||||
                                    nil];
 | 
			
		||||
                        attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
 | 
			
		||||
                                                                         mimeType:@"text/plain"
 | 
			
		||||
                                                                         fileName:exportFileName], nil];
 | 
			
		||||
            return;
 | 
			
		||||
        }]];
 | 
			
		||||
        [alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
			
		||||
@@ -569,7 +603,7 @@
 | 
			
		||||
                    URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
 | 
			
		||||
                    URLByAppendingPathComponent:exportFileName isDirectory:NO];
 | 
			
		||||
            NSError *writeError = nil;
 | 
			
		||||
            if (![[mpsites dataUsingEncoding:NSUTF8StringEncoding]
 | 
			
		||||
            if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
 | 
			
		||||
                    writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
 | 
			
		||||
                MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
 | 
			
		||||
            else {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,10 +9,6 @@
 | 
			
		||||
	<key>CFBundleDocumentTypes</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<dict>
 | 
			
		||||
			<key>CFBundleTypeExtensions</key>
 | 
			
		||||
			<array>
 | 
			
		||||
				<string>mpsites</string>
 | 
			
		||||
			</array>
 | 
			
		||||
			<key>CFBundleTypeIconFiles</key>
 | 
			
		||||
			<array>
 | 
			
		||||
				<string>Icon-Small</string>
 | 
			
		||||
@@ -20,7 +16,7 @@
 | 
			
		||||
			<key>CFBundleTypeName</key>
 | 
			
		||||
			<string>Master Password sites</string>
 | 
			
		||||
			<key>LSHandlerRank</key>
 | 
			
		||||
			<string>Owner</string>
 | 
			
		||||
			<string>Alternate</string>
 | 
			
		||||
			<key>LSItemContentTypes</key>
 | 
			
		||||
			<array>
 | 
			
		||||
				<string>com.lyndir.masterpassword.sites</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@
 | 
			
		||||
                                        </collectionViewFlowLayout>
 | 
			
		||||
                                        <cells>
 | 
			
		||||
                                            <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"/>
 | 
			
		||||
                                                <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
 | 
			
		||||
                                                    <rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user