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];
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"success",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
}];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
||||
}
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"success",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
}];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
@@ -224,18 +222,16 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"failed",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
||||
}];
|
||||
}
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"failed",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
||||
}];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,13 +173,11 @@
|
||||
else
|
||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"failed",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"failed",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
|
||||
return NO;
|
||||
}
|
||||
@@ -203,15 +201,13 @@
|
||||
}
|
||||
|
||||
@try {
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance userLoggedIn:user.userID];
|
||||
[Countly.sharedInstance userLoggedIn:user.userID];
|
||||
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"success",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"success",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"While setting username: %@", exception );
|
||||
|
||||
@@ -119,8 +119,7 @@ static MPAppDelegate_Shared *instance;
|
||||
if (self.key)
|
||||
self.key = nil;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
[Countly.sharedInstance userLoggedOut];
|
||||
[Countly.sharedInstance userLoggedOut];
|
||||
|
||||
self.activeUserOID = activeUserOID;
|
||||
|
||||
|
||||
@@ -563,50 +563,46 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
// Read metadata for the import file.
|
||||
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
|
||||
if (!file)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
if (file->error.type != MPMarshalSuccess) {
|
||||
MPMarshalErrorType type = file->error.type;
|
||||
mpw_marshal_file_free( &file );
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(type),
|
||||
NSLocalizedDescriptionKey: @"Could not parse Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
if (file->info->format == MPMarshalFormatNone) {
|
||||
mpw_marshal_file_free( &file );
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorFormat),
|
||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||
}]), @"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]), @"" );
|
||||
MPMarshalledUser *importUser = nil;
|
||||
@try {
|
||||
if (!file)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
if (file->error.type != MPMarshalSuccess) {
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(file->error.type),
|
||||
NSLocalizedDescriptionKey: strf( @"Could not parse Master Password import data:\n%@", @(file->error.message) ),
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
if (file->info->format == MPMarshalFormatNone) {
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorFormat),
|
||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
|
||||
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
||||
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex]
|
||||
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
|
||||
// Get master password for import file.
|
||||
MPKey *importKey;
|
||||
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.
|
||||
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
|
||||
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
||||
} 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)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(file->error.type),
|
||||
NSLocalizedDescriptionKey: @(file->error.message),
|
||||
NSLocalizedDescriptionKey: strf( @"Could not authenticate Master Password import:\n%@", @(file->error.message) ),
|
||||
}]), @"While importing sites." );
|
||||
|
||||
// Find an existing user to update.
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
|
||||
@property(nonatomic, retain) NSNumber *sendInfo;
|
||||
@property(nonatomic, retain) NSNumber *sendInfoDecided;
|
||||
@property(nonatomic, retain) NSNumber *notificationsDecided;
|
||||
|
||||
@property(nonatomic, retain) NSNumber *rememberLogin;
|
||||
@property(nonatomic, retain) NSNumber *hidePasswords;
|
||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||
|
||||
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
||||
|
||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
@implementation MPConfig
|
||||
|
||||
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
|
||||
@dynamic sendInfo, sendInfoDecided, notificationsDecided, rememberLogin, hidePasswords, siteAttacker, checkInconsistency;
|
||||
|
||||
- (id)init {
|
||||
|
||||
@@ -29,15 +29,16 @@
|
||||
return nil;
|
||||
|
||||
[self.defaults registerDefaults:@{
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
|
||||
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
||||
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
|
||||
NSStringFromSelector( @selector( notificationsDecided ) ): @NO,
|
||||
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
}];
|
||||
|
||||
self.delegate = [MPAppDelegate_Shared get];
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
|
||||
|
||||
|
||||
@implementation MPMacAppDelegate
|
||||
|
||||
#pragma clang diagnostic push
|
||||
@@ -134,7 +133,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
#if DEBUG
|
||||
countlyConfig.enableDebug = YES;
|
||||
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
||||
#elif ! PUBLIC
|
||||
#elif !PUBLIC
|
||||
countlyConfig.enableDebug = NO;
|
||||
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
||||
#endif
|
||||
@@ -205,7 +204,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
[self enableNotifications];
|
||||
[self tryNotifications];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
@@ -231,7 +230,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
- (void)enableNotifications {
|
||||
- (void)tryNotifications {
|
||||
|
||||
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
|
||||
if (@available( macOS 10.14, * )) {
|
||||
@@ -250,18 +249,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
- (void)askNotifications {
|
||||
|
||||
if ([[MPMacConfig get].notificationsDecided boolValue])
|
||||
return;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
|
||||
if (@available( macOS 10.14, * )) {
|
||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||
^(BOOL granted, NSError *error) {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[Countly.sharedInstance askForNotificationPermission];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
||||
}
|
||||
if (@available( macOS 10.14, * )) {
|
||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||
^(BOOL granted, NSError *error) {
|
||||
[MPMacConfig get].notificationsDecided = @(YES);
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[Countly.sharedInstance askForNotificationPermission];
|
||||
[MPMacConfig get].notificationsDecided = @(YES);
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -408,14 +408,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
- (IBAction)togglePreference:(id)sender {
|
||||
|
||||
if (sender == self.diagnosticsItem) {
|
||||
BOOL sendInfo = self.diagnosticsItem.state != NSOnState;
|
||||
[[Countly sharedInstance] recordEvent:@"sendInfoDecided" segmentation:@{
|
||||
@"from": @"preferences",
|
||||
@"sendInfo": [@(sendInfo) description],
|
||||
}];
|
||||
[MPMacConfig get].sendInfo = @(sendInfo);
|
||||
}
|
||||
if (sender == self.diagnosticsItem)
|
||||
[MPMacConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
|
||||
if (sender == self.hidePasswordsItem)
|
||||
[MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
||||
if (sender == self.rememberPasswordItem)
|
||||
@@ -446,8 +440,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert setMessageText:@"New User"];
|
||||
[alert setInformativeText:@"To begin, enter your full name.\n\n"
|
||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||
@"as you would on an official document."];
|
||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||
@"as you would on an official document."];
|
||||
[alert addButtonWithTitle:@"Create User"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
@@ -775,10 +769,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
// Send info
|
||||
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]) {
|
||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||
if ([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 platform] forKey:@"platform"];
|
||||
}];
|
||||
|
||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||
}
|
||||
else {
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,12 +61,7 @@
|
||||
[alert addButtonWithTitle:@"Thanks!"];
|
||||
[alert addButtonWithTitle:@"Disable"];
|
||||
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
|
||||
BOOL sendInfo = returnCode != NSAlertSecondButtonReturn;
|
||||
[[Countly sharedInstance] recordEvent:@"sendInfoDecided" segmentation:@{
|
||||
@"from": @"initial",
|
||||
@"sendInfo": [@(sendInfo) description],
|
||||
}];
|
||||
[MPMacConfig get].sendInfo = @(sendInfo);
|
||||
[MPMacConfig get].sendInfo = @(returnCode != NSAlertSecondButtonReturn);
|
||||
[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;
|
||||
NSArray *types = [site.algorithm allTypes];
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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">
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@@ -143,11 +143,11 @@
|
||||
</shadow>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
|
||||
<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.
|
||||
⑶ 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).
|
||||
⑸ 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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@@ -310,7 +310,7 @@
|
||||
</connections>
|
||||
</scrollView>
|
||||
<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>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="startingColor">
|
||||
<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>
|
||||
</textField>
|
||||
<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>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
@@ -659,6 +659,35 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</binding>
|
||||
</connections>
|
||||
</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">
|
||||
<rect key="frame" x="0.0" y="85" width="177" height="19"/>
|
||||
<shadow key="shadow">
|
||||
@@ -805,7 +834,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</buttonCell>
|
||||
<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">
|
||||
<dictionary key="options">
|
||||
<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"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<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"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4">
|
||||
|
||||
@@ -231,12 +231,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
user.avatar = newUserAvatar;
|
||||
user.name = newUserName;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
@"avatar" : @(user.avatar).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
@"avatar" : @(user.avatar).description,
|
||||
}];
|
||||
}
|
||||
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
if ([[MPiOSConfig get].showSetup boolValue])
|
||||
[self.navigationController performSegueWithIdentifier:@"setup" sender:self];
|
||||
|
||||
[self enableNotifications];
|
||||
[self consentFeatures];
|
||||
} );
|
||||
}
|
||||
@catch (id exception) {
|
||||
@@ -229,7 +229,44 @@
|
||||
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];
|
||||
if (@available( iOS 12, * )) {
|
||||
@@ -240,35 +277,36 @@
|
||||
|
||||
[self askNotifications];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
else {
|
||||
[self askNotifications];
|
||||
}
|
||||
|
||||
[self askNotifications];
|
||||
}
|
||||
|
||||
- (void)askNotifications {
|
||||
|
||||
if ([[MPiOSConfig get].notificationsDecided boolValue])
|
||||
return;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
|
||||
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"
|
||||
@"When it's time, we'll send you a notification to help you make an effortless transition."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
if (@available( iOS 12, * )) {
|
||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||
^(BOOL granted, NSError *error) {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[Countly.sharedInstance askForNotificationPermission];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
|
||||
}
|
||||
}]];
|
||||
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
|
||||
presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
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"
|
||||
@"When it's time, we'll send you a notification to help you make an effortless transition."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
if (@available( iOS 12, * )) {
|
||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
|
||||
^(BOOL granted, NSError *error) {
|
||||
[MPiOSConfig get].notificationsDecided = @(YES);
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[Countly.sharedInstance askForNotificationPermission];
|
||||
[MPiOSConfig get].notificationsDecided = @(YES);
|
||||
}
|
||||
}]];
|
||||
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
|
||||
presentViewController:alert animated:YES completion:nil];
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -643,10 +681,13 @@
|
||||
|
||||
// Send info
|
||||
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]) {
|
||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
||||
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
||||
|
||||
@@ -670,11 +711,13 @@
|
||||
#else
|
||||
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
|
||||
#endif
|
||||
|
||||
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,21 @@ MPMarshalledFile *mpw_marshal_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(
|
||||
MPMarshalledInfo **info) {
|
||||
|
||||
@@ -228,6 +243,7 @@ void mpw_marshal_file_free(
|
||||
|
||||
mpw_marshal_info_free( &(*file)->info );
|
||||
mpw_marshal_data_free( &(*file)->data );
|
||||
mpw_free_string( &(*file)->error.message );
|
||||
mpw_free( file, sizeof( MPMarshalledFile ) );
|
||||
}
|
||||
|
||||
@@ -539,7 +555,7 @@ static const char *mpw_marshal_write_flat(
|
||||
|
||||
const MPMarshalledData *data = file->data;
|
||||
if (!data) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing data." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -583,9 +599,9 @@ static const char *mpw_marshal_write_flat(
|
||||
}
|
||||
|
||||
if (!out)
|
||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
|
||||
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
|
||||
else
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -637,7 +653,7 @@ static const char *mpw_marshal_write_json(
|
||||
// Section: "export"
|
||||
json_object *json_file = mpw_get_json_data( file->data );
|
||||
if (!json_file) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't serialize export data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't serialize export data." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -650,9 +666,9 @@ static const char *mpw_marshal_write_json(
|
||||
json_object_put( json_file );
|
||||
|
||||
if (!out)
|
||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
|
||||
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
|
||||
else
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -698,17 +714,17 @@ const char *mpw_marshal_write(
|
||||
if (!file_)
|
||||
mpw_marshal_file_free( &file );
|
||||
else
|
||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||
return NULL;
|
||||
}
|
||||
if (!user->fullName || !strlen( user->fullName )) {
|
||||
if (!file_)
|
||||
mpw_marshal_file_free( &file );
|
||||
else
|
||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing full name." );
|
||||
return NULL;
|
||||
}
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
|
||||
MPMasterKey masterKey = NULL;
|
||||
if (user->masterKeyProvider)
|
||||
@@ -749,7 +765,7 @@ const char *mpw_marshal_write(
|
||||
if (!file_)
|
||||
mpw_marshal_file_free( &file );
|
||||
else
|
||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -807,7 +823,7 @@ const char *mpw_marshal_write(
|
||||
const char *out = NULL;
|
||||
switch (outFormat) {
|
||||
case MPMarshalFormatNone:
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
break;
|
||||
case MPMarshalFormatFlat:
|
||||
out = mpw_marshal_write_flat( file );
|
||||
@@ -818,7 +834,7 @@ const char *mpw_marshal_write(
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
||||
mpw_marshal_error( file, MPMarshalErrorFormat, "Unsupported output format: %u", outFormat );
|
||||
break;
|
||||
}
|
||||
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() );
|
||||
if (!file->data) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -891,10 +907,7 @@ static void mpw_marshal_read_flat(
|
||||
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
|
||||
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||
if (!headerName || !headerValue) {
|
||||
file->error = (MPMarshalError){
|
||||
MPMarshalErrorStructure,
|
||||
mpw_str( "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) )
|
||||
};
|
||||
mpw_marshal_error( file, MPMarshalErrorStructure, "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) );
|
||||
mpw_free_strings( &headerName, &headerValue, NULL );
|
||||
continue;
|
||||
}
|
||||
@@ -908,7 +921,7 @@ static void mpw_marshal_read_flat(
|
||||
if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) {
|
||||
unsigned long value = strtoul( headerValue, NULL, 10 );
|
||||
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
|
||||
algorithm = (MPAlgorithmVersion)value;
|
||||
}
|
||||
@@ -923,7 +936,7 @@ static void mpw_marshal_read_flat(
|
||||
if (mpw_strcasecmp( headerName, "Default Type" ) == OK) {
|
||||
unsigned long value = strtoul( headerValue, NULL, 10 );
|
||||
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
|
||||
defaultType = (MPResultType)value;
|
||||
}
|
||||
@@ -934,7 +947,7 @@ static void mpw_marshal_read_flat(
|
||||
if (!headerEnded)
|
||||
continue;
|
||||
if (!fullName)
|
||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing header: Full Name" );
|
||||
if (positionInLine >= endOfLine)
|
||||
continue;
|
||||
|
||||
@@ -973,7 +986,7 @@ static void mpw_marshal_read_flat(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
|
||||
mpw_marshal_error( file, MPMarshalErrorFormat, "Unexpected import format: %u", format );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -981,28 +994,24 @@ static void mpw_marshal_read_flat(
|
||||
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
|
||||
MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 );
|
||||
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;
|
||||
}
|
||||
long long int value = strtoll( str_counter, NULL, 10 );
|
||||
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;
|
||||
}
|
||||
MPCounterValue siteCounter = (MPCounterValue)value;
|
||||
value = strtoll( str_algorithm, NULL, 0 );
|
||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||
file->error = (MPMarshalError){
|
||||
MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm )
|
||||
};
|
||||
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %s", siteName, str_algorithm );
|
||||
continue;
|
||||
}
|
||||
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
|
||||
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
||||
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 );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1020,11 +1029,9 @@ static void mpw_marshal_read_flat(
|
||||
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
|
||||
}
|
||||
else {
|
||||
file->error = (MPMarshalError){
|
||||
MPMarshalErrorMissing,
|
||||
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 )
|
||||
};
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing,
|
||||
"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 );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1044,7 +1051,7 @@ static void mpw_marshal_read_json(
|
||||
|
||||
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
|
||||
if (!file->data) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1052,7 +1059,7 @@ static void mpw_marshal_read_json(
|
||||
enum json_tokener_error json_error = json_tokener_success;
|
||||
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1074,9 +1081,9 @@ MPMarshalledFile *mpw_marshal_read(
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
if (!info) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate info." };
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate info." );
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -1116,13 +1123,13 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
|
||||
mpw_marshal_error( file, MPMarshalSuccess, NULL );
|
||||
if (!file->info) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "File wasn't parsed yet." };
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing, "File wasn't parsed yet." );
|
||||
return NULL;
|
||||
}
|
||||
if (!file->data) {
|
||||
file->error = (MPMarshalError){ MPMarshalErrorMissing, "No input data." };
|
||||
mpw_marshal_error( file, MPMarshalErrorMissing, "No input data." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1132,43 +1139,43 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
MPAlgorithmVersion algorithm =
|
||||
mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
|
||||
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;
|
||||
}
|
||||
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 );
|
||||
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;
|
||||
}
|
||||
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 );
|
||||
MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) );
|
||||
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;
|
||||
}
|
||||
const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL );
|
||||
time_t lastUsed = mpw_timegm( str_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;
|
||||
}
|
||||
|
||||
MPMasterKey masterKey = NULL;
|
||||
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;
|
||||
}
|
||||
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 );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MPMarshalledUser *user = NULL;
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
@@ -1189,21 +1196,21 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
|
||||
algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
}
|
||||
MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
}
|
||||
MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
@@ -1211,7 +1218,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
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 ) );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
@@ -1221,7 +1228,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL );
|
||||
time_t siteLastUsed = mpw_timegm( str_lastUsed );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
@@ -1231,7 +1238,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
|
||||
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm );
|
||||
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_marshal_user_free( &user );
|
||||
return NULL;
|
||||
@@ -1245,10 +1252,7 @@ MPMarshalledUser *mpw_marshal_auth(
|
||||
// Clear Text
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||
file->error = (MPMarshalError){
|
||||
MPMarshalErrorInternal,
|
||||
"Couldn't derive master key."
|
||||
};
|
||||
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
mpw_marshal_user_free( &user );
|
||||
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. */
|
||||
MPMarshalledFile *mpw_marshal_file(
|
||||
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.
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
Reference in New Issue
Block a user