2
0

Replace Crashlytics/Fabric with Countly & Sentry.

This commit is contained in:
Maarten Billemont
2020-01-27 13:27:10 -05:00
parent b460e27696
commit 6eaa491d67
77 changed files with 980 additions and 2899 deletions

View File

@@ -0,0 +1,28 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import <Foundation/Foundation.h>
extern NSString *appSecret;
extern NSString *appSalt;
extern NSString *sentryDSN;
extern NSString *countlyKey;
extern NSString *countlySalt;
NSString *decrypt(NSString *appSecret);
NSString *digest(NSString *value);

View File

@@ -0,0 +1,75 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import "MPSecrets.h"
#import "base64.h"
// printf <secret> | openssl enc -[ed] -aes-128-cbc -a -A -K <appSecret> -iv 0
NSString *appSecret = @"";
NSString *appSalt = @"";
NSString *sentryDSN = @"";
NSString *countlyKey = @"";
NSString *countlySalt = @"";
NSString *decrypt(NSString *secret) {
if (!secret)
return nil;
const char *secretString = [secret cStringUsingEncoding:NSUTF8StringEncoding];
size_t length = mpw_base64_decode_max( secretString );
if (!length)
return nil;
size_t keyLength;
const uint8_t *key = mpw_unhex( [appSecret cStringUsingEncoding:NSUTF8StringEncoding], &keyLength );
if (!key)
return nil;
uint8_t *base64 = calloc( length, sizeof( uint8_t ) );
length = mpw_base64_decode( base64, secretString );
const void *plain = mpw_aes_decrypt( key, keyLength, base64, &length );
mpw_free( &key, keyLength );
@try {
return [[NSString alloc] initWithBytes:plain length:length encoding:NSUTF8StringEncoding];
}
@finally {
mpw_free( &plain, length );
}
}
NSString *digest(NSString *value) {
if (!value)
return nil;
size_t appSaltLength, valueLength;
const void *appSaltString = [decrypt( appSalt ) cStringUsingEncoding:NSUTF8StringEncoding length:&appSaltLength];
const void *valueString = [value cStringUsingEncoding:NSUTF8StringEncoding length:&valueLength];
const uint8_t *digest = mpw_hash_hmac_sha256( appSaltString, appSaltLength, valueString, valueLength );
if (!digest)
return nil;
@try {
return [[NSString alloc] initWithBytes:mpw_hex( digest, 32 ) length:16 encoding:NSUTF8StringEncoding];
}
@finally {
mpw_free( &digest, 32 );
}
}

View File

@@ -57,6 +57,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.dataSource = [NSMutableArray new];
self.view.backgroundColor = [UIColor clearColor];
if (@available( iOS 11, * ))
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
[self.collectionView automaticallyAdjustInsetsForKeyboard];
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
if ([self.searchBar respondsToSelector:@selector( keyboardAppearance )])

View File

@@ -16,7 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import <Crashlytics/Answers.h>
#import <Countly/Countly.h>
#import "MPUsersViewController.h"
#import "MPEntities.h"
#import "MPAvatarCell.h"
@@ -228,14 +229,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
user.name = newUserName;
if ([[MPConfig get].sendInfo boolValue]) {
#ifdef CRASHLYTICS
[Answers logSignUpWithMethod:@"Manual"
success:@YES
customAttributes:@{
@"algorithm": @(user.algorithm.version),
@"avatar" : @(user.avatar),
}];
#endif
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
@"algorithm": @(user.algorithm.version).description,
@"avatar" : @(user.avatar).description,
}];
}
}
@@ -666,7 +663,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self removeObservers];
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *self) {
[self updateAvatarVisibility];
PearlMainQueue( ^{ [self updateAvatarVisibility]; } );
}];
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],

View File

@@ -21,6 +21,10 @@
#import "MPAppDelegate_Store.h"
#import "MPStoreViewController.h"
#import "mpw-marshal.h"
#import "MPSecrets.h"
#import <Sentry/Sentry.h>
#import <Countly/Countly.h>
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@@ -35,11 +39,10 @@
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
#ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelTrace;
#else
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
//[PearlLogger get].printLevel = PearlLogLevelTrace;
#endif
} );
}
@@ -47,34 +50,73 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
@try {
// [[NSBundle mainBundle] mutableInfoDictionary][@"CFBundleDisplayName"] = @"Master Password";
// [[NSBundle mainBundle] mutableLocalizedInfoDictionary][@"CFBundleDisplayName"] = @"Master Password";
#ifdef CRASHLYTICS
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
if ([crashlyticsAPIKey length]) {
inf( @"Initializing Crashlytics" );
#if DEBUG
[Crashlytics sharedInstance].debugMode = YES;
// Sentry
SentryClient.sharedClient = [[SentryClient alloc] initWithDsn:decrypt( sentryDSN ) didFailWithError:nil];
#ifdef DEBUG
SentryClient.sharedClient.environment = @"Development";
#elif PUBLIC
SentryClient.sharedClient.environment = @"Public";
#else
SentryClient.sharedClient.environment = @"Private";
#endif
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue])
level = PearlLogLevelDebug;
SentryClient.sharedClient.enabled = [MPiOSConfig get].sendInfo;
[SentryClient.sharedClient enableAutomaticBreadcrumbTracking];
[SentryClient.sharedClient startCrashHandlerWithError:nil];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue])
level = PearlLogLevelDebug;
if (message.level >= level)
CLSLog( @"%@", [message messageDescription] );
if (message.level >= level) {
SentrySeverity sentryLevel = kSentrySeverityInfo;
switch (message.level) {
case PearlLogLevelTrace:
sentryLevel = kSentrySeverityDebug;
break;
case PearlLogLevelDebug:
sentryLevel = kSentrySeverityDebug;
break;
case PearlLogLevelInfo:
sentryLevel = kSentrySeverityInfo;
break;
case PearlLogLevelWarn:
sentryLevel = kSentrySeverityWarning;
break;
case PearlLogLevelError:
sentryLevel = kSentrySeverityError;
break;
case PearlLogLevelFatal:
sentryLevel = kSentrySeverityFatal;
break;
}
SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] initWithLevel:sentryLevel category:@"Pearl"];
breadcrumb.type = @"log";
breadcrumb.message = message.message;
breadcrumb.timestamp = message.occurrence;
breadcrumb.data = @{ @"file": message.fileName, @"line": @(message.lineNumber), @"function": message.function };
[SentryClient.sharedClient.breadcrumbs addBreadcrumb:breadcrumb];
}
return YES;
}];
CLSLog( @"Crashlytics (%@) initialized for: %@ v%@.", //
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
}
return YES;
}];
// Countly
CountlyConfig *countlyConfig = [CountlyConfig new];
countlyConfig.host = @"https://countly.lyndir.app";
countlyConfig.appKey = decrypt( countlyKey );
countlyConfig.features = @[ CLYPushNotifications ];
countlyConfig.requiresConsent = true;
#if PUBLIC
countlyConfig.pushTestMode = nil;
#elif DEBUG
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
#else
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
#endif
countlyConfig.alwaysUsePOST = true;
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
countlyConfig.secretSalt = decrypt( countlySalt );
[Countly.sharedInstance startWithConfig:countlyConfig];
[self.hangDetector = [[PearlHangDetector alloc] initWithHangAction:^(NSTimeInterval hangTime) {
MPError( [NSError errorWithDomain:MPErrorDomain code:MPErrorHangCode userInfo:@{
@@ -111,21 +153,21 @@
case MPFixableResultNoProblems:
break;
case MPFixableResultProblemsFixed: {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Inconsistencies Fixed" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Inconsistencies Fixed" message:
@"Some inconsistencies were detected in your sites.\n"
@"All issues were fixed."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
break;
}
case MPFixableResultProblemsNotFixed: {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Inconsistencies Found" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Inconsistencies Found" message:
@"Some inconsistencies were detected in your sites.\n"
@"Not all issues could be fixed. Try signing in to each user or checking the logs."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
break;
}
}
@@ -138,12 +180,12 @@
NSString *latestFeatures = [MPStoreViewController latestStoreFeatures];
if (latestFeatures) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"New Features" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"New Features" message:
strf( @"The following features are now available in the store:\n\n%@•••\n\n"
@"Find the store from the user pulldown after logging in.", latestFeatures )
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
}
@catch (id exception) {
@@ -167,22 +209,22 @@
MPError( error, @"While reading imported sites from %@.", url );
if (!importedSitesData) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:
strf( @"Master Password couldn't read the import sites.\n\n%@",
(id)[error localizedDescription]?: error )
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
return;
}
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
if (!importedSitesString) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:
@"Master Password couldn't understand the import file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
return;
}
@@ -204,35 +246,35 @@
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
[self importSites:importData askImportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Importing Sites For\n%@", userName ) message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Importing Sites For\n%@", userName ) message:
@"Enter the master password used to create this export file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
[alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( alert.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} askUserPassword:^NSString *(NSString *userName) {
return PearlAwait( (id)^(void (^setResult)(id)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Master Password For\n%@", userName ) message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For\n%@", userName ) message:
@"Enter the current master password for this user."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
[alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( alert.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} result:^(NSError *error) {
[activityOverlay cancelOverlayAnimated:YES];
@@ -262,19 +304,20 @@
PearlNotMainQueue( ^{
NSString *importData = [UIPasteboard generalPasteboard].string;
MPMarshalInfo *importInfo = mpw_marshal_read_info( importData.UTF8String );
if (importInfo->format != MPMarshalFormatNone) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Import Sites?" message:
MPMarshalledFile *importFile = mpw_marshal_read( NULL, importData.UTF8String );
if (importFile && importFile->error.type == MPMarshalSuccess && importFile->info->format != MPMarshalFormatNone) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Import Sites?" message:
@"We've detected Master Password import sites on your pasteboard, would you like to import them?"
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Import Sites" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self importSites:importData];
[UIPasteboard generalPasteboard].string = @"";
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Import Sites" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) {
[self importSites:importData];
[UIPasteboard generalPasteboard].string = @"";
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
mpw_marshal_info_free( &importInfo );
mpw_marshal_file_free( &importFile );
} );
[super applicationDidBecomeActive:application];
@@ -341,27 +384,27 @@
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail]) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Feedback" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"We'd love to hear what you think!\n"
@"masterpassword@lyndir.com"
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
else if (logs) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Feedback" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@"Have a question, comment, issue or just saying thanks?\n\n"
@"If you're having trouble, it may help us if you can first reproduce the problem "
@"and then include log files in your message."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Include Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Include Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:viewController];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:NO forVC:viewController];
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
else
[self openFeedbackWithLogs:NO forVC:viewController];
@@ -401,83 +444,83 @@
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Failed To Load Sites" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed To Load Sites" message:
@"Master Password was unable to open your sites history.\n"
@"This may be due to corruption. You can either reset Master Password and "
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self deleteAndResetStore];
[alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self deleteAndResetStore];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[alert addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
}
- (void)showExportForVC:(UIViewController *)viewController {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Exporting Your Sites" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Exporting Your Sites" message:
@"An export is great for keeping a backup list of your accounts.\n\n"
@"When the file is ready, you will be able to mail it to yourself.\n"
@"You can open it with a text editor or with Master Password if you need to restore your list of sites."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Export Sites" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Show Passwords?" message:
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Export Sites" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
UIAlertController *sheet = [UIAlertController alertControllerWithTitle:@"Show Passwords?" message:
@"Would you like to make all your passwords visible in the export file?\n\n"
@"A safe export will include all sites but make their passwords invisible.\n"
@"It is great as a backup and remains safe when fallen in the wrong hands."
preferredStyle:UIAlertControllerStyleAlert];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:NO forVC:viewController];
preferredStyle:UIAlertControllerStyleActionSheet];
[sheet addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:NO forVC:viewController];
}]];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Show Passwords" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:YES forVC:viewController];
[sheet addAction:[UIAlertAction actionWithTitle:@"Show Passwords" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:YES forVC:viewController];
}]];
[controller_ addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller_ animated:YES completion:nil];
[sheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:sheet animated:YES completion:nil];
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
- (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail]) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Cannot Send Mail" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Cannot Send Mail" message:
@"Your device is not yet set up for sending mail.\n"
@"Close Master Password, go into Settings and add a Mail account."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
return;
}
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) message:
@"Enter the user's master password to create an export file."
preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[controller addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( controller.textFields.firstObject.text );
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
setResult( alert.textFields.firstObject.text );
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} result:^(NSString *mpsites, NSError *error) {
if (!mpsites || error) {
MPError( error, @"Failed to export mpsites." );
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
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;
}
@@ -486,9 +529,9 @@
NSString *exportFileName = strf( @"%@ (%@).mpsites",
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
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"
@@ -511,11 +554,12 @@
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc]
initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName:exportFileName],
mimeType:@"text/plain"
fileName:exportFileName],
nil];
return;
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Share / Airdrop" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL
@@ -532,19 +576,19 @@
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[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 {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Changing Master Password" message:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Changing Master Password" message:
@"If you continue, you'll be able to set a new master password.\n\n"
@"Changing your master password will cause all your generated passwords to change!\n"
@"Changing the master password back to the old one will cause your passwords to revert as well."
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[moc performBlockAndWait:^{
inf( @"Clearing keyID for user: %@.", user.userID );
user.keyID = nil;
@@ -556,8 +600,8 @@
if (didReset)
didReset();
}]];
[controller addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
#pragma mark - UIDocumentInteractionControllerDelegate
@@ -581,51 +625,41 @@
// Send info
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance giveConsentForAllFeatures];
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo;
#ifdef CRASHLYTICS
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].sendInfo boolValue] forKey:@"sendInfo"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
[[Crashlytics sharedInstance] setBoolValue:[PearlDeviceUtils isSimulator] forKey:@"simulator"];
[[Crashlytics sharedInstance] setBoolValue:[PearlDeviceUtils isAppEncrypted] forKey:@"encrypted"];
[[Crashlytics sharedInstance] setBoolValue:[PearlDeviceUtils isJailbroken] forKey:@"jailbroken"];
[[Crashlytics sharedInstance] setObjectValue:[PearlDeviceUtils platform] forKey:@"platform"];
NSMutableDictionary *prefs = [NSMutableDictionary new];
prefs[@"rememberLogin"] = [MPConfig get].rememberLogin;
prefs[@"sendInfo"] = [MPConfig get].sendInfo;
prefs[@"helpHidden"] = [MPiOSConfig get].helpHidden;
prefs[@"showQuickStart"] = [MPiOSConfig get].showSetup;
prefs[@"firstRun"] = [PearlConfig get].firstRun;
prefs[@"launchCount"] = [PearlConfig get].launchCount;
prefs[@"askForReviews"] = [PearlConfig get].askForReviews;
prefs[@"reviewAfterLaunches"] = [PearlConfig get].reviewAfterLaunches;
prefs[@"reviewedVersion"] = [PearlConfig get].reviewedVersion;
prefs[@"simulator"] = @([PearlDeviceUtils isSimulator]);
prefs[@"encrypted"] = @([PearlDeviceUtils isAppEncrypted]);
prefs[@"jailbroken"] = @([PearlDeviceUtils isJailbroken]);
prefs[@"platform"] = [PearlDeviceUtils platform];
#ifdef APPSTORE
[[Crashlytics sharedInstance] setBoolValue:[PearlDeviceUtils isAppEncrypted] forKey:@"reviewedVersion"];
prefs[@"reviewedVersion"] = @([PearlDeviceUtils isAppEncrypted]);
#else
[[Crashlytics sharedInstance] setBoolValue:YES forKey:@"reviewedVersion"];
#endif
prefs[@"reviewedVersion"] = @(YES);
#endif
PearlMainQueueOperation( ^{
if (![[SentryClient.sharedClient.extra dictionaryWithValuesForKeys:prefs.allKeys] isEqualToDictionary:prefs]) {
NSMutableDictionary *extra = [SentryClient.sharedClient.extra mutableCopy]?: [NSMutableDictionary dictionary];
[extra addEntriesFromDictionary:prefs];
SentryClient.sharedClient.extra = extra;
}
} );
}
else {
[Countly.sharedInstance cancelConsentForAllFeatures];
}
}
#pragma mark - Crashlytics
- (NSDictionary *)crashlyticsInfo {
static NSDictionary *crashlyticsInfo = nil;
if (crashlyticsInfo == nil)
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Fabric" withExtension:@"plist"]];
return crashlyticsInfo;
}
- (NSString *)crashlyticsAPIKey {
NSString *crashlyticsAPIKey = NSNullToNil( [[self crashlyticsInfo] valueForKeyPath:@"API Key"] );
if (![crashlyticsAPIKey length])
wrn( @"Crashlytics API key not set. Crash logs won't be recorded." );
return crashlyticsAPIKey;
}
@end

View File

@@ -16,6 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import "MPiOSConfig.h"
@implementation MPiOSConfig
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch, allowDowngrade;

View File

@@ -41,20 +41,6 @@
<string>[auto]</string>
<key>CFBundleVersion</key>
<string>[auto]</string>
<key>Fabric</key>
<dict>
<key>APIKey</key>
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
<key>Kits</key>
<array>
<dict>
<key>KitInfo</key>
<dict/>
<key>KitName</key>
<string>Crashlytics</string>
</dict>
</array>
</dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>

File diff suppressed because it is too large Load Diff