Enable support for internal actions from URLs.
This commit is contained in:
		@@ -39,8 +39,9 @@
 | 
				
			|||||||
            askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
 | 
					            askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
 | 
				
			||||||
              askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
 | 
					              askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
 | 
				
			||||||
                       result:(void ( ^ )(NSError *error))resultBlock;
 | 
					                       result:(void ( ^ )(NSError *error))resultBlock;
 | 
				
			||||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
 | 
					- (NSString *)exportSitesFor:(MPUserEntity *)user
 | 
				
			||||||
                 askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
 | 
					             revealPasswords:(BOOL)revealPasswords
 | 
				
			||||||
                            result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock;
 | 
					           askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
 | 
				
			||||||
 | 
					                       error:(__autoreleasing NSError **)error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -704,16 +704,17 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
				
			|||||||
    site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
 | 
					    site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
 | 
					- (NSString *)exportSitesFor:(MPUserEntity *)user
 | 
				
			||||||
                 askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
 | 
					             revealPasswords:(BOOL)revealPasswords
 | 
				
			||||||
                            result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock {
 | 
					           askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
 | 
				
			||||||
 | 
					                       error:(__autoreleasing NSError **)error {
 | 
				
			||||||
    [MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
					 | 
				
			||||||
        MPUserEntity *user = [self activeUserInContext:context];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MPMarshalledUser *exportUser = NULL;
 | 
				
			||||||
 | 
					    MPMarshalledFile *exportFile = NULL;
 | 
				
			||||||
 | 
					    @try {
 | 
				
			||||||
        inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
 | 
					        inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
 | 
				
			||||||
        MPMarshalledUser *exportUser = mpw_marshal_user( user.name.UTF8String,
 | 
					        exportUser = mpw_marshal_user( user.name.UTF8String,
 | 
				
			||||||
                mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
 | 
					                mpw_masterKeyProvider_str( askExportPassword( 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->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
 | 
				
			||||||
@@ -737,22 +738,26 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
				
			|||||||
                mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
 | 
					                mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MPMarshalledFile *exportFile = NULL;
 | 
					 | 
				
			||||||
        const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
 | 
					        const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
 | 
				
			||||||
        NSString *exportedUser = nil;
 | 
					        NSString *exportedUser = nil;
 | 
				
			||||||
        if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
 | 
					        if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
 | 
				
			||||||
            exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
 | 
					            exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
 | 
				
			||||||
        mpw_free_string( &export );
 | 
					        mpw_free_string( &export );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
 | 
					        if (error)
 | 
				
			||||||
                                   [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
 | 
					            *error = exportFile && exportFile->error.type == MPMarshalSuccess? nil:
 | 
				
			||||||
                                      @"type"                  : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
 | 
					                     [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
 | 
				
			||||||
                                      NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
 | 
					                             @"type"                  : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
 | 
				
			||||||
                              }] );
 | 
					                             NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
 | 
				
			||||||
 | 
					                     }];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return exportedUser;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @finally {
 | 
				
			||||||
        mpw_marshal_file_free( &exportFile );
 | 
					        mpw_marshal_file_free( &exportFile );
 | 
				
			||||||
        mpw_marshal_user_free( &exportUser );
 | 
					        mpw_marshal_user_free( &exportUser );
 | 
				
			||||||
        mpw_masterKeyProvider_free();
 | 
					        mpw_masterKeyProvider_free();
 | 
				
			||||||
    }];
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#import "MPNavigationController.h"
 | 
					#import "MPNavigationController.h"
 | 
				
			||||||
#import "MPWebViewController.h"
 | 
					#import "MPWebViewController.h"
 | 
				
			||||||
 | 
					#import "MPiOSAppDelegate.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@implementation MPNavigationController
 | 
					@implementation MPNavigationController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,6 +30,16 @@
 | 
				
			|||||||
        [self performSegueWithIdentifier:@"setup" sender:self];
 | 
					        [self performSegueWithIdentifier:@"setup" sender:self];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ([identifier isEqualToString:@"web"] && [[(NSURL *)sender scheme] isEqualToString:@"masterpassword"]) {
 | 
				
			||||||
 | 
					        [[MPiOSAppDelegate get] openURL:sender];
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [super performSegueWithIdentifier:identifier sender:sender];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 | 
					- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ([segue.identifier isEqualToString:@"web"])
 | 
					    if ([segue.identifier isEqualToString:@"web"])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,9 +33,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
    MPPasswordsBadNameTip = 1 << 0,
 | 
					    MPPasswordsBadNameTip = 1 << 0,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate, SKStoreProductViewControllerDelegate>
 | 
					@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
 | 
					 | 
				
			||||||
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
 | 
					@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
 | 
				
			||||||
@property(nonatomic, strong) NSArray *fuzzyGroups;
 | 
					@property(nonatomic, strong) NSArray *fuzzyGroups;
 | 
				
			||||||
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
 | 
					@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
 | 
				
			||||||
@@ -435,13 +434,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
    }                completion:completion];
 | 
					    }                completion:completion];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma mark - SKStoreProductViewControllerDelegate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [viewController dismissViewControllerAnimated:YES completion:nil];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma mark - Actions
 | 
					#pragma mark - Actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (IBAction)dismissPopdown:(id)sender {
 | 
					- (IBAction)dismissPopdown:(id)sender {
 | 
				
			||||||
@@ -454,45 +446,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- (IBAction)upgradeVolto:(UIButton *)sender {
 | 
					- (IBAction)upgradeVolto:(UIButton *)sender {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
 | 
					    [[MPiOSAppDelegate get] migrateFor:[MPiOSAppDelegate get].activeUserForMainThread];
 | 
				
			||||||
        [[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];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma mark - Private
 | 
					#pragma mark - Private
 | 
				
			||||||
@@ -505,23 +459,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
        self.voltoMigrateAlert.visible = YES;
 | 
					        self.voltoMigrateAlert.visible = YES;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
        self.voltoInstallAlert.visible = NO;
 | 
					        self.voltoInstallAlert.visible = [MPiOSAppDelegate get].voltoViewController != nil;
 | 
				
			||||||
        self.voltoMigrateAlert.visible = NO;
 | 
					        self.voltoMigrateAlert.visible = NO;
 | 
				
			||||||
        self.voltoViewController = [SKStoreProductViewController new];
 | 
					 | 
				
			||||||
        self.voltoViewController.delegate = self;
 | 
					 | 
				
			||||||
        [self.voltoViewController loadProductWithParameters:@{
 | 
					 | 
				
			||||||
                SKStoreProductParameterCampaignToken       : @"app-masterpassword.ios", /* Campaign:    From MasterPassword iOS */
 | 
					 | 
				
			||||||
                SKStoreProductParameterProviderToken       : @153897, /*                   Provider:    Maarten Billemont */
 | 
					 | 
				
			||||||
                SKStoreProductParameterITunesItemIdentifier: @510296984, /*                Application: MasterPassword iOS */
 | 
					 | 
				
			||||||
                //SKStoreProductParameterITunesItemIdentifier: @1500430196, /*             Application: Volto iOS */
 | 
					 | 
				
			||||||
        }                                   completionBlock:^(BOOL result, NSError *error) {
 | 
					 | 
				
			||||||
            if (error)
 | 
					 | 
				
			||||||
                err( @"Failed loading Volto product information: %@", error );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            [UIView animateWithDuration:0.3f animations:^{
 | 
					 | 
				
			||||||
                self.voltoInstallAlert.visible = result;
 | 
					 | 
				
			||||||
            }];
 | 
					 | 
				
			||||||
        }];
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@
 | 
				
			|||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#import "MPWebViewController.h"
 | 
					#import "MPWebViewController.h"
 | 
				
			||||||
 | 
					#import "MPiOSAppDelegate.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@implementation MPWebViewController
 | 
					@implementation MPWebViewController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,6 +58,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma mark - WKNavigationDelegate
 | 
					#pragma mark - WKNavigationDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
 | 
				
			||||||
 | 
					decisionHandler:(void ( ^ )(WKNavigationActionPolicy))decisionHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ([navigationAction.request.mainDocumentURL.scheme isEqualToString:@"masterpassword"]) {
 | 
				
			||||||
 | 
					        [[MPiOSAppDelegate get] openURL:navigationAction.request.mainDocumentURL];
 | 
				
			||||||
 | 
					        decisionHandler( WKNavigationActionPolicyCancel );
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    decisionHandler( WKNavigationActionPolicyAllow );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
 | 
					- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self.webNavigationItem.title = webView.URL.host;
 | 
					    self.webNavigationItem.title = webView.URL.host;
 | 
				
			||||||
@@ -77,7 +90,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [self.webNavigationItem setLeftBarButtonItem:[[UIBarButtonItem alloc]
 | 
					    [self.webNavigationItem setLeftBarButtonItem:[[UIBarButtonItem alloc]
 | 
				
			||||||
            initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(openURL:)]];
 | 
					            initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector( action: )]];
 | 
				
			||||||
    [webView evaluateJavaScript:@"document.title" completionHandler:^(id o, NSError *error) {
 | 
					    [webView evaluateJavaScript:@"document.title" completionHandler:^(id o, NSError *error) {
 | 
				
			||||||
        self.webNavigationItem.prompt = [o description];
 | 
					        self.webNavigationItem.prompt = [o description];
 | 
				
			||||||
    }];
 | 
					    }];
 | 
				
			||||||
@@ -85,7 +98,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma mark - Actions
 | 
					#pragma mark - Actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (IBAction)openURL:(id)sender {
 | 
					- (IBAction)action:(id)sender {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    UIAlertController *controller = [UIAlertController new];
 | 
					    UIAlertController *controller = [UIAlertController new];
 | 
				
			||||||
    controller.title = self.webView.URL.host;
 | 
					    controller.title = self.webView.URL.host;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,15 +17,22 @@
 | 
				
			|||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#import <UIKit/UIKit.h>
 | 
					#import <UIKit/UIKit.h>
 | 
				
			||||||
 | 
					#import <StoreKit/StoreKit.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#import "MPAppDelegate_Shared.h"
 | 
					#import "MPAppDelegate_Shared.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@interface MPiOSAppDelegate : MPAppDelegate_Shared
 | 
					@interface MPiOSAppDelegate : MPAppDelegate_Shared <SKStoreProductViewControllerDelegate>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)openURL:(NSURL *)url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
 | 
					- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
 | 
				
			||||||
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
 | 
					- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)showExportForVC:(UIViewController *)viewController;
 | 
					- (void)showExportForVC:(UIViewController *)viewController;
 | 
				
			||||||
 | 
					- (void)migrateFor:(MPUserEntity *)user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset;
 | 
					- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
@implementation CountlyPushNotifications(MPNotifications)
 | 
					@implementation CountlyPushNotifications(MPNotifications)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)openURL:(NSString *)URLString {
 | 
					- (void)openURL:(NSString *)URLString {
 | 
				
			||||||
    [UIApp.keyWindow.rootViewController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:URLString]];
 | 
					    [[MPiOSAppDelegate get].navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:URLString]];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end
 | 
					@end
 | 
				
			||||||
@@ -186,7 +186,26 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SKStoreProductViewController *migrateVC = [SKStoreProductViewController new];
 | 
				
			||||||
 | 
					        [migrateVC loadProductWithParameters:@{
 | 
				
			||||||
 | 
					                SKStoreProductParameterCampaignToken       : @"app-masterpassword.ios", /* Campaign:    From MasterPassword iOS */
 | 
				
			||||||
 | 
					                SKStoreProductParameterProviderToken       : @153897, /*                   Provider:    Maarten Billemont */
 | 
				
			||||||
 | 
					                SKStoreProductParameterITunesItemIdentifier: @510296984, /*                Application: MasterPassword iOS */
 | 
				
			||||||
 | 
					                //SKStoreProductParameterITunesItemIdentifier: @1500430196, /*             Application: Volto iOS */
 | 
				
			||||||
 | 
					        }                                   completionBlock:^(BOOL result, NSError *error) {
 | 
				
			||||||
 | 
					            if (error)
 | 
				
			||||||
 | 
					                err( @"Failed loading Volto product information: %@", error );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (result) {
 | 
				
			||||||
 | 
					                    self.voltoViewController = migrateVC;
 | 
				
			||||||
 | 
					                    self.voltoViewController.delegate = self;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    self.voltoViewController = nil;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        PearlMainQueueOperation( ^{
 | 
					        PearlMainQueueOperation( ^{
 | 
				
			||||||
 | 
					            [self.navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:@"masterpassword://foo?bar=quux"]];
 | 
				
			||||||
            if ([[MPiOSConfig get].showSetup boolValue])
 | 
					            if ([[MPiOSConfig get].showSetup boolValue])
 | 
				
			||||||
                [self.navigationController performSegueWithIdentifier:@"setup" sender:self];
 | 
					                [self.navigationController performSegueWithIdentifier:@"setup" sender:self];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -207,6 +226,12 @@
 | 
				
			|||||||
    if (!url)
 | 
					    if (!url)
 | 
				
			||||||
        return NO;
 | 
					        return NO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // masterpassword: URLs.
 | 
				
			||||||
 | 
					    if ([url.scheme isEqualToString:@"masterpassword"]) {
 | 
				
			||||||
 | 
					        [self openURL:url];
 | 
				
			||||||
 | 
					        return YES;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Arbitrary URL to mpsites data.
 | 
					    // Arbitrary URL to mpsites data.
 | 
				
			||||||
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
 | 
					    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
 | 
				
			||||||
            ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
 | 
					            ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
 | 
				
			||||||
@@ -444,6 +469,42 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma mark - Behavior
 | 
					#pragma mark - Behavior
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)openURL:(NSURL *)url {
 | 
				
			||||||
 | 
					    if ([url.scheme isEqualToString:@"masterpassword"]) {
 | 
				
			||||||
 | 
					        if ([url.host isEqualToString:@"open-url"]) {
 | 
				
			||||||
 | 
					            for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
 | 
				
			||||||
 | 
					                if ([item.name isEqualToString:@"url"]) {
 | 
				
			||||||
 | 
					                    [UIApp openURL:[NSURL URLWithString:item.value]];
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if ([url.host isEqualToString:@"show-url"]) {
 | 
				
			||||||
 | 
					            for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
 | 
				
			||||||
 | 
					                if ([item.name isEqualToString:@"url"]) {
 | 
				
			||||||
 | 
					                    [[MPiOSAppDelegate get].navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:item.value]];
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if ([url.host isEqualToString:@"migrate"]) {
 | 
				
			||||||
 | 
					            for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
 | 
				
			||||||
 | 
					                if ([item.name isEqualToString:@"fullName"]) {
 | 
				
			||||||
 | 
					                    [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
				
			||||||
 | 
					                        NSFetchRequest
 | 
				
			||||||
 | 
					                                *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
 | 
				
			||||||
 | 
					                        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", item.value];
 | 
				
			||||||
 | 
					                        NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
 | 
				
			||||||
 | 
					                        [self migrateFor:users.firstObject];
 | 
				
			||||||
 | 
					                    }];
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [self migrateFor:nil];
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else
 | 
				
			||||||
 | 
					        [UIApp openURL:url];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
 | 
					- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (![PearlEMail canSendMail]) {
 | 
					    if (![PearlEMail canSendMail]) {
 | 
				
			||||||
@@ -564,89 +625,162 @@
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
 | 
					    [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
				
			||||||
        return PearlAwait( ^(void (^setResult)(id)) {
 | 
					        NSError *error = nil;
 | 
				
			||||||
            PearlMainQueue( ^{
 | 
					        NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
 | 
				
			||||||
                UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
 | 
					            return PearlAwait( ^(void (^setResult)(id)) {
 | 
				
			||||||
                                                                               message:@"Enter your master password to export the user."
 | 
					                PearlMainQueue( ^{
 | 
				
			||||||
                                                                        preferredStyle:UIAlertControllerStyleAlert];
 | 
					                    UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
 | 
				
			||||||
                [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
 | 
					                                                                                   message:@"Enter your master password to export the user."
 | 
				
			||||||
                    textField.secureTextEntry = YES;
 | 
					                                                                            preferredStyle:UIAlertControllerStyleAlert];
 | 
				
			||||||
                }];
 | 
					                    [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
 | 
				
			||||||
                [alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
					                        textField.secureTextEntry = YES;
 | 
				
			||||||
                    setResult( alert.textFields.firstObject.text );
 | 
					                    }];
 | 
				
			||||||
                }]];
 | 
					                    [alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
				
			||||||
                [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
 | 
					                        setResult( alert.textFields.firstObject.text );
 | 
				
			||||||
                    setResult( nil );
 | 
					                    }]];
 | 
				
			||||||
                }]];
 | 
					                    [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
 | 
				
			||||||
                [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
					                        setResult( nil );
 | 
				
			||||||
 | 
					                    }]];
 | 
				
			||||||
 | 
					                    [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
				
			||||||
 | 
					                } );
 | 
				
			||||||
            } );
 | 
					            } );
 | 
				
			||||||
        } );
 | 
					        } error:&error];
 | 
				
			||||||
    }                         result:^(NSString *exportedUser, NSError *error) {
 | 
					
 | 
				
			||||||
        if (!exportedUser || error) {
 | 
					        PearlMainQueue( ^{
 | 
				
			||||||
            MPError( error, @"Failed to export mpsites." );
 | 
					            if (!exportedUser || error) {
 | 
				
			||||||
            PearlMainQueue( ^{
 | 
					                MPError( error, @"Failed to export mpsites." );
 | 
				
			||||||
                UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
 | 
					                UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
 | 
				
			||||||
                                                                        preferredStyle:UIAlertControllerStyleAlert];
 | 
					                                                                        preferredStyle:UIAlertControllerStyleAlert];
 | 
				
			||||||
                [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
 | 
					                [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
 | 
				
			||||||
                [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
					                [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
				
			||||||
            } );
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
 | 
				
			||||||
 | 
					            [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
 | 
				
			||||||
 | 
					            NSString *exportFileName = strf( @"%@ (%@).mpsites",
 | 
				
			||||||
 | 
					                    [self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
 | 
				
			||||||
 | 
					                                                                    preferredStyle:UIAlertControllerStyleActionSheet];
 | 
				
			||||||
 | 
					            [alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
				
			||||||
 | 
					                NSString *message;
 | 
				
			||||||
 | 
					                if (revealPasswords)
 | 
				
			||||||
 | 
					                    message = strf( @"Export of Master Password sites with passwords included.\n\n"
 | 
				
			||||||
 | 
					                                    @"REMINDER: Make sure nobody else sees this file!  Passwords are visible!\n\n\n"
 | 
				
			||||||
 | 
					                                    @"--\n"
 | 
				
			||||||
 | 
					                                    @"%@\n"
 | 
				
			||||||
 | 
					                                    @"Master Password %@, build %@",
 | 
				
			||||||
 | 
					                            [self activeUserForMainThread].name,
 | 
				
			||||||
 | 
					                            [PearlInfoPlist get].CFBundleShortVersionString,
 | 
				
			||||||
 | 
					                            [PearlInfoPlist get].CFBundleVersion );
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    message = strf( @"Backup of Master Password sites.\n\n\n"
 | 
				
			||||||
 | 
					                                    @"--\n"
 | 
				
			||||||
 | 
					                                    @"%@\n"
 | 
				
			||||||
 | 
					                                    @"Master Password %@, build %@",
 | 
				
			||||||
 | 
					                            [self activeUserForMainThread].name,
 | 
				
			||||||
 | 
					                            [PearlInfoPlist get].CFBundleShortVersionString,
 | 
				
			||||||
 | 
					                            [PearlInfoPlist get].CFBundleVersion );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                [PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
 | 
				
			||||||
 | 
					                            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) {
 | 
				
			||||||
 | 
					                NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
 | 
				
			||||||
 | 
					                                                                                       inDomains:NSUserDomainMask] lastObject];
 | 
				
			||||||
 | 
					                NSURL *exportURL = [[applicationSupportURL
 | 
				
			||||||
 | 
					                        URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
 | 
				
			||||||
 | 
					                        URLByAppendingPathComponent:exportFileName isDirectory:NO];
 | 
				
			||||||
 | 
					                NSError *writeError = nil;
 | 
				
			||||||
 | 
					                if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
 | 
				
			||||||
 | 
					                        writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
 | 
				
			||||||
 | 
					                    MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
 | 
				
			||||||
 | 
					                    self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
 | 
				
			||||||
 | 
					                    self.interactionController.delegate = self;
 | 
				
			||||||
 | 
					                    [self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }]];
 | 
				
			||||||
 | 
					            [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
 | 
				
			||||||
 | 
					            [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
				
			||||||
 | 
					        } );
 | 
				
			||||||
 | 
					    }];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)migrateFor:(MPUserEntity *)user {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
 | 
				
			||||||
 | 
					        if (!user) {
 | 
				
			||||||
 | 
					            [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
				
			||||||
 | 
					                NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
 | 
				
			||||||
 | 
					                NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
 | 
				
			||||||
 | 
					                if (![users count])
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                UIAlertController *usersSheet = [UIAlertController alertControllerWithTitle:@"Migrate User"
 | 
				
			||||||
 | 
					                                                                                    message:@"Choose a user to migrate out to Volto."
 | 
				
			||||||
 | 
					                                                                             preferredStyle:UIAlertControllerStyleActionSheet];
 | 
				
			||||||
 | 
					                [usersSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
 | 
				
			||||||
 | 
					                for (MPUserEntity *user_ in users)
 | 
				
			||||||
 | 
					                    [usersSheet addAction:[UIAlertAction actionWithTitle:user_.name style:UIAlertActionStyleDefault handler:
 | 
				
			||||||
 | 
					                            ^(UIAlertAction *action) { [self migrateFor:user_]; }]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                PearlMainQueue( ^{
 | 
				
			||||||
 | 
					                    [self.navigationController presentViewController:usersSheet animated:YES completion:nil];
 | 
				
			||||||
 | 
					                } );
 | 
				
			||||||
 | 
					            }];
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
 | 
					        [MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
				
			||||||
        [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
 | 
					            NSError *error = nil;
 | 
				
			||||||
        NSString *exportFileName = strf( @"%@ (%@).mpsites",
 | 
					            NSString *exportedUser = [[MPAppDelegate_Shared get] exportSitesFor:[MPUserEntity existingObjectWithID:user.objectID inContext:context]
 | 
				
			||||||
                [self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
 | 
					                                                                revealPasswords: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];
 | 
				
			||||||
 | 
					                            } );
 | 
				
			||||||
 | 
					                        } );
 | 
				
			||||||
 | 
					                    } error:&error];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
 | 
					            PearlMainQueue( ^{
 | 
				
			||||||
                                                                preferredStyle:UIAlertControllerStyleActionSheet];
 | 
					                if (!exportedUser || error) {
 | 
				
			||||||
        [alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
					                    MPError( error, @"Failed to export user." );
 | 
				
			||||||
            NSString *message;
 | 
					                    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
 | 
				
			||||||
            if (revealPasswords)
 | 
					                                                                                   message:[error localizedDescription]
 | 
				
			||||||
                message = strf( @"Export of Master Password sites with passwords included.\n\n"
 | 
					                                                                            preferredStyle:UIAlertControllerStyleAlert];
 | 
				
			||||||
                                @"REMINDER: Make sure nobody else sees this file!  Passwords are visible!\n\n\n"
 | 
					                    [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
 | 
				
			||||||
                                @"--\n"
 | 
					                    [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
				
			||||||
                                @"%@\n"
 | 
					                    return;
 | 
				
			||||||
                                @"Master Password %@, build %@",
 | 
					                }
 | 
				
			||||||
                        [self activeUserForMainThread].name,
 | 
					 | 
				
			||||||
                        [PearlInfoPlist get].CFBundleShortVersionString,
 | 
					 | 
				
			||||||
                        [PearlInfoPlist get].CFBundleVersion );
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                message = strf( @"Backup of Master Password sites.\n\n\n"
 | 
					 | 
				
			||||||
                                @"--\n"
 | 
					 | 
				
			||||||
                                @"%@\n"
 | 
					 | 
				
			||||||
                                @"Master Password %@, build %@",
 | 
					 | 
				
			||||||
                        [self activeUserForMainThread].name,
 | 
					 | 
				
			||||||
                        [PearlInfoPlist get].CFBundleShortVersionString,
 | 
					 | 
				
			||||||
                        [PearlInfoPlist get].CFBundleVersion );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
 | 
					                NSURLComponents *components = [NSURLComponents new];
 | 
				
			||||||
                        attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
 | 
					                components.scheme = @"volto";
 | 
				
			||||||
                                                                         mimeType:@"text/plain"
 | 
					                components.path = @"import";
 | 
				
			||||||
                                                                         fileName:exportFileName], nil];
 | 
					                components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
 | 
				
			||||||
            return;
 | 
					                [UIApp openURL:components.URL];
 | 
				
			||||||
        }]];
 | 
					            } );
 | 
				
			||||||
        [alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
 | 
					        }];
 | 
				
			||||||
            NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
 | 
					    }
 | 
				
			||||||
                                                                                   inDomains:NSUserDomainMask] lastObject];
 | 
					
 | 
				
			||||||
            NSURL *exportURL = [[applicationSupportURL
 | 
					    else if (self.voltoViewController)
 | 
				
			||||||
                    URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
 | 
					        [self.navigationController presentViewController:self.voltoViewController animated:YES completion:nil];
 | 
				
			||||||
                    URLByAppendingPathComponent:exportFileName isDirectory:NO];
 | 
					 | 
				
			||||||
            NSError *writeError = nil;
 | 
					 | 
				
			||||||
            if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
 | 
					 | 
				
			||||||
                    writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
 | 
					 | 
				
			||||||
                MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
 | 
					 | 
				
			||||||
                self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
 | 
					 | 
				
			||||||
                self.interactionController.delegate = self;
 | 
					 | 
				
			||||||
                [self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }]];
 | 
					 | 
				
			||||||
        [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
 | 
					 | 
				
			||||||
        [self.navigationController presentViewController:alert animated:YES completion:nil];
 | 
					 | 
				
			||||||
    }];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
 | 
					- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
 | 
				
			||||||
@@ -674,6 +808,13 @@
 | 
				
			|||||||
    } );
 | 
					    } );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma mark - SKStoreProductViewControllerDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [viewController dismissViewControllerAnimated:YES completion:nil];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma mark - UIDocumentInteractionControllerDelegate
 | 
					#pragma mark - UIDocumentInteractionControllerDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
 | 
					- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,21 @@
 | 
				
			|||||||
	<string>APPL</string>
 | 
						<string>APPL</string>
 | 
				
			||||||
	<key>CFBundleShortVersionString</key>
 | 
						<key>CFBundleShortVersionString</key>
 | 
				
			||||||
	<string>[auto]</string>
 | 
						<string>[auto]</string>
 | 
				
			||||||
 | 
						<key>CFBundleURLTypes</key>
 | 
				
			||||||
 | 
						<array>
 | 
				
			||||||
 | 
							<dict>
 | 
				
			||||||
 | 
								<key>CFBundleTypeRole</key>
 | 
				
			||||||
 | 
								<string>Editor</string>
 | 
				
			||||||
 | 
								<key>CFBundleURLIconFile</key>
 | 
				
			||||||
 | 
								<string>Icon-320</string>
 | 
				
			||||||
 | 
								<key>CFBundleURLName</key>
 | 
				
			||||||
 | 
								<string>com.lyndir.masterpassword</string>
 | 
				
			||||||
 | 
								<key>CFBundleURLSchemes</key>
 | 
				
			||||||
 | 
								<array>
 | 
				
			||||||
 | 
									<string>masterpassword</string>
 | 
				
			||||||
 | 
								</array>
 | 
				
			||||||
 | 
							</dict>
 | 
				
			||||||
 | 
						</array>
 | 
				
			||||||
	<key>CFBundleVersion</key>
 | 
						<key>CFBundleVersion</key>
 | 
				
			||||||
	<string>[auto]</string>
 | 
						<string>[auto]</string>
 | 
				
			||||||
	<key>LSApplicationQueriesSchemes</key>
 | 
						<key>LSApplicationQueriesSchemes</key>
 | 
				
			||||||
@@ -44,6 +59,7 @@
 | 
				
			|||||||
		<string>firefox</string>
 | 
							<string>firefox</string>
 | 
				
			||||||
		<string>googlechrome</string>
 | 
							<string>googlechrome</string>
 | 
				
			||||||
		<string>opera-http</string>
 | 
							<string>opera-http</string>
 | 
				
			||||||
 | 
							<string>volto</string>
 | 
				
			||||||
	</array>
 | 
						</array>
 | 
				
			||||||
	<key>LSRequiresIPhoneOS</key>
 | 
						<key>LSRequiresIPhoneOS</key>
 | 
				
			||||||
	<true/>
 | 
						<true/>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user