Compare commits
7 Commits
2.8-mac-10
...
2.8-mac-11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2279aacb5a | ||
|
|
1bd654621c | ||
|
|
c4f60e325d | ||
|
|
d4de3afb72 | ||
|
|
694b5ea227 | ||
|
|
66dd78797b | ||
|
|
61d1660560 |
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
Submodule platform-darwin/External/Pearl updated: d4de9198ec...72de3d1b49
@@ -197,18 +197,16 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
|||||||
forKey:transaction.payment.productIdentifier];
|
forKey:transaction.payment.productIdentifier];
|
||||||
[queue finishTransaction:transaction];
|
[queue finishTransaction:transaction];
|
||||||
|
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
[attributes addEntriesFromDictionary:@{
|
||||||
[attributes addEntriesFromDictionary:@{
|
@"id": product.productIdentifier,
|
||||||
@"id": product.productIdentifier,
|
@"name": product.localizedTitle,
|
||||||
@"name": product.localizedTitle,
|
@"price": product.price.description,
|
||||||
@"price": product.price.description,
|
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
@"state" : @"success",
|
||||||
@"state" : @"success",
|
@"quantity": @(transaction.payment.quantity).description,
|
||||||
@"quantity": @(transaction.payment.quantity).description,
|
}];
|
||||||
}];
|
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
||||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SKPaymentTransactionStateRestored: {
|
case SKPaymentTransactionStateRestored: {
|
||||||
@@ -224,18 +222,16 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
|||||||
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
||||||
[queue finishTransaction:transaction];
|
[queue finishTransaction:transaction];
|
||||||
|
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
|
||||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
|
@"id": product.productIdentifier,
|
||||||
@"id": product.productIdentifier,
|
@"name": product.localizedTitle,
|
||||||
@"name": product.localizedTitle,
|
@"price": product.price.description,
|
||||||
@"price": product.price.description,
|
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
@"state" : @"failed",
|
||||||
@"state" : @"failed",
|
@"quantity": @(transaction.payment.quantity).description,
|
||||||
@"quantity": @(transaction.payment.quantity).description,
|
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
||||||
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
}];
|
||||||
}];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,13 +173,11 @@
|
|||||||
else
|
else
|
||||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||||
|
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
@"method" : password? @"Password": @"Automatic",
|
||||||
@"method" : password? @"Password": @"Automatic",
|
@"state" : @"failed",
|
||||||
@"state" : @"failed",
|
@"algorithm": @(user.algorithm.version).description,
|
||||||
@"algorithm": @(user.algorithm.version).description,
|
}];
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
@@ -203,15 +201,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
[Countly.sharedInstance userLoggedIn:user.userID];
|
||||||
[Countly.sharedInstance userLoggedIn:user.userID];
|
|
||||||
|
|
||||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||||
@"method" : password? @"Password": @"Automatic",
|
@"method" : password? @"Password": @"Automatic",
|
||||||
@"state" : @"success",
|
@"state" : @"success",
|
||||||
@"algorithm": @(user.algorithm.version).description,
|
@"algorithm": @(user.algorithm.version).description,
|
||||||
}];
|
}];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@catch (id exception) {
|
@catch (id exception) {
|
||||||
err( @"While setting username: %@", exception );
|
err( @"While setting username: %@", exception );
|
||||||
|
|||||||
@@ -119,8 +119,7 @@ static MPAppDelegate_Shared *instance;
|
|||||||
if (self.key)
|
if (self.key)
|
||||||
self.key = nil;
|
self.key = nil;
|
||||||
|
|
||||||
if ([[MPConfig get].sendInfo boolValue])
|
[Countly.sharedInstance userLoggedOut];
|
||||||
[Countly.sharedInstance userLoggedOut];
|
|
||||||
|
|
||||||
self.activeUserOID = activeUserOID;
|
self.activeUserOID = activeUserOID;
|
||||||
|
|
||||||
|
|||||||
@@ -563,50 +563,46 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
|
|
||||||
// Read metadata for the import file.
|
// Read metadata for the import file.
|
||||||
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
|
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
|
||||||
if (!file)
|
MPMarshalledUser *importUser = nil;
|
||||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
@try {
|
||||||
@"type" : @(MPMarshalErrorInternal),
|
if (!file)
|
||||||
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||||
}]), @"While importing sites." );
|
@"type" : @(MPMarshalErrorInternal),
|
||||||
if (file->error.type != MPMarshalSuccess) {
|
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
||||||
MPMarshalErrorType type = file->error.type;
|
}]), @"While importing sites." );
|
||||||
mpw_marshal_file_free( &file );
|
if (file->error.type != MPMarshalSuccess) {
|
||||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||||
@"type" : @(type),
|
@"type" : @(file->error.type),
|
||||||
NSLocalizedDescriptionKey: @"Could not parse Master Password import data.",
|
NSLocalizedDescriptionKey: strf( @"Could not parse Master Password import data:\n%@", @(file->error.message) ),
|
||||||
}]), @"While importing sites." );
|
}]), @"While importing sites." );
|
||||||
}
|
}
|
||||||
if (file->info->format == MPMarshalFormatNone) {
|
if (file->info->format == MPMarshalFormatNone) {
|
||||||
mpw_marshal_file_free( &file );
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
@"type" : @(MPMarshalErrorFormat),
|
||||||
@"type" : @(MPMarshalErrorFormat),
|
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
}]), @"While importing sites." );
|
||||||
}]), @"While importing sites." );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get master password for import file.
|
|
||||||
MPKey *importKey;
|
|
||||||
NSString *importMasterPassword;
|
|
||||||
do {
|
|
||||||
importMasterPassword = askImportPassword( @(file->info->fullName) );
|
|
||||||
if (!importMasterPassword) {
|
|
||||||
inf( @"Import cancelled." );
|
|
||||||
mpw_marshal_file_free( &file );
|
|
||||||
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
// Get master password for import file.
|
||||||
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex]
|
MPKey *importKey;
|
||||||
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
|
NSString *importMasterPassword;
|
||||||
|
do {
|
||||||
|
importMasterPassword = askImportPassword( @(file->info->fullName) );
|
||||||
|
if (!importMasterPassword) {
|
||||||
|
inf( @"Import cancelled." );
|
||||||
|
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
||||||
|
}
|
||||||
|
|
||||||
// Parse import data.
|
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
||||||
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
|
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex]
|
||||||
|
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
|
||||||
|
|
||||||
@try {
|
// Parse import data.
|
||||||
|
importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
|
||||||
if (!importUser || file->error.type != MPMarshalSuccess)
|
if (!importUser || file->error.type != MPMarshalSuccess)
|
||||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||||
@"type" : @(file->error.type),
|
@"type" : @(file->error.type),
|
||||||
NSLocalizedDescriptionKey: @(file->error.message),
|
NSLocalizedDescriptionKey: strf( @"Could not authenticate Master Password import:\n%@", @(file->error.message) ),
|
||||||
}]), @"While importing sites." );
|
}]), @"While importing sites." );
|
||||||
|
|
||||||
// Find an existing user to update.
|
// Find an existing user to update.
|
||||||
|
|||||||
@@ -22,11 +22,12 @@
|
|||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *sendInfo;
|
@property(nonatomic, retain) NSNumber *sendInfo;
|
||||||
@property(nonatomic, retain) NSNumber *sendInfoDecided;
|
@property(nonatomic, retain) NSNumber *sendInfoDecided;
|
||||||
|
@property(nonatomic, retain) NSNumber *notificationsDecided;
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *rememberLogin;
|
@property(nonatomic, retain) NSNumber *rememberLogin;
|
||||||
@property(nonatomic, retain) NSNumber *hidePasswords;
|
@property(nonatomic, retain) NSNumber *hidePasswords;
|
||||||
|
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
||||||
|
|
||||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
@implementation MPConfig
|
@implementation MPConfig
|
||||||
|
|
||||||
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
|
@dynamic sendInfo, sendInfoDecided, notificationsDecided, rememberLogin, hidePasswords, siteAttacker, checkInconsistency;
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
|
|
||||||
@@ -29,15 +29,16 @@
|
|||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
[self.defaults registerDefaults:@{
|
[self.defaults registerDefaults:@{
|
||||||
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
|
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
|
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
|
||||||
|
NSStringFromSelector( @selector( notificationsDecided ) ): @NO,
|
||||||
|
|
||||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||||
|
|
||||||
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
|
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
self.delegate = [MPAppDelegate_Shared get];
|
self.delegate = [MPAppDelegate_Shared get];
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
|
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
|
||||||
|
|
||||||
|
|
||||||
@implementation MPMacAppDelegate
|
@implementation MPMacAppDelegate
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
@@ -134,7 +133,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
countlyConfig.enableDebug = YES;
|
countlyConfig.enableDebug = YES;
|
||||||
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
||||||
#elif ! PUBLIC
|
#elif !PUBLIC
|
||||||
countlyConfig.enableDebug = NO;
|
countlyConfig.enableDebug = NO;
|
||||||
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
||||||
#endif
|
#endif
|
||||||
@@ -205,7 +204,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
[NSApp activateIgnoringOtherApps:YES];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self enableNotifications];
|
[self tryNotifications];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||||
@@ -231,7 +230,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
return NSTerminateNow;
|
return NSTerminateNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enableNotifications {
|
- (void)tryNotifications {
|
||||||
|
|
||||||
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
|
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
|
||||||
if (@available( macOS 10.14, * )) {
|
if (@available( macOS 10.14, * )) {
|
||||||
@@ -250,18 +249,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
|
|
||||||
- (void)askNotifications {
|
- (void)askNotifications {
|
||||||
|
|
||||||
|
if ([[MPMacConfig get].notificationsDecided boolValue])
|
||||||
|
return;
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
|
if (@available( macOS 10.14, * )) {
|
||||||
if (@available( macOS 10.14, * )) {
|
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
^(BOOL granted, NSError *error) {
|
||||||
^(BOOL granted, NSError *error) {
|
[MPMacConfig get].notificationsDecided = @(YES);
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
}];
|
||||||
}];
|
}
|
||||||
}
|
else {
|
||||||
else {
|
[Countly.sharedInstance askForNotificationPermission];
|
||||||
[Countly.sharedInstance askForNotificationPermission];
|
[MPMacConfig get].notificationsDecided = @(YES);
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@@ -408,14 +408,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
|
|
||||||
- (IBAction)togglePreference:(id)sender {
|
- (IBAction)togglePreference:(id)sender {
|
||||||
|
|
||||||
if (sender == self.diagnosticsItem) {
|
if (sender == self.diagnosticsItem)
|
||||||
BOOL sendInfo = self.diagnosticsItem.state != NSOnState;
|
[MPMacConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
|
||||||
[[Countly sharedInstance] recordEvent:@"sendInfoDecided" segmentation:@{
|
|
||||||
@"from": @"preferences",
|
|
||||||
@"sendInfo": [@(sendInfo) description],
|
|
||||||
}];
|
|
||||||
[MPMacConfig get].sendInfo = @(sendInfo);
|
|
||||||
}
|
|
||||||
if (sender == self.hidePasswordsItem)
|
if (sender == self.hidePasswordsItem)
|
||||||
[MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
[MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
||||||
if (sender == self.rememberPasswordItem)
|
if (sender == self.rememberPasswordItem)
|
||||||
@@ -446,8 +440,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
NSAlert *alert = [NSAlert new];
|
NSAlert *alert = [NSAlert new];
|
||||||
[alert setMessageText:@"New User"];
|
[alert setMessageText:@"New User"];
|
||||||
[alert setInformativeText:@"To begin, enter your full name.\n\n"
|
[alert setInformativeText:@"To begin, enter your full name.\n\n"
|
||||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||||
@"as you would on an official document."];
|
@"as you would on an official document."];
|
||||||
[alert addButtonWithTitle:@"Create User"];
|
[alert addButtonWithTitle:@"Create User"];
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||||
@@ -775,10 +769,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
|
|
||||||
// Send info
|
// Send info
|
||||||
NSArray *countlyFeatures = @[
|
NSArray *countlyFeatures = @[
|
||||||
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
||||||
];
|
];
|
||||||
|
if ([[MPConfig get].sendInfo boolValue] || ![[MPConfig get].sendInfoDecided boolValue])
|
||||||
|
[Countly.sharedInstance giveConsentForFeature:CLYConsentSessions];
|
||||||
|
else
|
||||||
|
[Countly.sharedInstance cancelConsentForFeature:CLYConsentSessions];
|
||||||
if ([[MPMacConfig get].sendInfo boolValue]) {
|
if ([[MPMacConfig get].sendInfo boolValue]) {
|
||||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
|
||||||
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
||||||
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
||||||
|
|
||||||
@@ -796,10 +793,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
|
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
|
||||||
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
|
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
|
||||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||||
|
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,12 +61,7 @@
|
|||||||
[alert addButtonWithTitle:@"Thanks!"];
|
[alert addButtonWithTitle:@"Thanks!"];
|
||||||
[alert addButtonWithTitle:@"Disable"];
|
[alert addButtonWithTitle:@"Disable"];
|
||||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||||
BOOL sendInfo = returnCode != NSAlertSecondButtonReturn;
|
[MPMacConfig get].sendInfo = @(returnCode != NSAlertSecondButtonReturn);
|
||||||
[[Countly sharedInstance] recordEvent:@"sendInfoDecided" segmentation:@{
|
|
||||||
@"from": @"initial",
|
|
||||||
@"sendInfo": [@(sendInfo) description],
|
|
||||||
}];
|
|
||||||
[MPMacConfig get].sendInfo = @(sendInfo);
|
|
||||||
[MPMacConfig get].sendInfoDecided = @(YES);
|
[MPMacConfig get].sendInfoDecided = @(YES);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -399,7 +394,52 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)changeType:(id)sender {
|
- (IBAction)changeDefaultType:(id)sender {
|
||||||
|
|
||||||
|
MPSiteModel *site = self.selectedSite;
|
||||||
|
MPUserEntity *user = [MPMacAppDelegate get].activeUserForMainThread;
|
||||||
|
NSArray *types = [user.algorithm allTypes];
|
||||||
|
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
|
||||||
|
for (NSUInteger t = 0; t < [types count]; ++t) {
|
||||||
|
MPResultType type = (MPResultType)[types[t] unsignedIntegerValue];
|
||||||
|
NSString *title = [user.algorithm nameOfType:type];
|
||||||
|
if (type & MPResultTypeClassTemplate)
|
||||||
|
title = strf( @"%@ – %@", [user.algorithm mpwTemplateForSiteNamed:site.name?: @"masterpassword.app" ofType:type
|
||||||
|
withCounter:site.counter?: MPCounterValueDefault
|
||||||
|
usingKey:[MPMacAppDelegate get].key], title );
|
||||||
|
|
||||||
|
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||||
|
cell.tag = type;
|
||||||
|
cell.state = type == site.type? NSOnState: NSOffState;
|
||||||
|
cell.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.passwordTypesBox.title = strf( @"Choose a password type for new sites of %@:", user.name );
|
||||||
|
|
||||||
|
NSAlert *alert = [NSAlert new];
|
||||||
|
[alert addButtonWithTitle:@"Save"];
|
||||||
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
|
[alert setMessageText:@"Change Default Type"];
|
||||||
|
[alert setAccessoryView:self.passwordTypesBox];
|
||||||
|
[alert layout];
|
||||||
|
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||||
|
switch (returnCode) {
|
||||||
|
case NSAlertFirstButtonReturn: {
|
||||||
|
// "Save" button.
|
||||||
|
MPResultType type = (MPResultType)[self.passwordTypesMatrix.selectedCell tag];
|
||||||
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
|
[[MPMacAppDelegate get] activeUserInContext:context].defaultType = type;
|
||||||
|
[context saveToStore];
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)changeSiteType:(id)sender {
|
||||||
|
|
||||||
MPSiteModel *site = self.selectedSite;
|
MPSiteModel *site = self.selectedSite;
|
||||||
NSArray *types = [site.algorithm allTypes];
|
NSArray *types = [site.algorithm allTypes];
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||||
</shadow>
|
</shadow>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Master Password generates passwords for your sites (and other things)." id="YyD-hd-wi3">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Master Password generates passwords for your sites (and other things)." id="YyD-hd-wi3">
|
||||||
<font key="font" metaFont="system" size="14"/>
|
<font key="font" metaFont="menu" size="14"/>
|
||||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@@ -143,11 +143,11 @@
|
|||||||
</shadow>
|
</shadow>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
|
||||||
<font key="font" metaFont="message" size="11"/>
|
<font key="font" metaFont="message" size="11"/>
|
||||||
<mutableString key="title">⑴ When you create an account on a site, open Master Password to create your account password.
|
<string key="title">⑴ When you create an account on a site, open Master Password to create your account password.
|
||||||
⑵ Consider changing all your existing account passwords to the password Master Password creates for those sites.
|
⑵ Consider changing all your existing account passwords to the password Master Password creates for those sites.
|
||||||
⑶ To get the password for a site, just enter its domain name in the "site name" field (eg. "apple.com").
|
⑶ To get the password for a site, just enter its domain name in the "site name" field (eg. "apple.com").
|
||||||
⑷ When chosing a master password, make it easy but long (eg. a short sentence).
|
⑷ When chosing a master password, make it easy but long (eg. a short sentence).
|
||||||
⑸ Tell *nobody* your master password. It's OK to share your site passwords with people you trust: they can be changed if necessary.</mutableString>
|
⑸ Tell *nobody* your master password. It's OK to share your site passwords with people you trust: they can be changed if necessary.</string>
|
||||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@@ -310,7 +310,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="nM8-O3-spM" customClass="MPGradientView">
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="nM8-O3-spM" customClass="MPGradientView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="738" height="212"/>
|
<rect key="frame" x="0.0" y="0.0" width="738" height="238"/>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="color" keyPath="startingColor">
|
<userDefinedRuntimeAttribute type="color" keyPath="startingColor">
|
||||||
<color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@@ -568,7 +568,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pHt-gg-ZNX">
|
<stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pHt-gg-ZNX">
|
||||||
<rect key="frame" x="122" y="20" width="495" height="152"/>
|
<rect key="frame" x="122" y="20" width="495" height="178"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
|
||||||
<rect key="frame" x="0.0" y="-1" width="85" height="19"/>
|
<rect key="frame" x="0.0" y="-1" width="85" height="19"/>
|
||||||
@@ -591,10 +591,10 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DT0-RU-3LT">
|
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DT0-RU-3LT">
|
||||||
<rect key="frame" x="93" y="0.0" width="177" height="152"/>
|
<rect key="frame" x="93" y="0.0" width="177" height="178"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H">
|
||||||
<rect key="frame" x="77" y="138" width="22" height="14"/>
|
<rect key="frame" x="77" y="164" width="22" height="14"/>
|
||||||
<shadow key="shadow" blurRadius="0.5">
|
<shadow key="shadow" blurRadius="0.5">
|
||||||
<size key="offset" width="0.0" height="1"/>
|
<size key="offset" width="0.0" height="1"/>
|
||||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -631,7 +631,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="brI-fg-Kav">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="brI-fg-Kav">
|
||||||
<rect key="frame" x="40" y="111" width="96" height="19"/>
|
<rect key="frame" x="40" y="137" width="96" height="19"/>
|
||||||
<shadow key="shadow">
|
<shadow key="shadow">
|
||||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||||
</shadow>
|
</shadow>
|
||||||
@@ -659,6 +659,35 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
</binding>
|
</binding>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jl4-sS-xbm">
|
||||||
|
<rect key="frame" x="12" y="111" width="153" height="19"/>
|
||||||
|
<shadow key="shadow">
|
||||||
|
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</shadow>
|
||||||
|
<buttonCell key="cell" type="recessed" title="Default Password Type" bezelStyle="recessed" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ah6-gK-Rm7" customClass="MPNoStateButtonCell">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
|
||||||
|
<font key="font" metaFont="systemBold" size="12"/>
|
||||||
|
<string key="keyEquivalent">p</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="changeDefaultType:" target="-2" id="3Qg-xV-ewc"/>
|
||||||
|
<binding destination="-2" name="hidden2" keyPath="alternatePressed" previousBinding="2Mv-lM-iXB" id="6QO-NJ-Uyo">
|
||||||
|
<dictionary key="options">
|
||||||
|
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
|
||||||
|
<integer key="NSNoSelectionPlaceholder" value="-1"/>
|
||||||
|
<integer key="NSNotApplicablePlaceholder" value="-1"/>
|
||||||
|
<integer key="NSNullPlaceholder" value="-1"/>
|
||||||
|
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||||
|
</dictionary>
|
||||||
|
</binding>
|
||||||
|
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="2Mv-lM-iXB">
|
||||||
|
<dictionary key="options">
|
||||||
|
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||||
|
</dictionary>
|
||||||
|
</binding>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R46-fx-n14">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R46-fx-n14">
|
||||||
<rect key="frame" x="0.0" y="85" width="177" height="19"/>
|
<rect key="frame" x="0.0" y="85" width="177" height="19"/>
|
||||||
<shadow key="shadow">
|
<shadow key="shadow">
|
||||||
@@ -805,7 +834,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="changeType:" target="-2" id="6Jj-7p-da9"/>
|
<action selector="changeSiteType:" target="-2" id="6Jj-7p-da9"/>
|
||||||
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="Hat-GU-hcQ">
|
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="Hat-GU-hcQ">
|
||||||
<dictionary key="options">
|
<dictionary key="options">
|
||||||
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||||
@@ -821,6 +850,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
<integer value="1000"/>
|
<integer value="1000"/>
|
||||||
<integer value="1000"/>
|
<integer value="1000"/>
|
||||||
<integer value="1000"/>
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
</visibilityPriorities>
|
</visibilityPriorities>
|
||||||
<customSpacing>
|
<customSpacing>
|
||||||
<real value="3.4028234663852886e+38"/>
|
<real value="3.4028234663852886e+38"/>
|
||||||
@@ -829,6 +859,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
|||||||
<real value="3.4028234663852886e+38"/>
|
<real value="3.4028234663852886e+38"/>
|
||||||
<real value="3.4028234663852886e+38"/>
|
<real value="3.4028234663852886e+38"/>
|
||||||
<real value="3.4028234663852886e+38"/>
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
</customSpacing>
|
</customSpacing>
|
||||||
</stackView>
|
</stackView>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4">
|
||||||
|
|||||||
@@ -231,12 +231,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
user.avatar = newUserAvatar;
|
user.avatar = newUserAvatar;
|
||||||
user.name = newUserName;
|
user.name = newUserName;
|
||||||
|
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
||||||
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
@"algorithm": @(user.algorithm.version).description,
|
||||||
@"algorithm": @(user.algorithm.version).description,
|
@"avatar" : @(user.avatar).description,
|
||||||
@"avatar" : @(user.avatar).description,
|
}];
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||||
|
|||||||
@@ -176,7 +176,7 @@
|
|||||||
if ([[MPiOSConfig get].showSetup boolValue])
|
if ([[MPiOSConfig get].showSetup boolValue])
|
||||||
[self.navigationController performSegueWithIdentifier:@"setup" sender:self];
|
[self.navigationController performSegueWithIdentifier:@"setup" sender:self];
|
||||||
|
|
||||||
[self enableNotifications];
|
[self consentFeatures];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@catch (id exception) {
|
@catch (id exception) {
|
||||||
@@ -229,7 +229,44 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enableNotifications {
|
- (void)consentFeatures {
|
||||||
|
if ([self askDiagnostics])
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self tryNotifications];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)askDiagnostics {
|
||||||
|
|
||||||
|
if ([[MPiOSConfig get].sendInfoDecided boolValue])
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Welcome to Master Password!" message:
|
||||||
|
@"We want this experience to be top-notch.\n\n"
|
||||||
|
@"We look for bugs, runtime issues, crashes & usage counters.\n"
|
||||||
|
@"Needless to say, diagnostics are always scrubbed and personal details will never leave your device."
|
||||||
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
|
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:@"Disable" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
|
||||||
|
[MPiOSConfig get].sendInfo = @(NO);
|
||||||
|
[MPiOSConfig get].sendInfoDecided = @(YES);
|
||||||
|
[self consentFeatures];
|
||||||
|
}]];
|
||||||
|
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||||
|
[MPiOSConfig get].sendInfo = @(YES);
|
||||||
|
[MPiOSConfig get].sendInfoDecided = @(YES);
|
||||||
|
[self consentFeatures];
|
||||||
|
}]];
|
||||||
|
|
||||||
|
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
|
||||||
|
presentViewController:alert animated:YES completion:nil];
|
||||||
|
} );
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tryNotifications {
|
||||||
|
|
||||||
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
|
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
|
||||||
if (@available( iOS 12, * )) {
|
if (@available( iOS 12, * )) {
|
||||||
@@ -240,35 +277,36 @@
|
|||||||
|
|
||||||
[self askNotifications];
|
[self askNotifications];
|
||||||
}];
|
}];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
[self askNotifications];
|
[self askNotifications];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)askNotifications {
|
- (void)askNotifications {
|
||||||
|
|
||||||
|
if ([[MPiOSConfig get].notificationsDecided boolValue])
|
||||||
|
return;
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message:
|
||||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message:
|
@"Master Password is rolling out a new modern personal security platform and we're excited to bring you along.\n\n"
|
||||||
@"Master Password is rolling out a new modern personal security platform and we're excited to bring you along.\n\n"
|
@"When it's time, we'll send you a notification to help you make an effortless transition."
|
||||||
@"When it's time, we'll send you a notification to help you make an effortless transition."
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
if (@available( iOS 12, * )) {
|
||||||
if (@available( iOS 12, * )) {
|
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
^(BOOL granted, NSError *error) {
|
||||||
^(BOOL granted, NSError *error) {
|
[MPiOSConfig get].notificationsDecided = @(YES);
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
}];
|
||||||
}];
|
}
|
||||||
}
|
else {
|
||||||
else {
|
[Countly.sharedInstance askForNotificationPermission];
|
||||||
[Countly.sharedInstance askForNotificationPermission];
|
[MPiOSConfig get].notificationsDecided = @(YES);
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
}
|
||||||
}
|
}]];
|
||||||
}]];
|
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
|
||||||
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
|
presentViewController:alert animated:YES completion:nil];
|
||||||
presentViewController:alert animated:YES completion:nil];
|
|
||||||
}
|
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,10 +681,13 @@
|
|||||||
|
|
||||||
// Send info
|
// Send info
|
||||||
NSArray *countlyFeatures = @[
|
NSArray *countlyFeatures = @[
|
||||||
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
||||||
];
|
];
|
||||||
|
if ([[MPConfig get].sendInfo boolValue] || ![[MPConfig get].sendInfoDecided boolValue])
|
||||||
|
[Countly.sharedInstance giveConsentForFeature:CLYConsentSessions];
|
||||||
|
else
|
||||||
|
[Countly.sharedInstance cancelConsentForFeature:CLYConsentSessions];
|
||||||
if ([[MPConfig get].sendInfo boolValue]) {
|
if ([[MPConfig get].sendInfo boolValue]) {
|
||||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
|
||||||
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
||||||
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
||||||
|
|
||||||
@@ -670,11 +711,13 @@
|
|||||||
#else
|
#else
|
||||||
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
|
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
|
||||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||||
|
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,21 @@ MPMarshalledFile *mpw_marshal_file(
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MPMarshalledFile *mpw_marshal_error(
|
||||||
|
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...) {
|
||||||
|
|
||||||
|
file = mpw_marshal_file( file, NULL, NULL );
|
||||||
|
if (!file)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start( args, format );
|
||||||
|
file->error = (MPMarshalError){ type, mpw_strdup( mpw_vstr( format, args ) ) };
|
||||||
|
va_end( args );
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
void mpw_marshal_info_free(
|
void mpw_marshal_info_free(
|
||||||
MPMarshalledInfo **info) {
|
MPMarshalledInfo **info) {
|
||||||
|
|
||||||
@@ -228,6 +243,7 @@ void mpw_marshal_file_free(
|
|||||||
|
|
||||||
mpw_marshal_info_free( &(*file)->info );
|
mpw_marshal_info_free( &(*file)->info );
|
||||||
mpw_marshal_data_free( &(*file)->data );
|
mpw_marshal_data_free( &(*file)->data );
|
||||||
|
mpw_free_string( &(*file)->error.message );
|
||||||
mpw_free( file, sizeof( MPMarshalledFile ) );
|
mpw_free( file, sizeof( MPMarshalledFile ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +555,7 @@ static const char *mpw_marshal_write_flat(
|
|||||||
|
|
||||||
const MPMarshalledData *data = file->data;
|
const MPMarshalledData *data = file->data;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing data." };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing data." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,9 +599,9 @@ static const char *mpw_marshal_write_flat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!out)
|
if (!out)
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
|
||||||
else
|
else
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -637,7 +653,7 @@ static const char *mpw_marshal_write_json(
|
|||||||
// Section: "export"
|
// Section: "export"
|
||||||
json_object *json_file = mpw_get_json_data( file->data );
|
json_object *json_file = mpw_get_json_data( file->data );
|
||||||
if (!json_file) {
|
if (!json_file) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't serialize export data." };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't serialize export data." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,9 +666,9 @@ static const char *mpw_marshal_write_json(
|
|||||||
json_object_put( json_file );
|
json_object_put( json_file );
|
||||||
|
|
||||||
if (!out)
|
if (!out)
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
|
||||||
else
|
else
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -698,17 +714,17 @@ const char *mpw_marshal_write(
|
|||||||
if (!file_)
|
if (!file_)
|
||||||
mpw_marshal_file_free( &file );
|
mpw_marshal_file_free( &file );
|
||||||
else
|
else
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!user->fullName || !strlen( user->fullName )) {
|
if (!user->fullName || !strlen( user->fullName )) {
|
||||||
if (!file_)
|
if (!file_)
|
||||||
mpw_marshal_file_free( &file );
|
mpw_marshal_file_free( &file );
|
||||||
else
|
else
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing full name." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
|
|
||||||
MPMasterKey masterKey = NULL;
|
MPMasterKey masterKey = NULL;
|
||||||
if (user->masterKeyProvider)
|
if (user->masterKeyProvider)
|
||||||
@@ -749,7 +765,7 @@ const char *mpw_marshal_write(
|
|||||||
if (!file_)
|
if (!file_)
|
||||||
mpw_marshal_file_free( &file );
|
mpw_marshal_file_free( &file );
|
||||||
else
|
else
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,7 +823,7 @@ const char *mpw_marshal_write(
|
|||||||
const char *out = NULL;
|
const char *out = NULL;
|
||||||
switch (outFormat) {
|
switch (outFormat) {
|
||||||
case MPMarshalFormatNone:
|
case MPMarshalFormatNone:
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
break;
|
break;
|
||||||
case MPMarshalFormatFlat:
|
case MPMarshalFormatFlat:
|
||||||
out = mpw_marshal_write_flat( file );
|
out = mpw_marshal_write_flat( file );
|
||||||
@@ -818,7 +834,7 @@ const char *mpw_marshal_write(
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Unsupported output format: %u", outFormat );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (out && file->error.type == MPMarshalSuccess)
|
if (out && file->error.type == MPMarshalSuccess)
|
||||||
@@ -839,7 +855,7 @@ static void mpw_marshal_read_flat(
|
|||||||
|
|
||||||
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
|
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
|
||||||
if (!file->data) {
|
if (!file->data) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,10 +907,7 @@ static void mpw_marshal_read_flat(
|
|||||||
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
|
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
|
||||||
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||||
if (!headerName || !headerValue) {
|
if (!headerName || !headerValue) {
|
||||||
file->error = (MPMarshalError){
|
mpw_marshal_error( file, MPMarshalErrorStructure, "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) );
|
||||||
MPMarshalErrorStructure,
|
|
||||||
mpw_str( "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) )
|
|
||||||
};
|
|
||||||
mpw_free_strings( &headerName, &headerValue, NULL );
|
mpw_free_strings( &headerName, &headerValue, NULL );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -908,7 +921,7 @@ static void mpw_marshal_read_flat(
|
|||||||
if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) {
|
if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) {
|
||||||
unsigned long value = strtoul( headerValue, NULL, 10 );
|
unsigned long value = strtoul( headerValue, NULL, 10 );
|
||||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast)
|
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast)
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm version: %s", headerValue );
|
||||||
else
|
else
|
||||||
algorithm = (MPAlgorithmVersion)value;
|
algorithm = (MPAlgorithmVersion)value;
|
||||||
}
|
}
|
||||||
@@ -923,7 +936,7 @@ static void mpw_marshal_read_flat(
|
|||||||
if (mpw_strcasecmp( headerName, "Default Type" ) == OK) {
|
if (mpw_strcasecmp( headerName, "Default Type" ) == OK) {
|
||||||
unsigned long value = strtoul( headerValue, NULL, 10 );
|
unsigned long value = strtoul( headerValue, NULL, 10 );
|
||||||
if (!mpw_type_short_name( (MPResultType)value ))
|
if (!mpw_type_short_name( (MPResultType)value ))
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %s", headerValue );
|
||||||
else
|
else
|
||||||
defaultType = (MPResultType)value;
|
defaultType = (MPResultType)value;
|
||||||
}
|
}
|
||||||
@@ -934,7 +947,7 @@ static void mpw_marshal_read_flat(
|
|||||||
if (!headerEnded)
|
if (!headerEnded)
|
||||||
continue;
|
continue;
|
||||||
if (!fullName)
|
if (!fullName)
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing header: Full Name" );
|
||||||
if (positionInLine >= endOfLine)
|
if (positionInLine >= endOfLine)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -973,7 +986,7 @@ static void mpw_marshal_read_flat(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Unexpected import format: %u", format );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -981,28 +994,24 @@ static void mpw_marshal_read_flat(
|
|||||||
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
|
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
|
||||||
MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 );
|
MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 );
|
||||||
if (!mpw_type_short_name( siteType )) {
|
if (!mpw_type_short_name( siteType )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %s", siteName, str_type );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
long long int value = strtoll( str_counter, NULL, 10 );
|
long long int value = strtoll( str_counter, NULL, 10 );
|
||||||
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
|
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %s", siteName, str_counter );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MPCounterValue siteCounter = (MPCounterValue)value;
|
MPCounterValue siteCounter = (MPCounterValue)value;
|
||||||
value = strtoll( str_algorithm, NULL, 0 );
|
value = strtoll( str_algorithm, NULL, 0 );
|
||||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||||
file->error = (MPMarshalError){
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %s", siteName, str_algorithm );
|
||||||
MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm )
|
|
||||||
};
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
|
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
|
||||||
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
||||||
if (!siteLastUsed) {
|
if (!siteLastUsed) {
|
||||||
file->error = (MPMarshalError){
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
|
||||||
MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed )
|
|
||||||
};
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,11 +1029,9 @@ static void mpw_marshal_read_flat(
|
|||||||
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
|
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
file->error = (MPMarshalError){
|
mpw_marshal_error( file, MPMarshalErrorMissing,
|
||||||
MPMarshalErrorMissing,
|
"Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
|
||||||
mpw_str( "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
|
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName );
|
||||||
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName )
|
|
||||||
};
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1044,7 +1051,7 @@ static void mpw_marshal_read_json(
|
|||||||
|
|
||||||
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
|
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
|
||||||
if (!file->data) {
|
if (!file->data) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,7 +1059,7 @@ static void mpw_marshal_read_json(
|
|||||||
enum json_tokener_error json_error = json_tokener_success;
|
enum json_tokener_error json_error = json_tokener_success;
|
||||||
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
|
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
|
||||||
if (!json_file || json_error != json_tokener_success) {
|
if (!json_file || json_error != json_tokener_success) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) ) };
|
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,9 +1081,9 @@ MPMarshalledFile *mpw_marshal_read(
|
|||||||
if (!file)
|
if (!file)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
if (!info) {
|
if (!info) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate info." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate info." );
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1116,13 +1123,13 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
if (!file)
|
if (!file)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||||
if (!file->info) {
|
if (!file->info) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "File wasn't parsed yet." };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "File wasn't parsed yet." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!file->data) {
|
if (!file->data) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "No input data." };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "No input data." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1132,43 +1139,43 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
MPAlgorithmVersion algorithm =
|
MPAlgorithmVersion algorithm =
|
||||||
mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
|
mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
|
||||||
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
|
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm: %u", algorithm ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm: %u", algorithm );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
unsigned int avatar = mpw_default_n( 0U, mpw_marshal_data_get_num( file->data, "user", "avatar", NULL ) );
|
unsigned int avatar = mpw_default_n( 0U, mpw_marshal_data_get_num( file->data, "user", "avatar", NULL ) );
|
||||||
const char *fullName = mpw_marshal_data_get_str( file->data, "user", "full_name", NULL );
|
const char *fullName = mpw_marshal_data_get_str( file->data, "user", "full_name", NULL );
|
||||||
if (!fullName || !strlen( fullName )) {
|
if (!fullName || !strlen( fullName )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
|
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing value for full name." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
MPIdenticon identicon = mpw_identicon_encoded( mpw_marshal_data_get_str( file->data, "user", "identicon", NULL ) );
|
MPIdenticon identicon = mpw_identicon_encoded( mpw_marshal_data_get_str( file->data, "user", "identicon", NULL ) );
|
||||||
const char *keyID = mpw_marshal_data_get_str( file->data, "user", "key_id", NULL );
|
const char *keyID = mpw_marshal_data_get_str( file->data, "user", "key_id", NULL );
|
||||||
MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) );
|
MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) );
|
||||||
if (!mpw_type_short_name( defaultType )) {
|
if (!mpw_type_short_name( defaultType )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %u", defaultType );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL );
|
const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL );
|
||||||
time_t lastUsed = mpw_timegm( str_lastUsed );
|
time_t lastUsed = mpw_timegm( str_lastUsed );
|
||||||
if (!lastUsed) {
|
if (!lastUsed) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user last used: %s", str_lastUsed );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPMasterKey masterKey = NULL;
|
MPMasterKey masterKey = NULL;
|
||||||
if (masterKeyProvider && !(masterKey = masterKeyProvider( algorithm, fullName ))) {
|
if (masterKeyProvider && !(masterKey = masterKeyProvider( algorithm, fullName ))) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
|
if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." };
|
mpw_marshal_error( file, MPMarshalErrorMasterPassword, "Master key doesn't match key ID." );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPMarshalledUser *user = NULL;
|
MPMarshalledUser *user = NULL;
|
||||||
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
|
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new user." );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1189,21 +1196,21 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
|
|
||||||
algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) );
|
algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) );
|
||||||
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
|
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %u", siteName, algorithm ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %u", siteName, algorithm );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) );
|
MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) );
|
||||||
if (siteCounter < MPCounterValueFirst || siteCounter > MPCounterValueLast) {
|
if (siteCounter < MPCounterValueFirst || siteCounter > MPCounterValueLast) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, siteCounter ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %d", siteName, siteCounter );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) );
|
MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) );
|
||||||
if (!mpw_type_short_name( siteType )) {
|
if (!mpw_type_short_name( siteType )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %u", siteName, siteType );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1211,7 +1218,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
const char *siteResultState = mpw_marshal_data_get_str( siteData, "password", NULL );
|
const char *siteResultState = mpw_marshal_data_get_str( siteData, "password", NULL );
|
||||||
MPResultType siteLoginType = mpw_default_n( MPResultTypeTemplateName, mpw_marshal_data_get_num( siteData, "login_type", NULL ) );
|
MPResultType siteLoginType = mpw_default_n( MPResultTypeTemplateName, mpw_marshal_data_get_num( siteData, "login_type", NULL ) );
|
||||||
if (!mpw_type_short_name( siteLoginType )) {
|
if (!mpw_type_short_name( siteLoginType )) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site login type: %s: %u", siteName, siteLoginType );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1221,7 +1228,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL );
|
str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL );
|
||||||
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
||||||
if (!siteLastUsed) {
|
if (!siteLastUsed) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
|
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1231,7 +1238,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
|
|
||||||
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm );
|
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm );
|
||||||
if (!site) {
|
if (!site) {
|
||||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new site." );
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1245,10 +1252,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
|||||||
// Clear Text
|
// Clear Text
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
|
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||||
file->error = (MPMarshalError){
|
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
|
||||||
MPMarshalErrorInternal,
|
|
||||||
"Couldn't derive master key."
|
|
||||||
};
|
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
mpw_marshal_user_free( &user );
|
mpw_marshal_user_free( &user );
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -246,6 +246,10 @@ MPMarshalledQuestion *mpw_marshal_question(
|
|||||||
* @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
|
* @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
|
||||||
MPMarshalledFile *mpw_marshal_file(
|
MPMarshalledFile *mpw_marshal_file(
|
||||||
MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data);
|
MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data);
|
||||||
|
/** Record a marshal error.
|
||||||
|
* @return The given file or new (allocated) if file is NULL; or NULL if the file couldn't be allocated. */
|
||||||
|
MPMarshalledFile *mpw_marshal_error(
|
||||||
|
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...);
|
||||||
|
|
||||||
//// Disposing.
|
//// Disposing.
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
MP_LIBS_BEGIN
|
MP_LIBS_BEGIN
|
||||||
|
#define __STDC_WANT_LIB_EXT1__ 1
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user