@@ -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 l ength ] ) {
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 * control ler = [ UIAlertController alertControllerWithTitle : @ "Inconsistencies Fixed" message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Inconsistencies Fixed" message :
@ "Some inconsistencies were detected in your sites.\n"
@ "All issues were fixed."
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self presentViewController : a lert animated : YES completion : nil ] ;
break ;
}
case MPFixableResultProblemsNotFixed : {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Inconsistencies Found" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self presentViewController : a lert animated : YES completion : nil ] ;
break ;
}
}
@@ -138,12 +180,12 @@
NSString * latestFeatures = [ MPStoreViewController latestStoreFeatures ] ;
if ( latestFeatures ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "New Features" message :
UIAlertController * a lert = [ 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 pull‑ down after logging in." , latestFeatures )
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Thanks" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Thanks" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
}
}
@ catch ( id exception ) {
@@ -167,22 +209,22 @@
MPError ( error , @ "While reading imported sites from %@." , url ) ;
if ( ! importedSitesData ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Error" message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Error" message :
strf ( @ "Master Password couldn't read the import sites.\n\n%@" ,
( id ) [ error localizedDescription ] ? : error )
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
return ;
}
NSString * importedSitesString = [ [ NSString alloc ] initWithData : importedSitesData encoding : NSUTF8StringEncoding ] ;
if ( ! importedSitesString ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Error" message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Error" message :
@ "Master Password couldn't understand the import file."
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert 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 * control ler = [ UIAlertController alertControllerWithTitle : strf ( @ "Importing Sites For\n%@" , userName ) message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : strf ( @ "Importing Sites For\n%@" , userName ) message :
@ "Enter the master password used to create this export file."
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
textField . secureTextEntry = YES ;
} ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Import" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( control ler. textFields . firstObject . text ) ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Import" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( a lert . textFields . firstObject . text ) ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
setResult ( nil ) ;
} ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
} ) ;
} askUserPassword : ^ NSString * ( NSString * userName ) {
return PearlAwait ( ( id ) ^ ( void ( ^ setResult ) ( id ) ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : strf ( @ "Master Password For\n%@" , userName ) message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : strf ( @ "Master Password For\n%@" , userName ) message :
@ "Enter the current master password for this user."
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
textField . secureTextEntry = YES ;
} ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Import" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( control ler. textFields . firstObject . text ) ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Import" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( a lert . textFields . firstObject . text ) ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
setResult ( nil ) ;
} ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ self . navigationController presentViewController : a lert 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 ( importI nfo -> format ! = MPMarshalFormatNone ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Import Sites?" message :
MPMarshalledFile * importFile = mpw_marshal _read ( NULL , importData . UTF8String ) ;
if ( importFile && importFile -> error . type = = MPMarshalSuccess && importFile -> i nfo -> format ! = MPMarshalFormatNone ) {
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Import Sites?" message :
@ "We've detected Master Password import sites on your pasteboard, would you like to import them?"
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler 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 presentViewControl ler : controller animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Import Sites" style : UIAlertActionStyleDefault handler :
^ ( UIAlertAction * action ) {
[ self importSites : importData ] ;
[ UIPasteboard generalPasteboard ] . string = @ "" ;
} ] ] ;
[ alert addAction : [ UIAlertAction actionWithTit le: @ "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 * control ler = [ UIAlertController alertControllerWithTitle : @ "Feedback" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
}
else if ( logs ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Feedback" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Include Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Include Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self openFeedbackWithLogs : YES forVC : viewController ] ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "No Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "No Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self openFeedbackWithLogs : NO forVC : viewController ] ;
} ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
}
else
[ self openFeedbackWithLogs : NO forVC : viewController ] ;
@@ -401,83 +444,83 @@
static dispatch_once _t once = 0 ;
dispatch_once ( & once , ^ {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Failed To Load Sites" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "E-Mail Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self openFeedbackWithLogs : YES forVC : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "E-Mail Logs" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self openFeedbackWithLogs : YES forVC : nil ] ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Reset" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self deleteAndResetStore ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Reset" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self deleteAndResetStore ] ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Ignore" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Ignore" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
} ) ;
}
- ( void ) showExportForVC : ( UIViewController * ) viewController {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Exporting Your Sites" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Export Sites" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
UIAlertController * controller_ = [ UIAlertController alertControllerWithTitle : @ "Show Passwords?" message :
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert 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 : UIAlertControllerStyleAler t ] ;
[ controller_ addAction : [ UIAlertAction actionWithTitle : @ "Safe Export" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self showExportRevealPasswords : NO forVC : viewController ] ;
preferredStyle : UIAlertControllerStyleActionShee t ] ;
[ 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 ] ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
}
- ( void ) showExportRevealPasswords : ( BOOL ) revealPasswords forVC : ( UIViewController * ) viewController {
if ( ! [ PearlEMail canSendMail ] ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Cannot Send Mail" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
return ;
}
[ self exportSitesRevealPasswords : revealPasswords askExportPassword : ^ NSString * ( NSString * userName ) {
return PearlAwait ( ^ ( void ( ^ setResult ) ( id ) ) {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : strf ( @ "Master Password For:\n%@" , userName ) message :
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : strf ( @ "Master Password For:\n%@" , userName ) message :
@ "Enter the user's master password to create an export file."
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
textField . secureTextEntry = YES ;
} ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Export" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( control ler. textFields . firstObject . text ) ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Export" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
setResult ( a lert . textFields . firstObject . text ) ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
setResult ( nil ) ;
} ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
} ) ;
} result : ^ ( NSString * mpsites , NSError * error ) {
if ( ! mpsites || error ) {
MPError ( error , @ "Failed to export mpsites." ) ;
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Export Error" message : [ error localizedDescription ]
preferredStyle : UIAlertControllerStyleAlert ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Export Error" message : [ error localizedDescription ]
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Okay" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
return ;
}
@@ -486,9 +529,9 @@
NSString * exportFileName = strf ( @ "%@ (%@).mpsites" ,
[ self activeUserForMainThread ] . name , [ exportDateFormatter stringFromDate : [ NSDate date ] ] ) ;
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Export Destination" message : nil
preferredStyle : UIAlertControllerStyleActionSheet ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Send As E-Mail" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
UIAlertController * a lert = [ UIAlertController alertControllerWithTitle : @ "Export Destination" message : nil
preferredStyle : UIAlertControllerStyleActionSheet ] ;
[ a lert 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 ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Share / Airdrop " style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ a lert 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 ] ;
}
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Continue" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert animated : YES completion : nil ] ;
} ] ;
}
- ( void ) changeMasterPasswordFor : ( MPUserEntity * ) user saveInContext : ( NSManagedObjectContext * ) moc didResetBlock : ( void ( ^ ) ( void ) ) didReset {
UIAlertController * control ler = [ UIAlertController alertControllerWithTitle : @ "Changing Master Password" message :
UIAlertController * a lert = [ 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 ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Abort" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
preferredStyle : UIAlertControllerStyleAlert ] ;
[ a lert 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 ( ) ;
} ] ] ;
[ control ler addAction : [ UIAlertAction actionWithTitle : @ "Abort" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : control ler animated : YES completion : nil ] ;
[ a lert addAction : [ UIAlertAction actionWithTitle : @ "Abort" style : UIAlertActionStyleCancel handler : nil ] ] ;
[ self . navigationController presentViewController : a lert 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 ;
# ifd ef 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 * pr efs = [ 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